Account Permission Management

How TRON accounts support multi-party control through weighted permissions and signature thresholds.

A TRON account does not need to be controlled by a single private key. Through Account Permission Management (TIP-16), an account can be jointly operated by multiple parties — each holding their own keys, each with a configurable weight, and each transaction authorized only when the sum of signatures meets a threshold.

This page covers the three permission types, the threshold-and-weight model, the structure of the permission update contract, the operations bitmap that restricts active permissions to specific contract types, and the fees charged for permission updates and multi-signed transactions.

📘

Prerequisites


Permission types

Each TRON account has three permission slots:

PermissionDefault purposeNotes
OwnerFull control — can execute any contract type and modify the account's own permissionsDefault permission for new accounts
WitnessUsed by Super Representative (SR) accounts to sign block productionMust contain exactly 1 key; cannot authorize normal contract transactions
ActiveCustom-scoped permissions for delegated operationUp to 8 active permissions per account

The proto-level name witness_permission is the same as what TRON prose calls "Super Representative (SR) permission". This page uses Witness when referring to the permission type (matching the code), and SR when discussing the role. For the broader witness-vs-SR convention, see Consensus and DPoS.


Threshold and weight

A permission is authorized when the sum of signing weights meets or exceeds the threshold. Each permission is configured with:

  • A threshold — a positive integer
  • A list of keys, each with a base58 address and a weight (int64)

When a transaction is signed, the network recovers the signing addresses, looks them up in the relevant permission's keys list, sums the weights of the recognized signers, and checks the sum against the threshold.

The configuration must satisfy these constraints (enforced at AccountPermissionUpdateContract validation):

RuleDetail
Max keys per permissionFixed at 5
Distinct addressesA keys list cannot contain the same address twice
Positive weightsEach key's weight > 0
Sum ≥ thresholdThe sum of all key weights must be at least the threshold (otherwise threshold is unreachable)
permission_name lengthAt most 32 bytes
parent_idMust be 0
Witness permission single-keyWitness type permissions must contain exactly 1 key

Single-signer (default)

A new account starts with a single-signer owner permission:

threshold: 1
keys:
  - address: <account's own address>, weight: 1

A transaction signed by the account's private key produces a sum of 1, meeting the threshold, and is authorized.

Multi-party

A 2-of-3 owner permission across three parties:

threshold: 2
keys:
  - address: TKey1...AA, weight: 1
  - address: TKey2...BB, weight: 1
  - address: TKey3...CC, weight: 1

Any two of the three signers can authorize a transaction.

Weighted

Different parties can hold different authority levels:

threshold: 3
keys:
  - address: TFounder...A, weight: 2
  - address: TCofnd1...B, weight: 1
  - address: TCofnd2...C, weight: 1

A founder (weight 2) plus any one cofounder (weight 1) reaches threshold 3. Either cofounder alone (weight 1) does not. Both cofounders working without the founder (1 + 1 = 2) does not.


Active permissions and contract restrictions

Active permissions go beyond simple multi-sig: each active permission carries an operations field that restricts which contract types it may execute. This lets you grant a delegate the authority to perform specific actions without exposing the full owner permission.

The operations field is a 32-byte (256-bit) bitmap encoded little-endian. Each bit corresponds to a ContractType value from Tron.proto. Contract type ID n is bit (n & 7) of byte n / 8:

  • ID 0 → byte 0, bit 0 (least-significant bit of first byte)
  • ID 7 → byte 0, bit 7
  • ID 8 → byte 1, bit 0
  • ID 59 → byte 7, bit 3

A bit set to 1 means "this permission can execute this contract type". A bit set to 0 means "this contract type is forbidden under this permission".

Contract type IDs

Every TRON contract type has a numeric ID from the Transaction.Contract.ContractType proto enum. The table below lists popular transaction supported on mainnet:

IDContractTypeDescription
0AccountCreateContractCreate an account
1TransferContractTransfer TRX
2TransferAssetContractTransfer a TRC-10 token
4VoteWitnessContractVote for a Super Representative
5WitnessCreateContractApply to become an SR candidate
11FreezeBalanceContractStake 1.0 — stake TRX
12UnfreezeBalanceContractStake 1.0 — unstake TRX
13WithdrawBalanceContractWithdraw voting rewards
16ProposalCreateContractCreate a governance proposal
17ProposalApproveContractApprove a proposal
30CreateSmartContractDeploy a smart contract
31TriggerSmartContractCall a smart contract
33UpdateSettingContractUpdate consume_user_resource_percent
45UpdateEnergyLimitContractAdjust the Energy limit a deployer subsidizes
46AccountPermissionUpdateContractUpdate account permissions
48ClearABIContractClear a contract's ABI
49UpdateBrokerageContractUpdate SR brokerage rate
54FreezeBalanceV2ContractStake 2.0 — stake TRX
55UnfreezeBalanceV2ContractStake 2.0 — unstake TRX
56WithdrawExpireUnfreezeContractWithdraw unstaked TRX after the cooldown
57DelegateResourceContractDelegate Bandwidth or Energy
58UnDelegateResourceContractCancel resource delegation
59CancelAllUnfreezeV2ContractCancel all pending unstake operations

Source: protocol/src/main/protos/core/Tron.proto enum ContractType. The list reflects the protocol as of java-tron v4.8+. For the source-of-truth enum, also see Details of supported transaction types.

Computing the operations bitmap

To allow a permission to execute, say, TRX transfers (ID 1), SR voting (ID 4), and Stake 2.0 staking (ID 54), compute the bitmap by setting bits 1, 4, and 54:

import org.bouncycastle.util.encoders.Hex;

int[] contractIds = { 1, 4, 54 };  // TransferContract, VoteWitnessContract, FreezeBalanceV2Contract
byte[] operations = new byte[32];
for (int id : contractIds) {
  operations[id / 8] |= (byte) (1 << (id % 8));
}
System.out.println(Hex.toHexString(operations));
// "1200000000004000000000000000000000000000000000000000000000000000"

Decode in the reverse direction:

public static List<Integer> decodeOperations(String operationsHex) {
  byte[] ops = Hex.decode(operationsHex);
  List<Integer> ids = new ArrayList<>();
  for (int i = 0; i < ops.length; i++) {
    for (int bit = 0; bit < 8; bit++) {
      if ((ops[i] >> bit & 0x1) == 1) {
        ids.add(i * 8 + bit);
      }
    }
  }
  return ids;
}

Worked encoding examples

Operations allowedBinary (big-endian by byte, low bit first within each byte)Hex (little-endian byte order)
TransferContract (ID 1) + VoteWitnessContract (ID 4)00010010 00000000 00000000 …12 00 00 …
TransferContract (ID 1) + UpdateAssetContract (ID 15)00000010 10000000 00000000 …02 80 00 …
All system contracts (per Mainnet defaults)01111111 11111111 00011111 …7F FF 1F …

Decoding a hex string by eye: read each byte as a binary nibble pair, with the least-significant bit being the lowest contract ID for that byte. Byte 0's bit 0 = contract type ID 0; byte 0's bit 7 = ID 7; byte 1's bit 0 = ID 8; and so on.

Use case: a hot key for transfers only

A treasury account's owner permission is held by a multi-sig group. To pay routine vendor invoices without convening the multi-sig every time, the treasury creates an active permission with:

  • A single-signer threshold (e.g., threshold: 1, one delegate key with weight: 1)
  • An operations bitmap that allows only TransferContract (ID 1) — hex 02 00 00 …
  • All other contract types disabled — the delegate cannot stake, vote, deploy contracts, or modify permissions

The delegate's hot key can sign vendor transfers, but cannot perform any other operation. The owner permission remains in cold storage.


Updating permissions

To modify any permission, the account submits an AccountPermissionUpdateContract transaction. This contract overwrites all three permission slots at once — even if you only want to change one, you must include the other two unchanged in the contract body:

message AccountPermissionUpdateContract {
  bytes owner_address = 1;
  Permission owner = 2;
  Permission witness = 3;
  repeated Permission actives = 4;
}
FieldDescription
owner_addressThe account being modified
ownerThe new owner permission (required, not optional)
witnessThe new Witness permission (only meaningful for SR accounts; otherwise null)
activesThe new list of active permissions (up to 8)

The transaction must be signed under the account's existing owner permission.

Permission_id in transactions

Every transaction declares which permission level it is signed under via Permission_id in the contract body:

Permission_idPermission
0Owner permission (default if omitted)
1Witness permission — reserved for SR block production; cannot authorize normal contract transactions
2–9Active permissions, in the order they were added to the account

The id of an active permission is automatically assigned by the network in sequence starting at 2. For any Permission_id != 0, the permission's type must equal Active and the requested contract type must be allowed by the operations bitmap; otherwise the transaction is rejected with Permission type is wrong! or Permission denied!.


Fees

Permission management on TRON charges two kinds of fees. Both are dynamic chain parameters and can be adjusted by governance proposal — query the live values via wallet/getchainparameters rather than hard-coding them.

FeeChain parameterCurrent Mainnet defaultWhen charged
Update account permissions#22 getUpdateAccountPermissionFee100 TRX (100,000,000 sun)Every AccountPermissionUpdateContract transaction
Multi-signature surcharge#23 getMultiSignFee1 TRX (1,000,000 sun)Charged once when a transaction carries two or more signatures

Both fees are added on top of the normal Bandwidth or Energy cost of the transaction.


Worked example: 2-of-3 multi-sig setup

To configure a 2-of-3 owner permission on an account, send an AccountPermissionUpdateContract with:

{
  "owner_address": "TYourAccount...XX",
  "owner": {
    "type": 0,
    "id": 0,
    "permission_name": "owner",
    "threshold": 2,
    "keys": [
      { "address": "TKey1...AA", "weight": 1 },
      { "address": "TKey2...BB", "weight": 1 },
      { "address": "TKey3...CC", "weight": 1 }
    ]
  },
  "witness": null,
  "actives": [
    {
      "type": 2,
      "id": 2,
      "permission_name": "active0",
      "threshold": 1,
      "operations": "7fff1fc0037e0000000000000000000000000000000000000000000000000000",
      "keys": [
        { "address": "TKey1...AA", "weight": 1 }
      ]
    }
  ]
}

After this transaction confirms, all owner-level operations on the account require any two of the three keys. The active0 permission (id 2) lets TKey1 execute the operations enabled by the operations bitmap on its own — a hot-key path for routine work.

For the end-to-end transaction flow with multi-party signing, see Account Permission Management transaction example.


Limits

LimitValue
Maximum keys per permissionFixed at 5
Maximum active permissions per account8 (hardcoded)
permission_name length32 bytes
Permission_id range0–9 (0 = owner, 1 = Witness, 2–9 = active)
Witness permission key countMust be exactly 1
Active permission parent_idMust be 0

APIs

APIDescription
wallet/accountpermissionupdateUpdate account permissions
wallet/getsignweightInspect collected signature weight on a partial transaction
wallet/getapprovedlistList addresses that have signed
wallet/broadcasttransactionSubmit a fully-signed transaction

Related resources