Account Permission Management transaction example
A complete walkthrough of a 2-of-3 multi-signature transaction: setup, building, signing, weight check, and broadcast.
This page walks through a complete multi-signature transaction on TRON. The scenario: a 2-of-3 owner permission, where any two of three signers can authorize transfers on the account. The walkthrough covers configuration, transaction building, signature collection, weight verification, and broadcast.
Prerequisites
Scenario
Three parties — Alice, Bob, and Carol — jointly control an account. The account's owner permission is configured for 2-of-3:
- threshold: 2
- keys: Alice (weight 1), Bob (weight 1), Carol (weight 1)
Any two of them can authorize a transfer; any one alone cannot.
Step 1 — Configure the 2-of-3 permission
The first step is a one-time AccountPermissionUpdateContract that sets up the permission. The transaction must be signed by whoever currently controls the account (typically the original single-key owner). After this transaction confirms, the account is multi-sig.
const updateTxn = await tronWeb.transactionBuilder.updateAccountPermissions(
"TAccount...XX", // owner_address
{
threshold: 2,
keys: [
{ address: "TAlice...AA", weight: 1 },
{ address: "TBob1...BB", weight: 1 },
{ address: "TCarol...CC", weight: 1 }
]
},
null, // no witness permission (not an SR account)
[] // no active permissions in this example
);
const signedUpdate = await tronWeb.trx.sign(updateTxn, originalOwnerKey);
const updateResult = await tronWeb.trx.sendRawTransaction(signedUpdate);
console.log(updateResult.txid);Once the transaction is solidified (~1 minute), the account is under 2-of-3 control.
Step 2 — Build the transaction to be co-signed
Suppose the multi-sig account needs to transfer 100 TRX to a vendor. Any of the three signers can build the unsigned transaction:
const txn = await tronWeb.transactionBuilder.sendTrx(
"TVendor...DD", // recipient
100_000_000, // 100 TRX in sun
"TAccount...XX" // sender (the multi-sig account)
);
// Set Permission_id explicitly to indicate this transaction is signed under the owner permission
txn.raw_data.contract[0].Permission_id = 0;Step 3 — Collect signatures
Signatures can be collected one at a time. Each signer recovers the transaction object, signs it with their key, and passes the partially-signed transaction to the next signer.
Alice signs:
const signedByAlice = await tronWeb.trx.multiSign(txn, aliceKey, 0);
// signedByAlice.signature is now [<alice_sig>]The third argument (0) is the Permission_id — telling the SDK which permission this signature targets.
Pass signedByAlice to Bob.
Bob signs:
const signedByAliceAndBob = await tronWeb.trx.multiSign(signedByAlice, bobKey, 0);
// .signature is now [<alice_sig>, <bob_sig>]The transaction is now fully signed (sum of weights = 1 + 1 = 2, meeting the threshold of 2). It is ready to broadcast. Carol's signature is not needed.
Step 4 — Inspect signature weight before broadcast
To confirm the transaction has met the threshold without broadcasting, query the network:
BASE_URL=https://api.trongrid.io # example — replace with any TRON node (TronGrid, third-party, or self-hosted)
POST ${BASE_URL}/wallet/getsignweight
{
"transaction": <signedByAliceAndBob serialized>
}The response includes:
{
"approved_list": ["TAlice...AA", "TBob1...BB"],
"current_weight": 2,
"permission": {
"threshold": 2,
"keys": [...]
},
"result": {
"code": "NOT_ENOUGH_PERMISSION" | "ENOUGH_PERMISSION"
}
}When current_weight ≥ threshold, result.code is ENOUGH_PERMISSION and the transaction can be broadcast. While still collecting signatures, the code is NOT_ENOUGH_PERMISSION. Other codes such as SIGNATURE_FORMAT_ERROR indicate malformed inputs.
Step 5 — Broadcast
const result = await tronWeb.trx.sendRawTransaction(signedByAliceAndBob);
console.log(result.txid);Wait for the containing block to solidify (~1 minute) before considering the transfer final. See Consensus and DPoS — Block solidification.
Multi-sig surcharge. TRON charges an additional 1 TRX per signature beyond the first when a transaction carries multiple signatures (chain parameter#23getMultiSignFee, current Mainnet default; query live viawallet/getchainparameters). The 2-signature transaction above pays the normal transfer cost plus 1 TRX surcharge. Budget for this when designing multi-sig hot paths — see Account Permission Management § Fees.
Variations
Carol signs instead of Bob
Equivalent to the above — any two of the three keys reach threshold 2.
Adding extra signatures
Adding Carol's signature on top of Alice's and Bob's is harmless. The total weight (1 + 1 + 1 = 3) exceeds the threshold (2), and the transaction is still valid. Extra signatures don't cause rejection.
A signature from outside the keys list
If a fourth party not in the keys list signs the transaction, the entire transaction is rejected with PermissionException at validation time — the unknown signature does not just contribute 0 weight, it invalidates the whole transaction. The same applies to duplicate signers (the same key signing twice) and to transactions whose signature count exceeds the permission's key count. Multi-sig validation is strict; see Signature validation § Multi-signature validation.
Active permissions for delegated subsets
For routine vendor payments, set up an active permission with a smaller threshold and an operations bitmap that allows only TransferContract. Then daily transfers can go through a single delegate key, while owner-level changes (modifying permissions, deploying contracts) still need 2-of-3.
For active permission setup, see Account Permission Management.
APIs
| API | Description |
|---|---|
wallet/accountpermissionupdate | Configure permissions |
wallet/getsignweight | Check collected signature weight |
wallet/getapprovedlist | List signers so far |
wallet/broadcasttransaction | Submit fully-signed transaction |
Related resources
- Account Permission Management — Permission types, weights, contract restrictions
- Signature validation — How signatures are recovered and aggregated
- Multi-sig transaction recipe — Code-focused recipe with full SDK examples
Updated about 17 hours ago