Send your first transaction
End-to-end tutorial: install TronWeb, generate keys, fund a Shasta testnet account, and broadcast your first TRX transfer in 15 minutes.
Prerequisites
This tutorial walks through your first TRX transaction on the TRON Shasta testnet. You will install TronWeb, generate a key pair, obtain test tokens, build and sign a transaction, broadcast it, and verify the result on TronScan. The full flow takes about 15 minutes.
Just want the code?See the Send your first transaction recipe — same flow, condensed to a copy-paste snippet with a cost summary and error table.
Step 1 — Install TronWeb
Install a currently supported Node.js LTS release and npm, then create a project with the TronWeb version used in this tutorial:
mkdir tron-hello && cd tron-hello
npm init -y
npm install --save-exact [email protected]Step 2 — Generate a key pair
Every TRON account is controlled by a private key. Generate two Shasta-only accounts: a sender that will receive and transfer test TRX, and a recipient for verifying the result.
const { TronWeb } = require('tronweb');
const sender = TronWeb.createRandom();
const recipient = TronWeb.createRandom();
console.log('Sender address:', sender.address);
console.log('Sender private key:', sender.privateKey);
console.log('Recipient address:', recipient.address);
console.log('Recipient private key:', recipient.privateKey);Store both sets of test credentials securely and label them clearly. Depending on the SDK version, a displayed private key may begin with 0x; the code in Step 4 accepts either form.
Never share your private keyAnyone with the private key controls the account. This tutorial uses the testnet, where tokens have no real value, but treat the key the same way you would on Mainnet.
Step 3 — Get testnet TRX
Visit the Faucet and paste the sender's Base58 address to receive test TRX. Keep test TRX in addition to the 10 TRX transfer amount to cover possible Bandwidth and account-activation costs. If the recipient is not activated, this transfer creates its onchain account state. These costs are controlled by the current chain parameters; query wallet/getchainparameters before sending. See Account activation.
Step 4 — Send 10 TRX
Create a file called send.js:
const { TronWeb } = require('tronweb');
const rawPrivateKey = process.env.TRON_PRIVATE_KEY || '';
const privateKey = rawPrivateKey.replace(/^0x/i, '');
if (!/^[0-9a-fA-F]{64}$/.test(privateKey)) {
throw new Error('Set TRON_PRIVATE_KEY to a 64-character hexadecimal Shasta test private key');
}
const recipientAddress = process.env.TRON_RECIPIENT_ADDRESS;
if (!TronWeb.isAddress(recipientAddress)) {
throw new Error('Set TRON_RECIPIENT_ADDRESS to a valid TRON recipient address');
}
const tronWeb = new TronWeb({
fullHost: 'https://api.shasta.trongrid.io',
privateKey
});
async function sendTrx() {
const amount = 10 * 1_000_000; // 10 TRX in sun (1 TRX = 1,000,000 sun)
try {
const tx = await tronWeb.transactionBuilder.sendTrx(recipientAddress, amount);
const signed = await tronWeb.trx.sign(tx);
// Save the txID before broadcast so the same transaction can still be queried after a timeout.
console.log('Transaction ID:', signed.txID);
try {
const result = await tronWeb.trx.sendRawTransaction(signed);
if (result.result !== true) {
const message = /^(?:[0-9a-fA-F]{2})+$/.test(result.message || '')
? Buffer.from(result.message, 'hex').toString('utf8')
: result.message;
throw new Error(`${result.code || 'UNKNOWN'} ${message || ''}`.trim());
}
console.log('Broadcast call: no reported error');
} catch (error) {
console.error('Broadcast call reported an error or the result is unknown:', error);
console.error(`Keep querying the original txID: ${signed.txID}`);
process.exitCode = 1;
}
} catch (error) {
console.error('Transaction construction or signing failed:', error);
process.exitCode = 1;
}
}
sendTrx();Before running the script, replace the recipient-address placeholder below with the recipient generated in Step 2:
read -s TRON_PRIVATE_KEY
export TRON_PRIVATE_KEY
export TRON_RECIPIENT_ADDRESS='recipient address from Step 2'
node send.js
# Output: Transaction ID: 5f3e4e0a... Broadcast call: no reported error
unset TRON_PRIVATE_KEY TRON_RECIPIENT_ADDRESSStep 5 — Verify on TronScan
Open https://shasta.tronscan.org/#/transaction/<YOUR_TX_ID> and search with the txID printed before broadcast in Step 4. Verify the sender, recipient, amount (10 TRX), execution result, block, and resource use, and wait until the block is solidified. Finally, compare both account balances before and after the transfer. If the recipient was previously inactive, also confirm that its onchain account state now exists.
If the transaction is temporarily unavailable, do not immediately build and send another transfer. Continue querying the original txID, then follow How should I retry when a transaction cannot be found after broadcast? before retrying.
Java alternative (Trident SDK)
This example uses JDK 8 or 17, Maven 3.8+, and Trident 1.0.0. In an empty directory, create pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>send-trx</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>io.github.tronprotocol</groupId>
<artifactId>trident</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.1</version>
</plugin>
</plugins>
</build>
</project>Create the source directory:
mkdir -p src/main/javaSave the following class as src/main/java/SendTrx.java:
import org.tron.trident.core.ApiWrapper;
import org.tron.trident.proto.Chain.Transaction;
import org.tron.trident.proto.Response.TransactionExtention;
public class SendTrx {
public static void main(String[] args) throws Exception {
String privateKey = System.getenv("TRON_PRIVATE_KEY");
if (privateKey != null && (privateKey.startsWith("0x") || privateKey.startsWith("0X"))) {
privateKey = privateKey.substring(2);
}
if (privateKey == null || !privateKey.matches("[0-9a-fA-F]{64}")) {
throw new IllegalArgumentException("TRON_PRIVATE_KEY must contain 64 hexadecimal characters");
}
String recipientAddress = System.getenv("TRON_RECIPIENT_ADDRESS");
if (recipientAddress == null || recipientAddress.isEmpty()) {
throw new IllegalArgumentException("TRON_RECIPIENT_ADDRESS must contain a TRON recipient address");
}
ApiWrapper.parseAddress(recipientAddress);
ApiWrapper client = ApiWrapper.ofShasta(privateKey);
try {
String senderAddress = client.keyPair.toBase58CheckAddress();
TransactionExtention tx = client.transfer(
senderAddress, recipientAddress, 10_000_000L);
Transaction signed = client.signTransaction(tx);
String txid = ApiWrapper.toHex(ApiWrapper.calculateTransactionHash(signed));
System.out.println("Transaction ID: " + txid);
try {
String broadcastTxid = client.broadcastTransaction(signed);
if (!txid.equalsIgnoreCase(broadcastTxid)) {
throw new IllegalStateException("Broadcast returned a different transaction ID");
}
System.out.println("Broadcast call: no reported error");
} catch (RuntimeException error) {
System.err.println("Broadcast call reported an error or the result is unknown.");
System.err.println("Keep querying the original txID: " + txid);
throw error;
}
} finally {
client.close();
}
}
}Set the sender private key and recipient address, replace the recipient-address placeholder, and run the class:
read -s TRON_PRIVATE_KEY
export TRON_PRIVATE_KEY
export TRON_RECIPIENT_ADDRESS='recipient address from Step 2'
mvn -q compile exec:java -Dexec.mainClass=SendTrx
unset TRON_PRIVATE_KEY TRON_RECIPIENT_ADDRESSWhat happened under the hood
- Transaction construction. TronWeb created a
TransferContracttransaction — one of TRON's system contract types. - Signing. Your private key produced an ECDSA (secp256k1) digital signature.
- Broadcasting. The signed transaction was sent to a full node via HTTP API.
- Block inclusion. A Super Representative can include it in a subsequent block; TRON normally produces a block every three seconds.
- Resource cost. This simple transfer consumed Bandwidth but no Energy. The current
getFreeNetLimitparameter gives each account 600 Bandwidth, with usage recovering over a rolling 24-hour window. A typical TRX transfer consumes about 270 Bandwidth; whether TRX is burned depends on the transaction size and the account's available Bandwidth at that time.
Next steps
- Resource model — understand Bandwidth and Energy in detail
- Deploy a smart contract — deploy your first Solidity contract on TRON
- Connect to the TRON network — endpoints and SDK initialization reference
Related resources
- Query balance and resources — check an account's TRX, tokens, Bandwidth, and Energy
- Blockchain browsers — explore transactions, blocks, and accounts on TronScan
Updated 8 days ago