Tron Create Send TRC20 Tx


TRC20 (TRC20 Token Standard) is a token standard based on the implementation of smart contract when using TRON network to issue a token.

A TRC20 (smart contract compatible token) transfer consumes both bandwidth and energy.


Generate Send TRC20 Raw Tx

<?php 
use IEXBase\TronAPI\Tron;
use IEXBase\TronAPI\Support;
use Web3\Contracts\Ethabi;
use Web3\Contracts\Types\{Address, Boolean, Bytes, DynamicBytes, Integer, Str, Uinteger};
use kornrunner\Keccak;
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;

include_once "../libraries/vendor/autoload.php";
include_once("html_iframe_header.php");
include_once("tron_utils.php");

//include all php files that generated by protoc
$dir   = new RecursiveDirectoryIterator('protobuf/core/');
$iter  = new RecursiveIteratorIterator($dir);
$files = new RegexIterator($iter, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH); // an Iterator, not an array
foreach ( $files as $file ) {
    
    if (is_array($file)) {
        foreach($file as $filename) {
            include $filename;
        }
    } else {
        include $file;
    }
}

define("TRX_TO_SUN",'1000000');
define("SUN_TO_TRX", '0.000001');

$supportChains = ['main'=>"Tron Mainnet", 'shasta'=>"Shasta Testnet", 'nile'=>"Nile Testnet"];

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    try {
		
		$feeLimit = $_POST['fee_limit'];
		$feeLimitInSun = bcmul($feeLimit, TRX_TO_SUN);
		
		if (!is_numeric($feeLimit) OR $feeLimit <= 0) {
			throw new Exception('fee_limit is required.');
		} else if($feeLimit > 1000) {
            throw new Exception('fee_limit must not be greater than 1000 TRX.');
		}

		if ($_POST['chain'] == 'main') {
			$fullNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
			$solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
			$eventServer = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
		} else if ($_POST['chain'] == 'shasta') {
			$fullNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.shasta.trongrid.io');
			$solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.shasta.trongrid.io');
			$eventServer = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.shasta.trongrid.io');
		} else if ($_POST['chain'] == 'nile') {
			$fullNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://nile.trongrid.io');
			$solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://nile.trongrid.io');
			$eventServer = new \IEXBase\TronAPI\Provider\HttpProvider('https://nile.trongrid.io');
		}
		
		$tron = new \IEXBase\TronAPI\Tron($fullNode, $solidityNode, $eventServer);
		
		if ($_POST['generate_way'] == 'Generate Offline') {
			//[GENERATE ENCODED DATA FOR TOKEN CONTRACT]
			$ethAbi = new Ethabi(['address' => new Address,'bool' => new Boolean,'bytes' => new Bytes,'dynamicBytes' => new DynamicBytes,'int' => new Integer,'string' => new Str,'uint' => new Uinteger,]);
			
			$function = "transfer(address,uint256)";
			$functionAbi = json_decode('{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}',true);
			$functionSignature = ltrim($ethAbi->encodeFunctionSignature($function), '0x');

			$recipient = $_POST['recipient'];
			$tokenAmount = bcmul($_POST['token_amount'], bcpow("10", $_POST['token_decimals'], 0), 0);
			$contractParams = [base58check2HexString($recipient),$tokenAmount];
			$parameters = substr($ethAbi->encodeParameters($functionAbi, $contractParams),2);
			
			//[GENERATE CONTRACT'S SERIALIZED HEX]
			//get owner address from private key
			$privKeyFactory = new PrivateKeyFactory();
			$privateKey = $privKeyFactory->fromHexUncompressed($_POST['privkey']);
			$publicKey  = $privateKey->getPublicKey();
			$publicKeyHex = substr($publicKey->getHex(), 2);
			
			$ownerAddressHex = Keccak::hash(hex2bin($publicKeyHex), 256);
			$ownerAddressHex = "41" . substr($ownerAddressHex, -40);
			
			$ownerAddressBin = hex2str($ownerAddressHex);
			$contractAddressBin = hex2str(base58check2HexString($_POST['contract_addr']));
			$callValue = "0";
			
			$contract = new \Protocol\Transaction\Contract();
			$triggerSmartContract = new \Protocol\TriggerSmartContract();
			
			$triggerSmartContract->setData(hex2str($functionSignature.$parameters ));
			$triggerSmartContract->setOwnerAddress($ownerAddressBin);
			$triggerSmartContract->setContractAddress($contractAddressBin);
			$triggerSmartContract->setCallValue($callValue);
			
			$any = new \Google\Protobuf\Any();
			$any->pack($triggerSmartContract);
			
			$contract->setParameter( $any );
			$contract->setType( \Protocol\Transaction\Contract\ContractType::TriggerSmartContract );
			
			//[GENERATE RAW TX]
			//get current block
			$newestBlock = $tron->getCurrentBlock();
			$currentHeight = (int)$newestBlock['block_header']['raw_data']['number'];
			if ($currentHeight<=0) {
				throw new Exception("Fail retrieve current block.");
			}
			
			//get last confirmed block
			$confirmation = 20;
			$targetHeight = ($currentHeight - $confirmation) + 1;
			
			$confirmedBlock = $tron->getBlockByNumber($targetHeight);
			$blockHeight = (int)$confirmedBlock['block_header']['raw_data']['number'];
			$blockTs = (int)$confirmedBlock['block_header']['raw_data']['timestamp'];
			$blockHash = $confirmedBlock['blockID'];
			
			$currentTimeMillis = round(microtime(true) * 1000);
			
			//build tx
			$raw = new \Protocol\Transaction\Raw();
			$raw->setContract([$contract]);
			$raw->setFeeLimit($feeLimitInSun);
			
			$blockHeightIn64bits = str_pad(dechex($blockHeight), 8 * 2 /* 8 bytes = 16 hex chars*/, "0", STR_PAD_LEFT);
			
			$raw->setRefBlockBytes( hex2Str( $refBlockBytes = substr($blockHeightIn64bits, 12, 4) ));
			$raw->setRefBlockHash( hex2Str( $refBlockHash =  substr($blockHash, 16, 16) ));
			$raw->setTimestamp($currentTimeMillis);
			$raw->setExpiration( $blockTs + (10 * 60 * 60 * 1000) );#expiration set 10 hours from last confirmed block
			$txId = hash("sha256", $raw->serializeToString());
			$rawData = str2hex($raw->serializeToString());
			
			$tx = new \Protocol\Transaction();
			$tx->setRawData($raw);
			
			$signature = Support\Secp::sign($txId, $_POST['privkey']);
			$tx->setSignature([hex2str( $signature )]);
			
			?>
			<div class="alert alert-success">
		   
				<h6 class="mt-3">Raw Tx (Hex)</h6>
				<textarea class="form-control" rows="5" id="comment" readonly><?php echo str2hex($tx->serializeToString());?></textarea>
				
				<h6 class="mt-3">Raw Data (Hex)</h6>
				<textarea class="form-control" rows="5" id="comment" readonly><?php echo $rawData;?></textarea>
				<small>To sign manually, you may access to <a href="tron_sign_raw_data.php">Tron Sign Raw Data</a> page.</small>
				
				<h6 class="mt-3">Contract Serialized Hex</h6>
				<textarea class="form-control" rows="5" id="comment" readonly><?php echo str2hex($contract->serializeToString());?></textarea>
				
				<h6 class="mt-3">Encoded Data (Hex)</h6>
				<textarea class="form-control" rows="5" id="comment" readonly><?php echo "0x".$functionSignature.$parameters ;?></textarea>
				
				<h6 class="mt-3">Function</h6>
				<input class="form-control" value="<?php echo $function ;?>" readonly/>
				
				<h6 class="mt-3">TX Hash</h6>
				<input class="form-control" readonly value="<?php echo $txId?>"/>
			</div>
			<?Php
		} else {
			
			$tokenAmount = bcmul($_POST['token_amount'], bcpow("10", $_POST['token_decimals'], 0), 0);
			$function = "transfer";
			
			//get owner address from private key
			$privKeyFactory = new PrivateKeyFactory();
			$privateKey = $privKeyFactory->fromHexUncompressed($_POST['privkey']);
			$publicKey  = $privateKey->getPublicKey();
			$publicKeyHex = substr($publicKey->getHex(), 2);
			
			$ownerAddressHex = Keccak::hash(hex2bin($publicKeyHex), 256);
			$ownerAddressHex = "41" . substr($ownerAddressHex, -40);
			
			$abi = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"},{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]';
			$abiAry = json_decode($abi, true);
			
			$tx = $tron->getTransactionBuilder()->triggerSmartContract(
				$abiAry, 
				base58check2HexString($_POST['contract_addr']), 
				$function, 
				[base58check2HexString($_POST['recipient']),$tokenAmount], 
				$feeLimitInSun,
				$ownerAddressHex, 
				0, 
				0
			);
			
			$tron->setPrivateKey($_POST['privkey']);
			
			$mutatedTx = $tron->signTransaction($tx);
			
			$newTx = new \Protocol\Transaction();
			$parsedRaw =  new \Protocol\Transaction\Raw();
			$parsedRaw->mergeFromString(hex2str($rawData = $mutatedTx['raw_data_hex']));

			$newTx->setRawData($parsedRaw);
			$signature = Support\Secp::sign($mutatedTx['txID'], $_POST['privkey']);
			$newTx->setSignature([hex2str( $signature )]);

			?>
			<div class="alert alert-success">
		   
				<h6 class="mt-3">Function Return Result</h6>
				<textarea class="form-control" rows="10" id="comment" readonly><?Php print_r($mutatedTx)?></textarea>
				
				<h6 class="mt-3">Raw Tx (Hex)</h6>
				<textarea class="form-control" rows="5" id="comment" readonly><?php echo str2hex($newTx->serializeToString());?></textarea>
				
				<h6 class="mt-3">Raw Data (Hex)</h6>
				<textarea class="form-control" rows="5" id="comment" readonly><?php echo $rawData;?></textarea>
				<small>To sign manually, you may access to <a href="tron_sign_raw_data.php">Tron Sign Raw Data</a> page.</small>

				<h6 class="mt-3">TX Hash</h6>
				<input class="form-control" readonly value="<?php echo $mutatedTx['txID']?>"/>
			</div>
			<?php
		}
	
    } catch (Exception $e) {
        $errmsg .= "Problem found. " . $e->getMessage();

    }
} 

if ($errmsg) {
?>
    <div class="alert alert-danger">
        <strong>Error!</strong> <?php echo $errmsg?>
    </div>
<?php
}
?>
<form action='' method='post'>

	<div class="form-group">
		<label for="chain">Chain:</label>
		<select id="chain" name="chain" class="form-control" >
			<?php
			foreach($supportChains as $k=>$v) {
				echo "<option value='{$k}'".($k == $_POST['chain'] ? " selected": "").">{$v}</option>";
			}
			?>
		</select>
	</div>
	
	<div class="form-group">
		<label for="fee_limit">Fee Limit:</label>
		
		<div class="input-group mb-3">
			<input class="form-control" type='text' name='fee_limit' id='fee_limit' value='<?php echo $_POST['fee_limit']?>'>
			<div class="input-group-append">
			  <span class="input-group-text">TRX</span>
			</div>
		</div>
	</div>
	
	
    <div class="form-group">
        <label for="contract_addr">To:</label>
        <input placeholder="Token's Contract Address" class="form-control" type='text' name='contract_addr' id='contract_addr' value='<?php echo $_POST['contract_addr']?>'>
    </div>
	
	<div class="form-group">
        <label for="token_amount">Send Token Amount:</label>
        <input class="form-control" type='text' name='token_amount' id='token_amount' value='<?php echo $_POST['token_amount']?>'>
    </div>
	
	
	<div class="form-group">
        <label for="token_decimals">Token Decimal Places:</label>
        <input class="form-control" type='text' name='token_decimals' id='token_decimals' value='<?php echo $_POST['token_decimals']?>'>
    </div>
	
	<div class="form-group">
        <label for="recipient">Recipient:</label>
        <input class="form-control" type='text' name='recipient' id='recipient' value='<?php echo $_POST['recipient']?>'>
    </div>
   
     <div class="form-group">
        <label for="privkey">From:</label>
        <input placeholder="Sender's Private Key (Hex)" class="form-control" type='text' name='privkey' id='privkey' value='<?php echo $_POST['privkey']?>'>
    </div>
   
    <input type='submit' class="btn btn-success" name="generate_way" value="Generate Offline"/>
	
	<input type='submit' class="btn btn-success" name="generate_way" value="Generate By Trongrid"/>
</form>
<?php
include_once("html_iframe_footer.php");








Tutorials
About Us
Contents have been open source in GITHUB. Please give me a ⭐ if you found this helpful :)
Community
Problem? Raise me a new issue.
Support Us
Buy me a coffee. so i can spend more nights for this :)

BTCSCHOOLS would like to present you with more pratical but little theory throughout our tutorials. Pages' content are constantly keep reviewed to avoid mistakes, but we cannot warrant correctness of all contents. While using this site, you agree to accept our terms of use, cookie & privacy policy. Copyright 2019 by BTCSCHOOLS. All Rights Reserved.