简介波场网络跟以太坊很像,特别是接口设计,token的发送目标首先是合约地址而不是接收token的钱包地址,其次参数里加上接收者钱包地址和数量。
<?php
function getChainParamValue($chainParams, $key) {
if (is_array($chainParams)) {
foreach($chainParams as $chainParam) {
if ($chainParam['key'] == $key) {
return $chainParam['value'];
}
}
}
return false;
}
function base58_encode($string)
{
$alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base = strlen($alphabet);
if (is_string($string) === false) {
return false;
}
if (strlen($string) === 0) {
return '';
}
$bytes = array_values(unpack('C*', $string));
$decimal = $bytes[0];
for ($i = 1, $l = count($bytes); $i < $l; $i++) {
$decimal = bcmul($decimal, 256);
$decimal = bcadd($decimal, $bytes[$i]);
}
$output = '';
while ($decimal >= $base) {
$div = bcdiv($decimal, $base, 0);
$mod = bcmod($decimal, $base);
$output .= $alphabet[$mod];
$decimal = $div;
}
if ($decimal > 0) {
$output .= $alphabet[$decimal];
}
$output = strrev($output);
foreach ($bytes as $byte) {
if ($byte === 0) {
$output = $alphabet[0] . $output;
continue;
}
break;
}
return (string) $output;
}
function base58_decode($base58)
{
$alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base = strlen($alphabet);
if (is_string($base58) === false) {
return false;
}
if (strlen($base58) === 0) {
return '';
}
$indexes = array_flip(str_split($alphabet));
$chars = str_split($base58);
foreach ($chars as $char) {
if (isset($indexes[$char]) === false) {
return false;
}
}
$decimal = $indexes[$chars[0]];
for ($i = 1, $l = count($chars); $i < $l; $i++) {
$decimal = bcmul($decimal, $base);
$decimal = bcadd($decimal, $indexes[$chars[$i]]);
}
$output = '';
while ($decimal > 0) {
$byte = bcmod($decimal, 256);
$output = pack('C', $byte) . $output;
$decimal = bcdiv($decimal, 256, 0);
}
foreach ($chars as $char) {
if ($indexes[$char] === 0) {
$output = "\x00" . $output;
continue;
}
break;
}
return $output;
}
//encode address from byte[] to base58check string
function base58check_en($address)
{
$hash0 = hash("sha256", $address);
$hash1 = hash("sha256", hex2bin($hash0));
$checksum = substr($hash1, 0, 8);
$address = $address.hex2bin($checksum);
$base58add = base58_encode($address);
return $base58add;
}
//decode address from base58check string to byte[]
function base58check_de($base58add)
{
$address = base58_decode($base58add);
$size = strlen($address);
if ($size != 25) {
return false;
}
$checksum = substr($address, 21);
$address = substr($address, 0, 21);
$hash0 = hash("sha256", $address);
$hash1 = hash("sha256", hex2bin($hash0));
$checksum0 = substr($hash1, 0, 8);
$checksum1 = bin2hex($checksum);
if (strcmp($checksum0, $checksum1)) {
return false;
}
return $address;
}
function hexString2Base58check($hexString){
$address = hex2bin($hexString);
$base58add = base58check_en($address);
return $base58add;
}
function base58check2HexString($base58add){
$address = base58check_de($base58add);
$hexString = bin2hex($address);
return $hexString;
}
function hexString2Base64($hexString){
$address = hex2bin($hexString);
$base64 = base64_encode($address);
return $base64;
}
function base642HexString($base64){
$address = base64_decode($base64);
$hexString = bin2hex($address);
return $hexString;
}
function base58check2Base64($base58add){
$address = base58check_de($base58add);
$base64 = base64_encode($address);
return $base64;
}
function base642Base58check($base64){
$address = base64_decode($base64);
$base58add = base58check_en($address);
return $base58add;
}
function hex2Dec(string $hex): string
{
$dec = 0;
$len = strlen($hex);
for ($i = 1; $i <= $len; $i++) {
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
}
return $dec;
}
function dec2Hex($dec): string
{
$last = bcmod($dec, 16);
$remain = bcdiv(bcsub($dec, $last), 16);
if ($remain == 0) {
return dechex($last);
} else {
return dec2Hex($remain) . dechex($last);
}
}
function hex2Str(string $hex): string
{
$str = "";
for ($i = 0; $i < strlen($hex) - 1; $i += 2) {
$str .= chr(hexdec($hex[$i] . $hex[$i + 1]));
}
return $str;
}
function str2Hex(string $str): string
{
$hex = "";
for ($i = 0; $i < strlen($str); $i++) {
$hex .= str_pad( dechex(ord($str[$i])) , 2 ,"0", STR_PAD_LEFT);
}
return $hex;
}
<?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"];
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 {
$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');
}
$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());
$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">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($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">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");