Tron (TRX)
Sign Tron transactions in the native TronWeb raw_data.contract[] shape. Plain TRX sends use a TransferContract; TRC20 token transfers and approvals use a TriggerSmartContract with ABI-encoded data. The bridge does not refresh ref_block_* — the app supplies those and they are signed exactly as sent. expiration and timestamp are different: the bridge always regenerates them locally from Date.now(), so any value you send for those two is replaced (see the field table).
| Field | Value |
|---|---|
| Family | Tron |
| coinType | 195 |
| keyPath | m/44'/195'/<accountIdx>'/0/0 |
| amount unit | SUN |
| chainId | tron:0x2b6653dc/slip44:195 |
Supported Methods
| Method | Supported |
|---|---|
| getAddress | ✓ |
| signTransaction | ✓ |
| signMessage | ✗ |
| signTypedData | ✗ |
Requirements
Minimum firmware for each method. The two models are on different version lines, so DCENT Biometric Wallet and DCENT X are listed separately (the numbers are not comparable across models). Below this the request returns the firmware-update error (5005) and the bridge prompts to update.
| Method | DCENT Biometric Wallet | DCENT X |
|---|---|---|
getAddress | 2.3.0 or higher | 1.0.0 or higher |
signTransaction | 2.3.0 or higher | 1.0.0 or higher |
owner_address / to_address / contract_address accept either base58 (T…) or hex (41…). 1 TRX = 1,000,000 SUN. Amounts are in SUN; fee_limit is also in SUN.
getAddress — account address
Retrieves the Tron account address for a chainId + keyPath. Returns a base58 address (T…). The same account can also be expressed as a 21-byte hex address with a 0x41 prefix.
| Field | Type | Required | Source | Description |
|---|---|---|---|---|
chainId | string | Required | App | CAIP-19 chain id — tron:0x2b6653dc/slip44:195 |
keyPath | string | Required | App | BIP-44 derivation path — default m/44'/195'/0'/0/0 |
Request
await dcent.getAddress({
chainId: 'tron:0x2b6653dc/slip44:195',
keyPath: "m/44'/195'/0'/0/0"
})
Response
| Field | Type | Description |
|---|---|---|
header.status | string | success on completion |
body.command | string | Echoes getAddress |
body.parameter.address | base58 | Tron account address in base58 (T…) |
{
"header": {
"version": "1.0",
"status": "success"
},
"body": {
"command": "getAddress",
"parameter": {
"address": "T…"
}
}
}
signTransaction — TronWeb raw_data
Variant — native TRX transfer (TransferContract)
payload.transaction:
{
"raw_data": {
"contract": [
{
"type": "TransferContract",
"parameter": {
"value": {
"owner_address": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"to_address": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"amount": 1000000
},
"type_url": "type.googleapis.com/protocol.TransferContract"
}
}
],
"ref_block_bytes": "0000",
"ref_block_hash": "0000000000000000",
"expiration": 0,
"timestamp": 0
},
"fee": 1100000,
"raw_data_hex": "",
"fee_limit": 0
}
| Field | Type | Required | Source | Description |
|---|---|---|---|---|
raw_data.contract[].type | enum | Required | App | TransferContract for native TRX |
parameter.value.owner_address | base58 / hex | Required-Real | Wallet | Device Tron account that signs |
parameter.value.to_address | base58 / hex | Required-Real | App | Recipient address |
parameter.value.amount | number | Required | App | Value in SUN (1 TRX = 1e6) |
ref_block_bytes / ref_block_hash | hex | Required-Real | App | Real, current values from a node — the app must supply them; not auto-filled |
expiration / timestamp | number | Auto-filled | bridge (local) | Generated by the bridge locally (Date.now) — any value you send is replaced |
fee_limit | number | Optional | App | SUN; 0 for plain TRX (no contract energy) |
fee | number | Required-Real | App | Total fee in SUN, sent at the top level next to raw_data (display only). If you omit it the bridge substitutes 0, and a non-explicit zero is rejected with -32602 |
Request
await dcent.sign({
method: 'signTransaction',
chainId: 'tron:0x2b6653dc/slip44:195',
payload: {
keyPath: "m/44'/195'/0'/0/0",
transaction: {
raw_data: {
contract: [
{
type: 'TransferContract',
parameter: {
value: {
owner_address: 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb',
to_address: 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb',
amount: 1000000
},
type_url: 'type.googleapis.com/protocol.TransferContract'
}
}
],
ref_block_bytes: '0000',
ref_block_hash: '0000000000000000',
expiration: 0,
timestamp: 0
},
raw_data_hex: '',
fee: 1100000,
fee_limit: 0
}
}
})
Response
| Field | Type | Description |
|---|---|---|
header.status | string | success on completion |
body.command | string | Echoes signTransaction |
body.parameter.signature | hex | The broadcast-ready signed transaction in TronWeb format — the fully signed raw_data serialization you broadcast via TronWeb (broadcastTransaction), not a bare signature. |
{
"header": {
"status": "success"
},
"body": {
"command": "signTransaction",
"parameter": {
"signature": "<signed tx signature hex…>"
}
}
}
Variant — TRC20 transfer (TriggerSmartContract)
Token model: form-D descriptor. Always send the token's symbol and decimals under transaction.token — the bridge signs with no network access and uses the decimals you supply directly, so the device shows the correct token name and amount. See Core Concepts → Token descriptors.
payload.transaction:
{
"raw_data": {
"contract": [
{
"type": "TriggerSmartContract",
"parameter": {
"value": {
"owner_address": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"data": "a9059cbb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
}
}
],
"ref_block_bytes": "0000",
"ref_block_hash": "0000000000000000",
"expiration": 0,
"timestamp": 0
},
"fee": 1100000,
"raw_data_hex": "",
"fee_limit": 100000000
}
| Field | Type | Required | Source | Description |
|---|---|---|---|---|
raw_data.contract[].type | enum | Required | App | TriggerSmartContract for TRC20 |
parameter.value.owner_address | base58 / hex | Required-Real | Wallet | Device account that signs |
parameter.value.contract_address | base58 / hex | Required-Real | App | Token contract (e.g. USDT-TRC20 TR7NHq…) |
parameter.value.data | hex | Required-Real | App | ABI calldata: a9059cbb + recipient (32B) + amount (32B) |
fee_limit | number | Required | App | SUN energy cap (e.g. 100000000 = 100 TRX). Currently ignored on the v2 wire path — the bridge applies a fixed 50 TRX floor regardless of what you send. |
fee | number | Required-Real | App | Total fee in SUN, sent at the top level next to raw_data (display only). Omit it and the bridge substitutes 0, which is rejected with -32602 |
The data field is the standard ERC20-style transfer(address,uint256) selector a9059cbb followed by the recipient (left-padded to 32 bytes) and the amount (32 bytes). The example amount 0x3e8 = 1000 = 0.001 USDT at 6 decimals.
Variant — TRC20 approve (app request)
payload.transaction:
{
"raw_data": {
"contract": [
{
"type": "TriggerSmartContract",
"parameter": {
"value": {
"owner_address": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"data": "095ea7b30000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
}
}
],
"ref_block_bytes": "0000",
"ref_block_hash": "0000000000000000",
"expiration": 0,
"timestamp": 0
},
"fee": 1100000,
"raw_data_hex": "",
"fee_limit": 100000000
}
Same TriggerSmartContract shape, but data uses the approve(address,uint256) selector 095ea7b3 + spender (32B) + allowance (32B). The example grants an unlimited (0xff…ff) allowance — prefer approving only the amount you need.
Variant — TRC20 form-D descriptor
Send a compact token descriptor instead of a full raw_data envelope. The bridge builds the a9059cbb transfer(address,uint256) calldata from { contract, to, amount }, wraps it in a TriggerSmartContract, and signs. owner_address (or from) is read-only and identifies the signer — omitting it fails with -32602. amount is a base-unit string (not rescaled by decimals).
payload.transaction:
{
"token": {
"contract": "TThzxNRLrW2Brp9DcTQU8i4Wd9udCWEdZ3",
"to": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"amount": "1000000000000000",
"decimals": 18,
"symbol": "stUSDT"
},
"owner_address": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"fee": 1100000,
"raw_data": {
"ref_block_bytes": "b801",
"ref_block_hash": "e4a5f6d7c8b9a0e1"
}
}
| Field | Type | Required | Notes |
|---|---|---|---|
token.contract | string | Required | TRC-20 contract address (T…); unregistered tokens allowed. |
token.to | string | Required | Recipient (T… or hex 41…). |
token.amount | string | Required | Base-unit string; not rescaled by decimals. |
token.decimals | number | Required | Base-unit exponent; enables descriptor resolution. |
token.symbol | string | Optional | Display metadata only. |
owner_address | string | Required-Real | Device TRON account that signs (read-only; from also accepted). Omitting it fails with -32602. |
raw_data.ref_block_bytes | hex | Required | TAPOS reference — the app must build this from a recent solid block (must be within the last 65536 blocks to broadcast). No-network signing still requires a value; it is not defaulted. |
raw_data.ref_block_hash | hex | Required | TAPOS reference, paired with ref_block_bytes. |
fee | number | Required-Real | Total fee in SUN, sent at the top level next to raw_data (display only). Omit it and the bridge substitutes 0, which is rejected with -32602 |
decimals / symbol are display metadata; amount "1000" is already in base units. Replace token.to / owner_address with your own device TRON account (T…).
Common Mistakes
Amounts not in SUN — native amount is SUN, not TRX (1 TRX = 1e6 SUN). fee_limit of 0 on a TriggerSmartContract — TRC20 calls need an energy cap (e.g. 100 TRX); a 0 limit fails on broadcast. Unlimited approve — granting 0xff…ff in 095ea7b3 approve leaves a standing allowance; scope it to the needed amount and replace the placeholder spender with the real contract. Mismatched data encoding — the recipient/amount in TRC20 data must be left-padded to 32 bytes each; a malformed selector or padding signs the wrong transfer.