import type { TronWeb } from '../../tronweb.js'; import utils from '../../utils/index.js'; import { toHex } from '../../utils/address.js'; import { Validator } from '../../paramValidator/index.js'; import { Account, AccountResourceMessage, Address, BaseWitness, ChainParameter, Exchange, Precise64, Proposal, Token, TransactionInfo, TransactionSignWeight, Witness, WitnessList, } from '../../types/Trx.js'; import { Block, BlockHeaderRef, BlockWithoutDetail, GetTransactionResponse } from '../../types/APIResponse.js'; import { Transaction } from '../../types/Transaction.js'; import { Resource } from '../../types/TransactionBuilder.js'; export interface DelegatedResourceV2Response { delegatedResource: { from: string; to: string; frozen_balance_for_bandwidth: number; frozen_balance_for_energy: number; expire_time_for_bandwidth: number; expire_time_for_energy: number; }[]; } /** * Chain-reading APIs shared by {@link Trx} (regular reads, numbers) and * {@link RawTrx} (GET reads with `int64_as_string=true`, java-tron#6699, strings). * Only the routing of the HTTP request differs between the two; the method logic is * identical and lives here once. * * Reads that cannot take the flag are pinned to the regular request in both modes * (their int64 values stay numbers even on a `RawTrx`): * - `getNodeInfo` — the node does not honor the flag on getnodeinfo (and the response * carries doubles that must stay numbers); * - `listNodes`, `getBandwidthPrices`, `getEnergyPrices`, * `getDelegatedResourceAccountIndexV2`, `getBrokerage`/`getUnconfirmedBrokerage` — * no int64 in the response; * - `getTransactionsToAddress`/`getTransactionsFromAddress`/`getTransactionsRelated` — * deprecated walletextension APIs (rejected by nodes >= 4.1.1); * - `getSignWeight`/`getApprovedList` — consume a transaction object via POST body; * - `getCurrentRefBlockParams` — feeds local transaction building and MUST stay on the * regular number path. * `getCurrentBlock`/`getConfirmedCurrentBlock` (getnowblock does not honor the flag) * are instead rerouted in raw mode to `wallet[solidity]/getblock?detail=true`, * which is flag-honoring and returns the same latest block. */ export abstract class AbstractTrx

{ protected tronWeb: TronWeb; protected validator: Validator; protected cache: { contracts: Record }; /** * When true, reads are issued as GET with `int64_as_string=true` so the node * serializes int64/uint64 fields as JSON strings (requires java-tron#6699). */ protected abstract readonly int64AsString: P; constructor(tronWeb: TronWeb) { this.tronWeb = tronWeb; this.validator = new Validator(); this.cache = { contracts: {}, }; } /** * Routes a read request: regular mode sends it exactly as the historical * implementation did (`method`, POST body or GET query); raw mode re-issues it * as GET carrying the payload in the query string plus the `int64_as_string` flag * (the node only honors the flag on GET). */ protected readRequest( node: 'fullNode' | 'solidityNode', endpoint: string, payload: Record = {}, method: 'get' | 'post' = 'post' ): Promise { if (this.int64AsString) { return this.tronWeb[node].request(endpoint, { ...payload, int64_as_string: true }, 'get'); } return this.tronWeb[node].request(endpoint, payload, method); } _parseToken(token: any): Token { return { ...token, name: this.tronWeb.toUtf8(token.name), abbr: token.abbr && this.tronWeb.toUtf8(token.abbr), description: token.description && this.tronWeb.toUtf8(token.description), url: token.url && this.tronWeb.toUtf8(token.url), }; } // ─── Blocks ────────────────────────────────────────────────────────────── getCurrentBlock(): Promise> { if (this.int64AsString) { // getnowblock does not honor int64_as_string; wallet/getblock (flag-honoring, // verified on Nile) returns the same latest block when queried without an id return this.tronWeb.fullNode.request('wallet/getblock', { detail: true, int64_as_string: true }, 'get'); } return this.tronWeb.fullNode.request('wallet/getnowblock'); } getConfirmedCurrentBlock(): Promise> { if (this.int64AsString) { return this.tronWeb.solidityNode.request('walletsolidity/getblock', { detail: true, int64_as_string: true }, 'get'); } return this.tronWeb.solidityNode.request('walletsolidity/getnowblock'); } async getBlock( block: 'earliest' | 'latest' | number | string | false = this.tronWeb.defaultBlock ): Promise> { if (block === false) { throw new Error('No block identifier provided'); } if (block == 'earliest') block = 0; if (block == 'latest') return this.getCurrentBlock(); if (isNaN(+block) && utils.isHex(block.toString())) return this.getBlockByHash(block as string); return this.getBlockByNumber(block as number); } async getBlockByHash(blockHash: string): Promise> { const block = await this.readRequest>('fullNode', 'wallet/getblockbyid', { value: blockHash, }); if (!Object.keys(block).length) { throw new Error('Block not found'); } return block; } async getBlockByNumber(blockID: number): Promise> { if (!utils.isInteger(blockID) || blockID < 0) { throw new Error('Invalid block number provided'); } const block = await this.readRequest>('fullNode', 'wallet/getblockbynum', { num: parseInt(blockID), }); if (!Object.keys(block).length) { throw new Error('Block not found'); } return block; } async getBlockTransactionCount( block: 'earliest' | 'latest' | number | string | false = this.tronWeb.defaultBlock ): Promise { const { transactions = [] } = (await this.getBlock(block)) as unknown as { transactions?: unknown[] }; return transactions.length; } async getTransactionFromBlock( block: 'earliest' | 'latest' | number | string | false = this.tronWeb.defaultBlock, index: number ): Promise> { const { transactions } = (await this.getBlock(block)) as unknown as { transactions?: Precise64[]; }; if (!transactions) { throw new Error('Transaction not found in block'); } if (index >= 0 && index < transactions.length) return transactions[index]; else throw new Error('Invalid transaction index provided'); } async getTransactionsFromBlock( block: 'earliest' | 'latest' | number | string | false = this.tronWeb.defaultBlock ): Promise[]> { const { transactions } = (await this.getBlock(block)) as unknown as { transactions?: Precise64[]; }; if (!transactions) { throw new Error('Transaction not found in block'); } return transactions; } async getBlockRange(start = 0, end = 30): Promise[]> { if (!utils.isInteger(start) || start < 0) { throw new Error('Invalid start of range provided'); } if (!utils.isInteger(end) || end < start) { throw new Error('Invalid end of range provided'); } if (end + 1 - start > 100) { throw new Error('Invalid range size, which should be no more than 100.'); } return this.readRequest<{ block: Precise64[] }>('fullNode', 'wallet/getblockbylimitnext', { startNum: parseInt(start), endNum: parseInt(end) + 1, }).then(({ block = [] }) => block); } // ─── Transactions ──────────────────────────────────────────────────────── async getTransaction(transactionID: string): Promise> { const transaction = await this.readRequest>( 'fullNode', 'wallet/gettransactionbyid', { value: transactionID, } ); if (!Object.keys(transaction).length) { throw new Error('Transaction not found'); } return transaction; } async getConfirmedTransaction(transactionID: string): Promise> { const transaction = await this.readRequest>( 'solidityNode', 'walletsolidity/gettransactionbyid', { value: transactionID, } ); if (!Object.keys(transaction).length) { throw new Error('Transaction not found'); } return transaction; } getUnconfirmedTransactionInfo(transactionID: string): Promise> { return this.readRequest('fullNode', 'wallet/gettransactioninfobyid', { value: transactionID }); } getTransactionInfo(transactionID: string): Promise> { return this.readRequest('solidityNode', 'walletsolidity/gettransactioninfobyid', { value: transactionID }); } getTransactionsToAddress( address = this.tronWeb.defaultAddress.hex, limit = 30, offset = 0 ): Promise { return this.getTransactionsRelated(toHex(address as string), 'to', limit, offset); } getTransactionsFromAddress( address = this.tronWeb.defaultAddress.hex, limit = 30, offset = 0 ): Promise { return this.getTransactionsRelated(toHex(address as string), 'from', limit, offset); } /** Deprecated walletextension API — rejected by nodes >= 4.1.1; not flag-adapted. */ async getTransactionsRelated( address = this.tronWeb.defaultAddress.hex, direction = 'all', limit = 30, offset = 0 ): Promise { if (this.tronWeb.fullnodeSatisfies('>=4.1.1')) { throw new Error('This api is not supported any more'); } if (!['to', 'from', 'all'].includes(direction)) { throw new Error('Invalid direction provided: Expected "to", "from" or "all"'); } if (direction == 'all') { const [from, to] = await Promise.all([ this.getTransactionsRelated(address, 'from', limit, offset), this.getTransactionsRelated(address, 'to', limit, offset), ]); return [ ...from.map((tx) => (((tx as any).direction = 'from'), tx)), ...to.map((tx) => (((tx as any).direction = 'to'), tx)), ].sort((a, b) => { return b.raw_data.timestamp - a.raw_data.timestamp; }); } if (!this.tronWeb.isAddress(address as string)) { throw new Error('Invalid address provided'); } if (!utils.isInteger(limit) || limit < 0 || (offset && limit < 1)) { throw new Error('Invalid limit provided'); } if (!utils.isInteger(offset) || offset < 0) { throw new Error('Invalid offset provided'); } address = toHex(address as string); return this.tronWeb.solidityNode .request<{ transaction: GetTransactionResponse[] }>( `walletextension/gettransactions${direction}this`, { account: { address, }, offset, limit, }, 'post' ) .then(({ transaction }) => { return transaction; }); } // ─── Accounts / balances ───────────────────────────────────────────────── async getAccount(address = this.tronWeb.defaultAddress.hex): Promise> { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } address = toHex(address as string); return this.readRequest('solidityNode', 'walletsolidity/getaccount', { address, }); } getAccountById(id: string): Promise> { return this.getAccountInfoById(id, { confirmed: true }); } async getAccountInfoById(id: string, options: { confirmed: boolean }): Promise> { this.validator.notValid([ { name: 'accountId', type: 'hex', value: id, }, { name: 'accountId', type: 'string', lte: 32, gte: 8, value: id, }, ]); if (id.startsWith('0x')) { id = id.slice(2); } return this.readRequest( options.confirmed ? 'solidityNode' : 'fullNode', `wallet${options.confirmed ? 'solidity' : ''}/getaccountbyid`, { account_id: id, } ); } async getBalance(address = this.tronWeb.defaultAddress.hex): Promise> { const { balance } = await this.getAccount(address); return (balance ?? (this.int64AsString ? '0' : 0)) as Precise64; } async getUnconfirmedAccount(address = this.tronWeb.defaultAddress.hex): Promise> { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } address = toHex(address as string); return this.readRequest('fullNode', 'wallet/getaccount', { address, }); } getUnconfirmedAccountById(id: string): Promise> { return this.getAccountInfoById(id, { confirmed: false }); } async getUnconfirmedBalance(address = this.tronWeb.defaultAddress.hex): Promise> { const { balance } = await this.getUnconfirmedAccount(address); return (balance ?? (this.int64AsString ? '0' : 0)) as Precise64; } async getBandwidth(address = this.tronWeb.defaultAddress.hex): Promise> { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } address = toHex(address as string); // string|number typing keeps the raw-mode wire format visible in the code const { freeNetUsed = 0, freeNetLimit = 0, NetUsed = 0, NetLimit = 0, } = await this.readRequest>>( 'fullNode', 'wallet/getaccountnet', { address, } ); if (this.int64AsString) { // fields arrive as strings; BigInt keeps the arithmetic exact and the // result goes back out as a string return (BigInt(freeNetLimit) - BigInt(freeNetUsed) + (BigInt(NetLimit) - BigInt(NetUsed))).toString() as Precise64< number, P >; } // Number() is an identity on the regular (number) responses return (Number(freeNetLimit) - Number(freeNetUsed) + (Number(NetLimit) - Number(NetUsed))) as Precise64; } async getAccountResources(address = this.tronWeb.defaultAddress.hex): Promise> { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } return this.readRequest('fullNode', 'wallet/getaccountresource', { address: toHex(address as string), }); } /** * Query the amount of resources of a specific resourceType delegated by fromAddress to toAddress */ async getDelegatedResourceV2( fromAddress = this.tronWeb.defaultAddress.hex, toAddress = this.tronWeb.defaultAddress.hex, options = { confirmed: true } ): Promise> { if (!this.tronWeb.isAddress(fromAddress as Address)) { throw new Error('Invalid address provided'); } if (!this.tronWeb.isAddress(toAddress as Address)) { throw new Error('Invalid address provided'); } return this.readRequest( options.confirmed ? 'solidityNode' : 'fullNode', `wallet${options.confirmed ? 'solidity' : ''}/getdelegatedresourcev2`, { fromAddress: toHex(fromAddress as string), toAddress: toHex(toAddress as string), } ); } /** * Query the resource delegation index by an account. Not flag-adapted: the * response carries only addresses, no int64 values. */ async getDelegatedResourceAccountIndexV2( address = this.tronWeb.defaultAddress.hex, options = { confirmed: true } ): Promise<{ account: Address; fromAccounts: Address[]; toAccounts: Address[]; }> { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request( `wallet${options.confirmed ? 'solidity' : ''}/getdelegatedresourceaccountindexv2`, { value: toHex(address as Address), }, 'post' ); } /** * Remaining times of available unstaking API */ async getAvailableUnfreezeCount( address = this.tronWeb.defaultAddress.hex, options = { confirmed: true } ): Promise< Precise64< { count: number; }, P > > { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } return this.readRequest( options.confirmed ? 'solidityNode' : 'fullNode', `wallet${options.confirmed ? 'solidity' : ''}/getavailableunfreezecount`, { owner_address: toHex(address as Address), } ); } /** * Query the amount of delegatable resources of the specified resource Type for target address, unit is sun. */ async getCanDelegatedMaxSize( address = this.tronWeb.defaultAddress.hex, resource: Resource = 'BANDWIDTH', options = { confirmed: true } ): Promise< Precise64< { max_size: number; }, P > > { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } this.validator.notValid([ { name: 'resource', type: 'resource', value: resource, msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"', }, ]); return this.readRequest( options.confirmed ? 'solidityNode' : 'fullNode', `wallet${options.confirmed ? 'solidity' : ''}/getcandelegatedmaxsize`, { owner_address: toHex(address as Address), type: resource === 'ENERGY' ? 1 : 0, } ); } /** * Query the withdrawable balance at the specified timestamp */ async getCanWithdrawUnfreezeAmount( address = this.tronWeb.defaultAddress.hex, timestamp = Date.now(), options = { confirmed: true } ): Promise< Precise64< { amount: number; }, P > > { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } if (!utils.isInteger(timestamp) || timestamp < 0) { throw new Error('Invalid timestamp provided'); } return this.readRequest( options.confirmed ? 'solidityNode' : 'fullNode', `wallet${options.confirmed ? 'solidity' : ''}/getcanwithdrawunfreezeamount`, { owner_address: toHex(address as Address), timestamp: timestamp, } ); } // ─── TRC10 tokens ──────────────────────────────────────────────────────── async getTokensIssuedByAddress(address = this.tronWeb.defaultAddress.hex): Promise>> { if (!this.tronWeb.isAddress(address as Address)) { throw new Error('Invalid address provided'); } address = toHex(address as string); return this.readRequest<{ assetIssue: Token[] }>('fullNode', 'wallet/getassetissuebyaccount', { address, }).then(({ assetIssue }) => { if (!assetIssue) return {}; const tokens = assetIssue .map((token) => { return this._parseToken(token); }) .reduce( (tokens, token) => { return (tokens[token.name] = token), tokens; }, {} as Record ); return tokens as Record>; }); } async getTokenFromID(tokenID: string | number): Promise> { if (utils.isInteger(tokenID)) tokenID = tokenID.toString(); if (!utils.isString(tokenID) || !tokenID.length) { throw new Error('Invalid token ID provided'); } return this.readRequest('fullNode', 'wallet/getassetissuebyname', { value: this.tronWeb.fromUtf8(tokenID), }).then((token) => { if (!token.name) { throw new Error('Token does not exist'); } return this._parseToken(token) as Precise64; }); } async listTokens(limit = 0, offset = 0): Promise[]> { if (!utils.isInteger(limit) || limit < 0 || (offset && limit < 1)) { throw new Error('Invalid limit provided'); } if (!utils.isInteger(offset) || offset < 0) { throw new Error('Invalid offset provided'); } if (!limit) { return this.readRequest<{ assetIssue: Token[] }>('fullNode', 'wallet/getassetissuelist', {}, 'get').then( ({ assetIssue = [] }) => assetIssue.map((token) => this._parseToken(token)) as Precise64[] ); } return this.readRequest<{ assetIssue: Token[] }>('fullNode', 'wallet/getpaginatedassetissuelist', { offset: parseInt(offset), limit: parseInt(limit), }).then(({ assetIssue = [] }) => assetIssue.map((token) => this._parseToken(token)) as Precise64[]); } async getTokenListByName(tokenID: string | number): Promise | Precise64[]> { if (utils.isInteger(tokenID)) tokenID = tokenID.toString(); if (!utils.isString(tokenID) || !tokenID.length) { throw new Error('Invalid token ID provided'); } return this.readRequest<({ assetIssue: Token[] } & { name: undefined }) | (Token & { assetIssue: undefined })>( 'fullNode', 'wallet/getassetissuelistbyname', { value: this.tronWeb.fromUtf8(tokenID), } ).then((token) => { if (Array.isArray(token.assetIssue)) { return token.assetIssue.map((t) => this._parseToken(t)) as Precise64[]; } else if (!token.name) { throw new Error('Token does not exist'); } return this._parseToken(token) as Precise64; }); } getTokenByID(tokenID: number | string): Promise> { if (utils.isInteger(tokenID)) tokenID = tokenID.toString(); if (!utils.isString(tokenID) || !tokenID.length) { throw new Error('Invalid token ID provided'); } return this.readRequest('fullNode', 'wallet/getassetissuebyid', { value: tokenID, }).then((token) => { if (!token.name) { throw new Error('Token does not exist'); } return this._parseToken(token) as Precise64; }); } // ─── Nodes / witnesses ─────────────────────────────────────────────────── /** Not flag-adapted: the response carries no int64 values. */ async listNodes(): Promise { const { nodes = [] } = await this.tronWeb.fullNode.request<{ nodes: { address: { host: string; port: number } }[] }>( 'wallet/listnodes' ); return nodes.map(({ address: { host, port } }) => `${this.tronWeb.toUtf8(host)}:${port}`); } async listSuperRepresentatives(): Promise[]> { const { witnesses = [] } = await this.readRequest<{ witnesses: Precise64[] }>( 'fullNode', 'wallet/listwitnesses', {}, 'get' ); return witnesses; } async getNowWitnessList( options: { offset?: number; limit?: number; visible?: boolean; confirmed?: boolean; } = {} ): Promise[]> { const { offset = 0, limit = 10, visible = false, confirmed = true } = options; this.validator.notValid([ { name: 'offset', type: 'integer', gte: 0, value: offset, msg: 'Invalid offset: must be an integer >= 0', }, { name: 'limit', type: 'integer', gt: 0, value: limit, msg: 'Invalid limit: must be an integer > 0', }, { name: 'visible', type: 'boolean', value: visible, msg: 'Invalid visible: must be a boolean', }, ]); const data = { offset: Number(offset), limit: Number(limit), visible }; const node = confirmed ? 'solidityNode' : 'fullNode'; const endpoint = `wallet${confirmed ? 'solidity' : ''}/getpaginatednowwitnesslist`; const result = await this.readRequest>(node, endpoint, data); if ((result as Record)?.Error) { throw new Error((result as Record).Error); } return Array.isArray(result?.witnesses) ? (result.witnesses as Precise64[]) : []; } async timeUntilNextVoteCycle(): Promise> { const { num = -1 } = await this.readRequest<{ num: number | string }>( 'fullNode', 'wallet/getnextmaintenancetime', {}, 'get' ); if (num == -1) { throw new Error('Failed to get time until next vote cycle'); } if (this.int64AsString) { // bigint division truncates like Math.floor does for the positive values here return (BigInt(num) / 1000n).toString() as Precise64; } return Math.floor(Number(num) / 1000) as Precise64; } /** * Get info about the node. Not flag-adapted: getnodeinfo does not honor * `int64_as_string`, and the response carries doubles (cpuRate, minTimeRatio, ...) * that must stay numbers. */ async getNodeInfo(): Promise<{ beginSyncNum: number; block: string; solidityBlock: string; currentConnectCount: number; activeConnectCount: number; passiveConnectCount: number; totalFlow: number; peerInfoList: { lastSyncBlock: string; remainNum: number; lastBlockUpdateTime: number; syncFlag: boolean; headBlockTimeWeBothHave: number; needSyncFromPeer: boolean; needSyncFromUs: boolean; host: string; port: number; nodeId: string; connectTime: number; avgLatency: number; syncToFetchSize: number; syncToFetchSizePeekNum: number; syncBlockRequestedSize: number; unFetchSynNum: number; blockInPorcSize: number; headBlockWeBothHave: string; isActive: boolean; score: number; nodeCount: number; inFlow: number; disconnectTimes: number; localDisconnectReason: string; remoteDisconnectReason: string; }; configNodeInfo: { codeVersion: string; p2pVersion: string; listenPort: number; discoverEnable: boolean; activeNodeSize: number; passiveNodeSize: number; sendNodeSize: number; maxConnectCount: number; sameIpMaxConnectCount: number; backupListenPort: number; backupMemberSize: number; backupPriority: number; dbVersion: number; minParticipationRate: number; supportConstant: boolean; minTimeRatio: number; maxTimeRatio: number; allowCreationOfContracts: number; allowAdaptiveEnergy: number; }; machineInfo: { threadCount: number; deadLockThreadCount: number; cpuCount: number; totalMemory: number; freeMemory: number; cpuRate: number; javaVersion: string; osName: string; jvmTotalMemory: number; jvmFreeMemory: number; processCpuRate: number; memoryDescInfoList: { name: string; initSize: number; useSize: number; maxSize: number; useRate: number; }; deadLockThreadInfoList: { name: string; lockName: string; lockOwner: string; state: string; blockTime: number; waitTime: number; stackTrace: string; }; }; cheatWitnessInfoMap: Map; }> { return this.tronWeb.fullNode.request('wallet/getnodeinfo', {}, 'post'); } // ─── Contracts ─────────────────────────────────────────────────────────── async getContract(contractAddress: string): Promise { if (!this.tronWeb.isAddress(contractAddress)) { throw new Error('Invalid contract address provided'); } if (this.cache.contracts[contractAddress]) { return this.cache.contracts[contractAddress]; } contractAddress = toHex(contractAddress); const contract = await this.readRequest( 'fullNode', 'wallet/getcontract', { value: contractAddress, }, 'get' ); if (contract.Error) { throw new Error('Contract does not exist'); } this.cache.contracts[contractAddress] = contract; return contract; } async getContractInfo(contractAddress: string): Promise { if (!this.tronWeb.isAddress(contractAddress)) { throw new Error('Invalid contract address provided'); } contractAddress = toHex(contractAddress); const contractInfo = await this.readRequest( 'fullNode', 'wallet/getcontractinfo', { value: contractAddress, }, 'get' ); if (contractInfo.Error) { throw new Error('Contract does not exist'); } return contractInfo; } // ─── Governance / chain parameters ─────────────────────────────────────── /** * Gets a network modification proposal by ID. */ async getProposal(proposalID: number): Promise> { if (!utils.isInteger(proposalID) || proposalID < 0) { throw new Error('Invalid proposalID provided'); } return this.readRequest('fullNode', 'wallet/getproposalbyid', { id: parseInt(proposalID), }); } /** * Lists all network modification proposals. */ async listProposals(): Promise[]> { const { proposals = [] } = await this.readRequest<{ proposals: Precise64[] }>( 'fullNode', 'wallet/listproposals', {} ); return proposals; } /** * Lists all parameters available for network modification proposals. */ async getChainParameters(): Promise[]> { const { chainParameter = [] } = await this.readRequest<{ chainParameter: Precise64[] }>( 'fullNode', 'wallet/getchainparameters', {} ); return chainParameter; } // ─── Exchanges ─────────────────────────────────────────────────────────── /** * Get the exchange ID. */ async getExchangeByID(exchangeID: number): Promise> { if (!utils.isInteger(exchangeID) || exchangeID < 0) { throw new Error('Invalid exchangeID provided'); } return this.readRequest('fullNode', 'wallet/getexchangebyid', { id: exchangeID, }); } /** * Lists the exchanges */ async listExchanges(): Promise[]> { return this.readRequest<{ exchanges: Precise64[] }>('fullNode', 'wallet/listexchanges', {}).then( ({ exchanges = [] }) => exchanges ); } /** * Lists all network modification proposals. */ async listExchangesPaginated(limit = 10, offset = 0): Promise[]> { return this.readRequest<{ exchanges: Precise64[] }>('fullNode', 'wallet/getpaginatedexchangelist', { limit, offset, }).then(({ exchanges = [] }) => exchanges); } // ─── Rewards / brokerage / prices ──────────────────────────────────────── async getReward(address: Address, options: { confirmed?: boolean } = {}): Promise> { options.confirmed = true; return this._getReward(address, options); } async getUnconfirmedReward(address: Address, options: { confirmed?: boolean } = {}): Promise> { options.confirmed = false; return this._getReward(address, options); } async _getReward(address = this.tronWeb.defaultAddress.hex, options: { confirmed?: boolean }): Promise> { this.validator.notValid([ { name: 'origin', type: 'address', value: address, }, ]); const data = { address: toHex(address as Address), }; return this.readRequest<{ reward?: number | string }>( options.confirmed ? 'solidityNode' : 'fullNode', `wallet${options.confirmed ? 'solidity' : ''}/getReward`, data ).then((result = { reward: undefined }) => { if (typeof result.reward === 'undefined') { throw new Error('Not found.'); } return result.reward as Precise64; }); } /** Not flag-adapted: brokerage is a proto int32 percentage, no int64 involved. */ async getBrokerage(address: Address, options: { confirmed?: boolean } = {}): Promise { options.confirmed = true; return this._getBrokerage(address, options); } /** Not flag-adapted: brokerage is a proto int32 percentage, no int64 involved. */ async getUnconfirmedBrokerage(address: Address, options: { confirmed?: boolean } = {}): Promise { options.confirmed = false; return this._getBrokerage(address, options); } private async _getBrokerage(address = this.tronWeb.defaultAddress.hex, options: { confirmed?: boolean }): Promise { this.validator.notValid([ { name: 'origin', type: 'address', value: address, }, ]); const data = { address: toHex(address as Address), }; return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'] .request<{ brokerage?: number }>(`wallet${options.confirmed ? 'solidity' : ''}/getBrokerage`, data, 'post') .then((result = {}) => { if (typeof result.brokerage === 'undefined') { throw new Error('Not found.'); } return result.brokerage; }); } /** Not flag-adapted: the response is already a string. */ async getBandwidthPrices(): Promise { return this.tronWeb.fullNode.request<{ prices?: string }>('wallet/getbandwidthprices', {}, 'post').then((result = {}) => { if (typeof result.prices === 'undefined') { throw new Error('Not found.'); } return result.prices; }); } /** Not flag-adapted: the response is already a string. */ async getEnergyPrices(): Promise { return this.tronWeb.fullNode.request<{ prices?: string }>('wallet/getenergyprices', {}, 'post').then((result = {}) => { if (typeof result.prices === 'undefined') { throw new Error('Not found.'); } return result.prices; }); } // ─── Transaction-adjacent reads (pinned to the regular number path) ────── /** * MUST stay on the regular (number) path in both modes: it performs local hex * conversion and arithmetic on the header fields to build TAPOS references, which * string values would silently corrupt. */ async getCurrentRefBlockParams(): Promise { try { const { block_header, blockID } = await this.tronWeb.fullNode.request( 'wallet/getblock', { detail: false }, 'post' ); const { number, timestamp } = block_header.raw_data; return { ref_block_bytes: number.toString(16).slice(-4).padStart(4, '0'), ref_block_hash: blockID.slice(16, 32), expiration: timestamp + 60 * 1000, timestamp, }; } catch (e) { throw new Error(`Unable to get params: ${(e as Error).message || e}`); } } /** Not flag-adapted: consumes a transaction object via POST body. */ async getApprovedList(transaction: Transaction): Promise<{ approved_list: string[] }> { if (!utils.isObject(transaction)) { throw new Error('Invalid transaction provided'); } return this.tronWeb.fullNode.request('wallet/getapprovedlist', transaction, 'post'); } /** Not flag-adapted: consumes a transaction object via POST body. */ async getSignWeight(transaction: Transaction, permissionId?: number): Promise { if (!utils.isObject(transaction) || !transaction.raw_data || !transaction.raw_data.contract) throw new Error('Invalid transaction provided'); if (utils.isInteger(permissionId)) { transaction.raw_data.contract[0].Permission_id = parseInt(permissionId); } else if (typeof transaction.raw_data.contract[0].Permission_id !== 'number') { transaction.raw_data.contract[0].Permission_id = 0; } return this.tronWeb.fullNode.request('wallet/getsignweight', transaction, 'post'); } }