Transferring TRX in smart contracts

Transfer TRX from inside a Solidity contract using transfer(), send(), or call().

📘

Prerequisites

A smart contract holding TRX can send TRX to any address by calling one of Solidity's built-in address member functions. The common options are transfer(), send(), and call(). Choose between them based on how much Energy the recipient needs and whether failure should revert immediately.

Three transfer methods

transfer()

payable(receiver).transfer(amount);

transfer() forwards a fixed 2,300 Energy stipend to the recipient. If the recipient rejects the transfer or execution fails, transfer() automatically reverts the whole transaction. For most simple user-initiated transfers, this is the easiest default for keeping state consistent.

send()

bool success = payable(receiver).send(amount);
require(success);

send() also forwards only 2,300 Energy, but it does not automatically revert on failure. It returns false. If you use send(), always check the return value; otherwise, later state updates may continue even though the transfer failed.

call()

(bool success, ) = payable(receiver).call{value: amount}("");
require(success);

call{value: ...}("") forwards the remaining available Energy by default. Use it when the recipient contract legitimately needs to run more logic in receive() or fallback(). It also does not automatically revert, so you must check success explicitly.

For the full semantic comparison, refer to the Solidity documentation on address members.


Example: safe TRX transfer

The contract below exposes an owner-only safeTransferTRX function. It validates the recipient, amount, and contract balance before transferring TRX, and reverts before the balance changes if a check fails.

pragma solidity 0.8.0;

contract SimpleTransfer {
    address public owner;

    // Allow the contract to receive TRX
    receive() external payable {}
    fallback() external payable {}

    event TRXTransferred(address indexed recipient, uint256 amount);

    modifier onlyOwner() {
        require(msg.sender == owner, "Caller is not the owner.");
        _;
    }

    constructor() {
        owner = msg.sender;
    }

    // Safely transfer a specified amount of TRX to a given address.
    function safeTransferTRX(address payable recipient, uint256 amount) external onlyOwner {
        require(recipient != address(0), "Recipient cannot be the zero address.");
        require(amount > 0, "Amount must be greater than zero.");
        require(address(this).balance >= amount, "Insufficient TRX balance in contract.");

        // transfer() reverts the transaction automatically if the recipient rejects.
        recipient.transfer(amount);

        emit TRXTransferred(recipient, amount);
    }

    // Return the current TRX balance held by the contract.
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

Development notes

  • The receive() and fallback() functions must be marked payable for the contract to receive and hold TRX.
  • onlyOwner restricts transfers to the account that deployed the contract. Production contracts can instead use role-based access control or withdrawal rules tied to application state.
  • Reject the zero address before transferring to prevent an irreversible loss of funds.
  • Run require checks before the transfer. If a check fails, the transaction reverts before any state change.
  • transfer() emits no event on its own. Emit an application-level event, such as TRXTransferred, when indexers or explorers need to track the movement.

Related resources