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