TRC20 Allowance Query: Read One Owner-Spender Pair

Written by

in

A TRC20 allowance query reads how much one spender is allowed to use from one owner’s balance of one token contract. It can be performed through a constant contract call without connecting a wallet or signing a transaction. Keep the network, contract, owner and spender together: a zero result for one combination is not a security verdict about the whole wallet.

This guide provides an actual dated Mainnet query, request-building code, a strict result decoder and controlled failure cases. It answers the narrow task of reading a supplied allowance value. It does not approve, revoke, transfer tokens or promise recovery. The Tronscan News homepage provides the wider independent guide collection.

TRC20 allowance query roles: network, token contract, owner and spender identify one read-only question
Original query map. A public address in an example is not proof of who controls it.

1. Define the allowance question before building the request

The function is allowance(address,address), with the token owner first and the spender second. Reversing them asks another question. The token contract is a third address, selected independently. A token symbol and a shortened wallet label are insufficient to define this tuple.

The official TRC20 interaction guide includes an allowance example. Its example contract is on Shasta testnet. Our observation below uses the Mainnet TRON USDT contract instead, and does not reuse the testnet example’s addresses or encoded parameter.

Input Question it answers Common confusion
Network and endpoint Which chain state is being read? Copying a testnet example into a Mainnet investigation
Token contract Which token’s allowance mapping? Using a symbol instead of the complete issuer contract
Owner argument Whose token allowance is queried? Assuming it is always the address making the HTTP request
Spender argument Which party’s spending allowance? Substituting the token recipient or an unrelated application label
Request owner_address Caller context for the simulated call Treating a supplied caller address as authenticated ownership

Review the token-approval checklist when the task is understanding a wallet’s signing request. This page adds a read-only method for a specific allowance tuple after you know which addresses matter. Reading the value does not grant or cancel the underlying permission.

2. Use the constant endpoint, with its limits understood

The TriggerConstantContract reference describes local execution without broadcasting or committing state changes. The returned data describes the node’s execution context at the time of the call. It is not a guarantee that a later transaction will see identical state.

Our request went to https://api.trongrid.io/wallet/triggerconstantcontract. A node or provider can impose access conditions and limits. The observed HTTP 200 below is evidence of that one successful request, not a promise of unrestricted future access. If a provider requires credentials, use its normal account process; a token allowance read does not require a private signing key.

Do not confuse an unsigned transaction-shaped object returned by a simulation with an on-chain receipt. This procedure never signs or broadcasts that object. Its success criterion is an explicitly processed request containing one correctly shaped return word for the intended read method.

The endpoint can simulate methods beyond simple views, but the request builder below fixes the method to allowance(address,address). It does not expose an arbitrary function selector, value transfer or broadcast URL. The code prints a request or decodes a saved response; it does not send network traffic itself.

3. Encode the two address arguments in the right order

For this bounded standard-address ABI example, begin with complete 41-prefixed hex TRON addresses. Remove the leading 41 network prefix from each function argument, then left-pad its remaining 20-byte address to one 32-byte word. Concatenate the owner word and the spender word, in that order.

The official contract interaction guide documents this standard-address encoding. It does not mean that every contract argument can be assembled this way: dynamic types, arrays and nonstandard ABIs need appropriate encoding. This builder accepts only the declared two-address allowance shape.

The request’s top-level owner_address and contract_address remain complete TRON hex addresses when visible is false. Removing 41 applies to the standard Solidity address words inside parameter, not indiscriminately to every address field.

  1. Verify the network and issuer contract independently.
  2. Preserve the complete token-owner and spender addresses with separate labels.
  3. Convert or obtain their correct hex forms. This example validates hex shape; it is not a Base58Check converter.
  4. Build the owner word followed by the spender word.
  5. Inspect the printed request and save it before making the read-only call.
Allowance ABI encoding: remove each argument's 41 prefix, pad to 32 bytes, then place owner before spender
Original standard-address encoding worksheet. Top-level TRON address fields retain their prefix.

4. Run the TRC20 allowance query tools locally

Save this standard-library Python code as allowance_read.py. Its build mode prints request JSON; its read mode decodes a saved response. The filenames are local workflow examples, not hosted downloads offered by this page.

import json
import re
import sys


def address_word(address):
    if not isinstance(address, str) or not re.fullmatch(r'41[0-9a-fA-F]{40}', address):
        raise ValueError('Use a complete 41-prefixed hex TRON address')
    return address[2:].lower().rjust(64, '0')


def build_request(contract, owner, spender):
    address_word(contract)
    owner_word = address_word(owner)
    spender_word = address_word(spender)
    return {
        'owner_address': owner.lower(),
        'contract_address': contract.lower(),
        'function_selector': 'allowance(address,address)',
        'parameter': owner_word + spender_word,
        'visible': False
    }


def decode_allowance(response, decimals):
    if type(decimals) is not int or not 0 <= decimals <= 255:
        raise ValueError('Use verified token decimals as an integer from 0 to 255')
    if not isinstance(response, dict) or 'Error' in response:
        raise ValueError('Expected a constant-call response, not an API error')
    status = response.get('result')
    if not isinstance(status, dict) or status.get('result') is not True:
        raise ValueError('API request did not explicitly report success')
    words = response.get('constant_result')
    if not isinstance(words, list) or len(words) != 1:
        raise ValueError('Expected exactly one uint256 result word')
    word = words[0]
    if not isinstance(word, str) or not re.fullmatch(r'[0-9a-fA-F]{64}', word):
        raise ValueError('Expected an unprefixed 32-byte hexadecimal result')
    raw = int(word, 16)
    scale = 10 ** decimals
    whole, fraction = divmod(raw, scale)
    formatted = str(whole) if decimals == 0 else str(whole) + '.' + str(fraction).zfill(decimals)
    return {
        'raw_allowance': str(raw), 'token_decimals': decimals,
        'formatted_allowance': formatted,
        'scope': 'One supplied result word; no signature, ownership or wallet-safety conclusion'
    }


if __name__ == '__main__':
    if len(sys.argv) == 5 and sys.argv[1] == 'build':
        print(json.dumps(build_request(*sys.argv[2:]), indent=2))
    elif len(sys.argv) == 4 and sys.argv[1] == 'read':
        with open(sys.argv[2], encoding='utf-8') as source:
            response = json.load(source)
        print(json.dumps(decode_allowance(response, int(sys.argv[3])), indent=2))
    else:
        raise SystemExit('Use: build contract owner spender; or: read response.json decimals')

For build mode, run python allowance_read.py build CONTRACT_HEX OWNER_HEX SPENDER_HEX, replacing the three labels with your verified complete hex addresses. Copy the resulting JSON into a local request file. The builder uses the owner argument as the caller context for this example, while keeping the two ABI arguments explicit.

To submit the read-only request using a command-line client, a Linux or macOS shell example is curl -X POST https://api.trongrid.io/wallet/triggerconstantcontract -H 'Content-Type: application/json' --data-binary @request.json -o response.json. Use your platform’s actual curl executable and the provider’s normal access policy. The command sends the public addresses and function arguments to that provider; review the file first.

Then run python allowance_read.py read response.json 6 when the verified token uses six decimal places, as in our USDT observation. The decoder accepts a documented integer decimal count, uses integer arithmetic and retains the raw allowance as a string so large uint256 values do not lose precision in later JSON consumers.

The function checks API status and a single complete 32-byte result word. It does not authenticate the source, inspect every TVM execution-status field, or prove that the selected contract implements the intended method. An API-processed response and a decodable word support a narrow reading; preserve the full response for broader review.

5. Inspect our dated Mainnet observation

At 02:50:29 UTC on October 29, 2026, the Mainnet endpoint returned HTTP 200 with result.result: true and one complete all-zero word in constant_result. The decoded raw allowance was 0; using six decimals, the displayed value was 0.000000.

The contract was 41a614f803b6fd780986a42c78ec9c7f77e6ded13c. Tether’s supported-protocols page identifies the corresponding TRON USDT contract. For asset and network context, the TRON USDT guide separates native TRX, the token and its transfer records.

The owner argument was 41ccc1fa093d84927b8f1b16384fbf626c45f652a1 and the spender argument was 41f18dba0bf10be1c7deff6412722689af39106440. We selected these public addresses from a saved transfer body solely to define a reproducible query pair. A transfer between addresses is not evidence that either has granted the other an allowance, and we make no ownership or application attribution.

Observed item Saved result Interpretation
API request status Explicit true The request reported processing success
constant_result One 64-character zero word A complete supplied uint256 result, not an empty list
Decoded raw value 0 Zero for the queried tuple in that node view
Formatted with six decimals 0.000000 Display conversion, not a price
transaction.ret entry Empty object in the saved response No separate explicit TVM status claimed from that field
Signing or broadcast None No permission was changed by this procedure

The response also included simulation resource counters. Those counters are not a fee receipt for a broadcast transfer. We did not convert the observed energy_used into a charge or claim that the queried owner paid for the read.

6. Distinguish zero from missing data and errors

A complete zero word is an explicit value. An empty constant_result list is not that value. Neither is an HTTP error, a response containing Error, or a malformed word. Our decoder rejects those cases instead of converting them to zero.

Controlled input Expected behavior
Actual complete zero word Decode raw 0 and display 0.000000
Synthetic raw 1000001 at six decimals Display 1.000001 exactly
Largest uint256 word Preserve the exact integer as text
Empty or multiple result words Reject this single-value format
Short, prefixed or non-hex word Reject malformed input
API error or status not explicitly true Reject rather than infer zero
Owner and spender swapped Build a different ordered parameter
Missing or malformed hex address Reject before producing a request

We executed the real positive case and altered local fixtures. The synthetic nonzero and maximum values test arithmetic and precision; they are not claims about a real account’s authorization. No fixture was used to create an approval or sent as a state-changing transaction.

Preserve provider errors with their date and endpoint. A rate-limited or unavailable query leaves the result unresolved. Do not label the spender’s allowance zero simply because the page or API could not load. Respect provider access limits and retain the last successful observation under its own timestamp.

Allowance result decision map separating a complete zero word, missing return data and API errors
Original response worksheet. Only a complete returned value supports a numeric result.

7. Keep a single allowance result within its scope

A zero for this owner-spender-contract tuple does not enumerate every spender or every token contract. It does not inspect account-level permissions, private key exposure, contract ownership or another network’s records. Repeating the word “safe” would turn a narrow state read into a claim the evidence cannot support.

Use the account-permissions guide for the separate owner and active permission structures at the account level. Those authorization settings are not the same object as a token contract’s allowance mapping. A clean result in one system does not automatically establish a clean result in the other.

A nonzero allowance is also not a completed transfer receipt or evidence that the spender has already used it. Keep the token balance, granted allowance, observed movement and any private service posting as separate records. A number alone does not establish the spender’s identity or an application’s trustworthiness.

The value can change after the observation. An approval update, spending operation or other contract-specific behavior can affect the state read later. Preserve the source view and timestamp rather than treating a screenshot as a permanent permission certificate.

8. Save a query note another reader can reproduce

Keep the network, full contract, owner, spender, caller context, function selector, encoded parameter, endpoint and collection time together. Retain the complete response and the decoded raw integer, not only a rounded formatted amount. A reviewer should be able to tell which two addresses occupied the two ABI words.

Our read used the latest-state /wallet route. It was not a historical-state query and did not establish an exact snapshot block for the contract read. Do not attach an unrelated nearby block height to the result and present that height as the precise execution state used by the node.

If you compare a later response, keep it as a second dated observation. Confirm the same network, contract, argument order and formatting decimals before describing a change. A reversed pair or a different token contract is a different question, even when both values happen to be zero.

Allowance evidence note retaining the full tuple, source time, raw result and limits of a wallet-safety conclusion
Original evidence note. Reading a value does not modify permission or establish complete wallet safety.

9. Audit the bytes and display conversion by hand

The printed parameter contains two 32-byte words: 64 bytes, or 128 hexadecimal characters, in total. Each word begins with twelve zero bytes followed by the address’s twenty bytes. The function selector is supplied separately through function_selector in this endpoint request. Do not prepend a selector to parameter and silently shift the two argument positions.

For the observed owner, the first word ends with ccc1fa093d84927b8f1b16384fbf626c45f652a1. The second ends with f18dba0bf10be1c7deff6412722689af39106440. These endings make the argument order reviewable without relying on an application nickname. Retain the full words in the request file; the endings here are an explanation of the already supplied complete addresses.

The decoder’s arithmetic can be checked with a small synthetic nonzero word. Raw 1000001 divided into whole units and a six-digit remainder becomes 1.000001. Raw 1 becomes 0.000001. Raw 0 becomes 0.000000. The examples explain decimal placement; none is a claim about a real spender’s allowance.

Keeping the raw value as a string also avoids a common downstream problem. A uint256 can exceed the exact integer range of some JSON consumers. If another program converts the decimal string to a floating-point number, the last digits can be rounded away. Preserve the string or use an arbitrary-precision integer type before comparing results, and verify token decimals independently.

Review checkpoint Expected for this bounded method
Argument count Two standard addresses
Parameter length 128 hexadecimal characters after encoding
Argument order Owner first, spender second
Return word One complete 64-character uint256 word
Formatted result Integer-based decimal placement, raw value retained

A correctly shaped word can still answer the wrong question if the contract, method or arguments were wrong. Shape validation is valuable because it prevents a malformed response from becoming a numeric claim, but it cannot replace source and tuple verification. If two applications disagree, compare their complete inputs and state views before concluding that one decoded the same allowance incorrectly.

If the request file is correct but the returned file contains HTML, an access-denied page or a provider notice, do not pass it off as a token result. The Python JSON reader will stop before the allowance decoder can run. Keep the HTTP status and provider message, use the provider’s documented access process, and leave the allowance unknown for that attempted observation. Changing the contract or address to make an error disappear would change the question rather than resolve it.

Likewise, a locally missing response file is a file-handling error, not a zero allowance. Keep separate labels for request built, request sent, response received and value decoded. In our published example, the request and response were saved and the displayed decoder was executed; a reader building a request without sending it has completed only the first of those steps.

Frequently asked questions

Do I need to connect a wallet to read a TRC20 allowance?

No. A public read-only constant call can use the relevant public addresses without a signing key. The procedure here does not connect a wallet or broadcast a transaction.

Does zero allowance mean my wallet is safe?

No. It describes one owner-spender-token combination in the queried state. Other tokens, spenders, account permissions and signing-secret exposure are outside that result.

Is an empty constant_result the same as zero?

No. This workflow requires one complete uint256 word. Missing return data, malformed input or a node error leaves the numeric result unresolved.

Can I swap owner and spender in the request?

You can build another ordered query, but it asks a different question. The owner is the first allowance argument and the spender is the second; preserve those labels in your evidence note.

Does this script revoke an allowance?

No. It only builds the fixed read request or decodes a saved response. It cannot sign, broadcast, grant or revoke permission.

Checked October 29, 2026 by the Tronscan News editorial team. AI assisted drafting; request construction, decoding and synthetic failure cases were executed using retained public source files. Independent publication, not official TRONSCAN support. Report reproducible corrections through this page’s comments with public source details and the affected step; omit account credentials and signing secrets.

Rate this post

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *