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.
<?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");