Skip to content

Accounts & Permissions

TRON accounts leverage the same cryptographic foundation as Ethereum — ECDSA with secp256k1 — while introducing native multi-signature permissions and an explicit activation model. This guide covers address encoding, activation requirements, and the built-in permission system for secure account management.

A TRON address is derived from a private key using the same process as Ethereum, with one difference: the address bytes are prefixed with 41 before Base58Check encoding.

Every TRON address exists in two representations:

FormatPrefixLengthExample
Hex4142 characters418840E6C55B9ADA326D211D818C34A994AECED808
Base58CheckT34 charactersTNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL

Both refer to the same account. The hex format appears in raw API calls and internal ABI encoding. The Base58Check format (T-prefix) is what users see in wallets and explorers.

Address Derivation
# Task: Follow these steps to derive a TRON address from a private key.
1. Generate a random 256-bit private key (64 hex characters)
2. Derive the public key using ECDSA secp256k1
3. Keccak-256 hash the public key, take the last 20 bytes
4. Prepend 0x41
5. Base58Check encode → T-prefix address

This is identical to Ethereum key derivation except step 4 prepends 41 rather than 00.

address_utils.js
// Task: Validate, convert, and derive addresses via TronWeb.
// Validate
tronWeb.isAddress('TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL'); // true
// Hex → Base58Check
tronWeb.address.fromHex('418840e6c55b9ada326d211d818c34a994aeced808');
// → 'TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL'
// Base58Check → Hex
tronWeb.address.toHex('TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL');
// → '418840e6c55b9ada326d211d818c34a994aeced808'
// Derive address from private key
tronWeb.address.fromPrivateKey(privateKey);
// Generate a new key pair
const { address, privateKey } = tronWeb.utils.accounts.generateAccount();

TypeDescription
EOAExternally Owned Account. Controlled by a private key. Holds TRX, tokens, and can call contracts.
Contract AccountCreated when a smart contract is deployed. Address is derived from the deployment transaction ID and sender address. No private key.

A freshly derived TRON address does not exist on-chain until it is activated. Until activation, the address can hold a valid key pair but the network has no record of it. Many operations — including receiving TRC-20 transfers for the first time — trigger or require activation.

(For Web2 developers: Unlike traditional databases where creating a new user record is free, blockchains require “activating” an account to prevent spam. Think of this as paying a tiny, one-time infrastructure fee to provision a permanent database row for a new user).

Receive a TRX transfer The simplest path. When any account sends TRX to a new address, activation happens automatically. Cost: 1 TRX deducted from the sender.

CreateAccount API

Create Account
# Task: Call the wallet/createaccount endpoint to activate an address.
POST https://api.trongrid.io/wallet/createaccount
{
"owner_address": "TSenderAddress...",
"account_address": "TNewAddress...",
"visible": true
}

Cost: 1 TRX from owner_address.

Smart contract token transfer If a TRC-20 contract transfers to an unactivated address, activation costs 25,000 Energy from the contract deployer. DApp deployers should budget for this when onboarding new users — see Fee Model for how to set origin_energy_limit to cover it.

check_activation.js
// Task: Determine if an address has been activated on-chain.
const account = await tronWeb.trx.getAccount('TAddress...');
// Unactivated address → empty object {}
// Activated address → object with at least { address: '...', balance: 0 }
const isActivated = Object.keys(account).length > 0;

To maintain network stability, TRON enforces strict transaction size limits:

  • Account Creation: Transactions that create a new account must be strictly less than 1,000 bytes.
  • General Transactions: All transactions (including contract deployment and execution results) are capped at a maximum size of 500 KB.

TRON’s permission system allows an account to require multiple signers for transactions, and to restrict which operations each key can authorize. This is built into the protocol — no multisig contract is needed. (For Web2 developers: Think of this as native Role-Based Access Control (RBAC), similar to requiring multiple admin approvals before executing a critical production deployment).

Common uses:

  • Exchange hot wallets requiring M-of-N approval for withdrawals
  • DAO treasury requiring multiple council members to sign
  • Separating operational keys (daily use) from administrative keys (rare, high-trust operations)
PermissionIDDefaultControls
Owner0Threshold 1, account’s own keyAll operations, including permission updates
Witness1Set only on SR accountsBlock production signing for Super Representatives
Active2–9Auto-created with standard operationsDay-to-day: transfers, contract calls, staking

A new account starts with one Owner permission and one Active permission, both controlled by the account’s own key with threshold 1.

Each permission defines:

  • threshold — minimum total weight of signatures required
  • keys — up to 5 address/weight pairs; each signer contributes their weight when they sign
  • operations — a 32-byte bitmap specifying which of TRON’s 60 transaction types this permission can authorize
setup_multisig.js
// Task: Update account permissions to require 2 of 3 signers.
const tx = await tronWeb.transactionBuilder.updateAccountPermissions(
'TOwnerAddress...', // account being configured
{ // owner permission — keep as single-key
type: 0,
threshold: 1,
keys: [{ address: 'TOwnerAddress...', weight: 1 }],
},
null, // witness permission (null if not an SR)
[{ // active permissions — require 2 of 3 signers
type: 2,
threshold: 2,
operations: '7fff1fc0033efb07000000000000000000000000000000000000000000000000',
keys: [
{ address: 'TSigner1...', weight: 1 },
{ address: 'TSigner2...', weight: 1 },
{ address: 'TSigner3...', weight: 1 },
],
}],
);
const signed = await tronWeb.trx.sign(tx);
await tronWeb.trx.sendRawTransaction(signed);

Cost: 100 TRX for the permission update transaction.

Once a multi-sig threshold is set, transactions from that account must be signed by enough key holders to meet the threshold before broadcast:

sign_multisig.js
// Task: Sign a transaction sequentially with multiple private keys.
// Signer 1 adds their signature
const partialSigned = await tronWeb.trx.multiSign(unsignedTx, privateKey1);
// Signer 2 adds their signature to the same object
const fullySigned = await tronWeb.trx.multiSign(partialSigned, privateKey2);
// Threshold met — broadcast
const result = await tronWeb.trx.sendRawTransaction(fullySigned);

Each additional signer beyond the first adds 1 TRX to the transaction fee.

The operations field is a 256-bit hex string where each bit controls one of TRON’s 60 contract types. The default active permission operations value:

Operations Bitmap
# Task: Note the default bitmap for full active permissions.
7fff1fc0033efb07000000000000000000000000000000000000000000000000

This grants all common operations except AccountPermissionUpdateContract (which is reserved for Owner). If you need a restricted active permission — for example, an operator key that can only send TRX but cannot deploy contracts — you can compute a custom bitmap from the contract type index table in the TRON TIP documentation.


resources_summary.js
// Task: Fetch all available and consumed resources for an account.
const resources = await tronWeb.trx.getAccountResources('TAddress...');
// Energy
console.log(resources.EnergyLimit); // total Energy available from staking
console.log(resources.EnergyUsed); // Energy consumed in current period
console.log(resources.TotalEnergyLimit); // your share of network Energy pool
// Bandwidth
console.log(resources.NetLimit); // staked Bandwidth available
console.log(resources.NetUsed); // staked Bandwidth consumed
console.log(resources.freeNetLimit); // free allocation (600/day)
console.log(resources.freeNetUsed); // free Bandwidth consumed today
// TRON Power (voting weight from staking)
console.log(resources.tronPowerUsed); // TP currently allocated to votes
console.log(resources.tronPowerLimit); // total TP (= total staked TRX)

For staking mechanics and how to increase these limits, see Fee Model & Delegation.