A TRON account type describes a protocol account record. It is different from an address’s Base58 or Hex format, a token’s TRC10 or TRC20 standard, and a label such as issuer. The address prefix alone does not tell you whether you are examining a contract account.
This guide helps readers interpret public node responses and distinguish a missing default from missing evidence. On 1 November 2026, we queried two public Mainnet accounts and one contract-information record without credentials. We also tested a local response classifier. No wallet was connected, and no transaction was created, signed or broadcast.

Table of Contents
Separate account type, format and role
The protocol’s AccountType enum defines Normal = 0, AssetIssue = 1 and Contract = 2. The Account message has its own type field. These are protocol enum values, not names of token standards or commercial categories.
| Question | Relevant evidence | What does not answer it |
|---|---|---|
| How is the address written? | Base58Check or Hex representation | A display name |
| What account type is returned? | A valid GetAccount response in its endpoint context | The initial T character |
| Is this the token’s issuer or contract? | The source’s field label and complete linked identifier | A guess based on the enum name |
| Who can authorize an operation? | The relevant permissions or contract rules | A type label alone |
The Base58 and Hex address guide covers representation. Use it to compare complete strings, then return to the question about account type. Converting one address between formats does not turn an ordinary account into a deployed smart contract.
Account type in two actual GetAccount observations
We used the Mainnet host https://api.trongrid.io and /wallet/getaccount, with each public address and visible: true. The GetAccount reference documents that address-format option and warns that an error body can accompany HTTP 200. Its displayed example host is Shasta; our observations below used Mainnet.
| Public record | Observed fields | Narrow interpretation |
|---|---|---|
| TUpMhErZL2fhh4sVNULAbNKLokS4GjC1F4 | Matching address; type: Contract; create_time absent | An explicit Contract enum in this response |
| TF5Bn4cJCT6GVeUgyCN4rBhDg42KBrpAjg | Matching address; type omitted; creation field present; asset_issued_ID: 1002000 | A default-omitted record, with a separate token-issuance field |
The first address is the TrueUSD proxy examined in the TUSD metadata and proxy-reading guide. The second came from the Issuer field of the public BitTorrent Old token page. We retained the full response locally but show only identity and classification fields here. Balances and activity totals are outside this comparison.
Both requests returned HTTP 200 without an Error entry. A successful transport response is only the start: we matched each returned address, inspected the object and kept the endpoint’s meaning with it. Neither the address’s label nor its token-issuer role was substituted for the returned account type.

Why an issuer role does not fill the type field
The second response is useful precisely because it has a token-issuance field while omitting type. A reader who sees “Issuer” in an explorer could be tempted to write AssetIssue into the node response. That would replace an observation with an assumption.
The official account-model guide distinguishes ordinary externally controlled accounts from code-controlled contract accounts. The protocol enum and a page’s descriptive roles belong to different layers of the record. Preserve the observed fields independently rather than deriving one from a word that looks related.
In our response note, the literal observation is “type absent; asset_issued_ID present.” The ProtoJSON default-value rules explain why implicit default fields may be omitted. Under that serialization model and the protocol enum, an otherwise valid account result with the default omitted can be interpreted as Normal. That interpretation needs the valid account-result context. An empty JSON object from an unknown source cannot inherit it.
A contract-information crosscheck
For the TUSD address, we separately queried /wallet/getcontract with value set to the same public address and visible: true. The GetContract reference describes contract metadata, including bytecode and ABI.
The response matched the contract address, reported the name TrueUSD, included 3,078 hexadecimal characters in its bytecode field and contained 46 ABI entries. These are dated structural observations, not an audit of the bytecode or a claim about the trustworthiness of the issuer. We did not deploy or execute a state-changing method.
This positive crosscheck supports the explicit Contract result in our first response. We did not test a negative GetContract query for the issuer account. Therefore, this article does not claim that every missing contract result proves a Normal account or that every absent ABI means no deployed code exists.
Reproduce the two public account observations
The short Python script below repeats the two GetAccount requests and prints only the fields relevant to this account type comparison. We executed it against Mainnet on 1 November 2026 and retained its output. It uses no API key, wallet library or signing operation. The public addresses are fixed examples, not instructions to receive funds.
import json
from urllib.request import Request, urlopen
addresses = [
'TUpMhErZL2fhh4sVNULAbNKLokS4GjC1F4',
'TF5Bn4cJCT6GVeUgyCN4rBhDg42KBrpAjg',
]
rows = []
for address in addresses:
request = Request(
'https://api.trongrid.io/wallet/getaccount',
data=json.dumps({'address': address, 'visible': True}).encode(),
headers={'Content-Type': 'application/json'},
)
with urlopen(request, timeout=30) as response:
obj = json.load(response)
if 'Error' in obj or obj.get('address') != address:
raise ValueError('No matching successful account result')
rows.append({
'address': address,
'typePresent': 'type' in obj,
'type': obj.get('type'),
'createTimePresent': 'create_time' in obj,
})
print(json.dumps(rows, indent=2))
The TUSD row printed typePresent: true, type: Contract and createTimePresent: false. The issuer row printed typePresent: false and createTimePresent: true. This confirms why the classifier does not require a creation field for an explicit account type, while its omitted-default branch uses that additional context.
The script creates a compact summary, not a copy of each raw response. Its obj.get("type") produces Python None, printed as JSON null, when the original key is absent. Read that summary together with typePresent; the generated null does not mean the original node explicitly returned a null field. This distinction prevents a reporting convenience from changing the underlying observation.
These two script requests repeated the earlier account checks; the separate contract-metadata query was performed once. An HTTP exception, unavailable service or changed response must be recorded as such. Do not suppress a failure and reuse the example values as if the new run succeeded. The local classifier can still be tested against retained fixtures without any live request.
Four practical checks of account type
- Record the question and network. If you want a balance, a token identity or an authorization rule, account type may be only one supporting field. Keep Mainnet and test networks separate.
- Preserve the complete request context. Keep the endpoint, public address, visible setting and check time. Different APIs can reuse the word type for different objects.
- Validate the result before reading defaults. Check that the body is an object, the returned address matches, and no Error field is present. Do not classify an empty, failed or partial response as account type Normal merely because type is missing.
- Keep classification and control separate. Record the literal account type field and the limited interpretation. Do not infer a private key, a real-world owner, a safe recipient or a permission threshold from the type alone.
The success condition is a reproducible note stating the account record, observed type or omission, endpoint context and unresolved conditions. When those inputs are absent, “unknown” is preferable to an account type classification assembled from an address prefix.
A local account type classifier with explicit limits
The following Python function reads retained JSON objects. It makes no network requests and does not validate an address checksum, authenticate a server or inspect bytecode. The caller must supply the expected address and confirm that the object came from the intended GetAccount endpoint.
def classify_account(obj, expected_address, endpoint_is_getaccount=True):
if type(obj) is not dict or 'Error' in obj:
return 'unknown'
if not endpoint_is_getaccount or obj.get('address') != expected_address:
return 'unknown'
if 'type' not in obj:
if type(obj.get('create_time')) is int:
return 'Normal (default omitted)'
return 'unknown'
value = obj['type']
if type(value) is str:
return value if value in ('Normal', 'AssetIssue', 'Contract') else 'unknown'
if type(value) is int:
return {0:'Normal', 1:'AssetIssue', 2:'Contract'}.get(value, 'unknown')
return 'unknown'
Our local tests passed 18 checks: the two retained real responses and 16 synthetic or context checks. The invented cases covered the three recognized string enum names, integer enum values zero through two, omitted defaults with a creation field, empty and partial objects, a mismatched address, an Error object, an unrecognized name, an unsupported integer, Boolean and null values, and an incorrect endpoint context.
The explicit type checks matter in Python because a Boolean is also an integer subclass. Treating False as enum zero would silently convert a wrong data type into Normal. This function rejects that case. This conservative classifier also refuses explicit null. A full ProtoJSON parser can accept null as unset under its own rules; this small evidence-checking helper intentionally requires clearer input instead of claiming to implement that entire format.
The creation-field requirement is a conservative local guard for these examples, not a claim that the protocol requires that field in every response shape. If your source intentionally omits creation metadata, the function returns unknown for a missing account type. Review the source contract rather than removing the guard simply to obtain a classification.
Account type errors and missing fields
HTTP 200 with Error. Record an application-level failure. A transport success cannot establish account type when the endpoint reports an error. Our synthetic fixture included both a Contract-looking field and an Error entry; the classifier refused the object.
An empty object. It does not provide the matched account context required by this method. It may reflect an unavailable record, request issue or another condition. We did not test account activation here, so we do not assign one cause to every empty result.
A partial export containing only an address. The format may look plausible, but the account type remains unsupported. Request the complete source context or retain unknown. The missing value is not numerical zero supplied by the author.
Different state views. Our observations used the FullNode wallet route. A solidified-state source is a different observation context. Preserve both times and endpoints when comparing results instead of silently treating them as one instant.
An unfamiliar explicit type. Keep it unresolved and consult the current protocol definition. Do not map every unrecognized string to Normal or assume a new field value is an error solely because this small classifier does not recognize it.
Copyable evidence note for account type
Task / network / environment: Request endpoint and check time: Requested full public address: Returned address matches: Literal type field or omission: Error field / missing evidence: Other role fields, kept separately: Contract-information crosscheck, if performed: Limited interpretation / unanswered question:
Keep a public address’s role next to the value. If it is an issuer or token contract, do not copy it into a personal recipient field. This article identifies record types; it does not test whether a receiving service accepts a deposit or whether funds can be recovered from a particular destination.
For authorization questions, continue with the TRON account-permission worksheet. A type enum is too narrow to describe signer weights, thresholds or contract-level administration. No permission changes were made in this test.
Frequently asked questions
Can a T-prefixed address identify the account type?
No. That appearance concerns address representation. Read the account record in its network and endpoint context.
Does missing type always mean Normal?
No. A default omission can be interpreted only within a valid account-result context for interpreting account type. Empty, failed, partial or mismatched objects remain unknown in this method.
Does an Issuer label prove the AssetIssue enum was returned?
No. Preserve the role and literal response separately. Our actual issuer response contained an issuance field but omitted type.
Does Contract prove that code is safe?
No. The type and structural metadata do not audit behavior, issuer backing or contract administration.
Does this method prove who owns an address?
No. We queried public records and tested a response parser, without identifying private-key holders or real-world ownership.
Independent Tronscan News editorial review with AI-assisted drafting and checks on 1 November 2026. Anonymous Mainnet metadata requests, a local 18-case parser check, a tested two-request script and two original illustrations support this article. No activation, private-key, transfer, permission-change or contract-security test is claimed. Browse the Tronscan News homepage; corrections can be raised through About & Standards.
Leave a Reply