If you run a Tron agent, the setup is almost certainly this:
const tronWeb = new TronWeb({
fullHost: 'https://api.trongrid.io',
privateKey: process.env.TRON_PRIVATE_KEY
});That private key is in memory from the moment the process starts. It is in scope for every function in your application. A prompt injection attack can print it. A bad log serializer can capture it. And the stakes are not small: Tron moves more USDT than any other chain. Over $40 billion a day. A leaked Tron private key is not a credentials rotation problem. It is a permanent loss of everything in that wallet.
We shipped Tron on the Intents API so agents stop holding keys at all. The agent sends chain, recipient, and amount. The server builds the transaction, signs it in the HSM, and broadcasts. Back comes a tx_hash, not a key.
Getting a signing key
A human provisions the key once. Agents cannot create or rotate keys themselves; those endpoints return 403 on purpose.
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/signing-keys" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "chain": "tron" }'You get back a secp256k1 public key and a Base58Check T-address. The private key goes into the org's __agent-keys vault, encrypted under Cloud KMS. The API never returns the private key.
{
"chain": "tron",
"curve": "secp256k1",
"address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"public_key": "04...",
"key_version": 1
}Tron uses the secp256k1 curve, the same as Bitcoin and Ethereum, but with its own Base58Check address encoding that produces the T-prefix. The address is compatible with TronLink, TronScan, and any tooling that speaks the TRON protocol.
Sending TRX and TRC-20 tokens
Transaction construction uses the TronGrid API. No RPC config on your end. Public endpoints work for standard usage without an API key.
# Native TRX transfer on Shasta testnet
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chain": "tron-shasta",
"to": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"value": "10"
}'value is plain TRX as a decimal string. "10"is 10,000,000 sun. The server calls TronGrid's /wallet/createtransaction, signs, and broadcasts.
For TRC-20 tokens, add token_mint and token_decimals:
# USDT transfer on mainnet
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chain": "tron",
"to": "TRecipientAddress...",
"value": "100",
"token_mint": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"token_decimals": 6
}'The server calls /wallet/triggersmartcontract with the transfer(address,uint256) selector, signs the resulting transaction, and broadcasts. The fee_limit_sun field sets the energy fee ceiling (default: 100,000,000 sun, or 100 TRX). Override it if your contract interactions need a different limit.
Supported chains: tron (mainnet), tron-shasta, and tron-nile. The address format is identical across all three.
One operational detail that trips up most teams building Tron agents for the first time: the wallet needs TRX to cover resource costs, not just USDT. Tron uses Energy for smart contract operations (including every TRC-20 transfer) and Bandwidth for basic transactions. Both come from staked TRX or are paid directly from the TRX balance. Fund the provisioned address with TRX before expecting token transfers to go through.
Where signing runs
api.1claw.xyz uses Cloud KMS HSM in Cloud Run. Key material is decrypted inside the HSM boundary, used once, then cleared from memory.
shroud.1claw.xyz and intents.1claw.xyz run the same flow inside AMD SEV-SNP confidential VMs on GKE. Keys only exist in confidential memory inside the enclave. That is the option if you need attestation or a stricter compliance story.
Both support sign-only via POST /v1/agents/:id/transactions/sign. You get the signed transaction back and broadcast it yourself via whatever Tron node you prefer.
The agent never gets the key
Flip intents_api_enabled: true on an agent and two things happen at once: it can call the transaction endpoints, and it gets a 403 if it tries to read private_key or ssh_key secrets through the normal vault API. That is enforced in the handler, not documented as a guideline.
Guardrails run before signing
Blocking key reads closes the obvious exfil path. Guardrails cap what a compromised agent can actually spend or touch. Every Tron submit runs through guardrail checks in the handler before the HSM unwraps anything. Configure them once on the agent via dashboard, SDK, or CLI.
- Allowed chains (
tx_allowed_chains): lock an agent totron-shastaduring dev, ortrononly in prod - Destination allowlist (
tx_to_allowlist): only pay T-addresses you have pre-approved. Anything else returns 403 at signing time - Per-transaction cap (
tx_max_value): max amount in one send, in TRX for native transfers or token units for TRC-20 - Daily spend cap (
tx_daily_limit): rolling 24-hour limit on Tron-family spend. The server trackstx_spent_today_by_chain.tron, not a cross-chain total - Token contract allowlist (
tx_token_allowlist): restrict which token contracts the agent is allowed to call. A USDT settlement agent has no business interacting with an arbitrary TRC-20 contract. Lock it to["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"]and nothing else goes through - Per-chain overrides (
per_chain_guardrails): set Tron-specificmax_value,daily_limit, andto_allowlistunder atronkey. Strictest of global and per-chain wins
Example: a USDT settlement agent on Shasta locked to one recipient and one token contract:
await client.agents.update(agentId, {
intents_api_enabled: true,
tx_allowed_chains: ["tron-shasta"],
tx_token_allowlist: ["TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"],
per_chain_guardrails: {
tron: {
to_allowlist: ["TYourSettlementAddress..."],
max_value: "10000",
daily_limit: "100000",
},
},
});Prompt injection cannot talk its way around this. The agent submits an intent, the server checks the token allowlist, destination allowlist, and caps, only then does signing happen. If anything fails, you get 403 and nothing hits the network.
Rotate and revoke
| Operation | Endpoint |
|---|---|
| Provision | POST /v1/agents/{id}/signing-keys |
| List | GET /v1/agents/{id}/signing-keys |
| Rotate | POST /v1/agents/{id}/signing-keys/tron/rotate |
| Deactivate | DELETE /v1/agents/{id}/signing-keys/tron |
Rotation mints a new secp256k1 keypair in the HSM, bumps key_version, and kills the old key immediately. Your agent code does not need to change.
Try Shasta first
Get free Shasta testnet TRX and USDT from shasta.tronex.io. The faucet issues 2,000 TRX and 1,000 USDT per request. View transactions on shasta.tronscan.org. If you want a second testnet environment, tron-nile is also supported. Use nile.tronscan.org to watch transactions there.
Docs: multi-chain signing keys · non-EVM transaction signing · token guardrails