前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >发送TRC20-Token交易

发送TRC20-Token交易

作者头像
零云
发布2023-07-24 21:32:08
7521
发布2023-07-24 21:32:08
举报
文章被收录于专栏:用户1998800的专栏

简介波场网络跟以太坊很像,特别是接口设计,token的发送目标首先是合约地址而不是接收token的钱包地址,其次参数里加上接收者钱包地址和数量。

代码语言:javascript
复制
<?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;
}

代码语言:javascript
复制
<?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");
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-01-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档