Generate serialized raw transaction that ready broadcast to TRON network.
<?php
use IEXBase\TronAPI\Tron;
use IEXBase\TronAPI\Support;
use Protocol\Transaction\Contract\ContractType;
define("TRX_TO_SUN",'1000000');
define("SUN_TO_TRX", '0.000001');
include_once "../libraries/vendor/autoload.php";
include_once("html_iframe_header.php");
include_once("tron_utils.php");
$supportChains = ['main'=>"Tron Mainnet", 'shasta'=>"Shasta Testnet"];
//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;
}
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
try {
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');
}
//get current block
$tron = new \IEXBase\TronAPI\Tron($fullNode, $solidityNode, $eventServer);
$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
$contract = new \Protocol\Transaction\Contract();
$contract->mergeFromString(hex2str($_POST['contract_hex']));
if (in_array($contract->getType(), [ContractType::TriggerSmartContract,ContractType::CreateSmartContract])) {
if (!is_numeric($_POST['feelimit'])) {
throw new Exception("Fee limit is required");
} else if (bccomp($_POST['feelimit'], "1000", 6) == 1) {
throw new Exception("Fee limit should not exceed 1000 TRX");
}
} else {
if (strlen($_POST['feelimit'])) {
throw new Exception("Fee limit is only applicable to TriggerSmartContract or CreateSmartContract contract type.");
}
}
$feeLimitInSun = bcmul($_POST['feelimit'], TRX_TO_SUN);
$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);
/*
If you reference a block that is generated before recent 65535 block, the transaction will be failed. I will suggest to use the latest solidified block as the reference block to ensure the transaction cannot failed because of TAPOS error.
*/
$raw->setRefBlockBytes( hex2Str( $refBlockBytes = substr($blockHeightIn64bits, 12, 4) ));
$raw->setRefBlockHash( hex2Str( $refBlockHash = substr($blockHash, 16, 16) ));
$raw->setTimestamp($currentTimeMillis);
$raw->setExpiration( $blockTs + ((int)$_POST['expiration'] * 1000) );
$txId = hash("sha256", $raw->serializeToString());
$tx = new \Protocol\Transaction();
$tx->setRawData($raw);
$unsigned_tx_bytes = strlen($tx->serializeToString());
$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">Unsigned Tx Byte Size</h6>
<input class="form-control" rows="5" id="comment" readonly value="<?php echo $unsigned_tx_bytes;?>"></textarea>
<h6 class="mt-3">Signed Tx Byte Size</h6>
<input class="form-control" rows="5" id="comment" readonly value="<?php echo $tx->byteSize();?>"></textarea>
<h6 class="mt-3">Consume Bandwidth</h6>
<input class="form-control" rows="5" id="comment" readonly value="<?php echo $tx->byteSize() + 64/*MAX_RESULT_SIZE*/;?>"></textarea>
<h6 class="mt-3">Tx Id</h6>
<input class="form-control" rows="5" id="comment" readonly value="<?php echo $txId;?>"></textarea>
</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="contract_hex">Contract Serialized Hex:</label>
<input class="form-control" type='text' name='contract_hex' id='contract_hex' value='<?php echo $_POST['contract_hex']?>'>
</div>
<div class="form-group">
<label for="feelimit">Fee Limit (Maximum TRX consumption):</label>
<div class="input-group mb-3">
<input class="form-control" type='text' name='feelimit' id='feelimit' value='<?php echo $_POST['feelimit']?>'>
<div class="input-group-append">
<span class="input-group-text">TRX</span>
</div>
</div>
<small>This applicable to smart contract deployment (CreateSmartContract) or execution (TriggerSmartContract) only, put blank if you are not sure.</small>
</div>
<div class="form-group">
<label for="expiration">Expiration Time:</label>
<div class="input-group mb-3">
<input class="form-control" type='text' name='expiration' id='expiration' value='<?php echo $_POST['expiration']?>'>
<div class="input-group-append">
<span class="input-group-text">Seconds</span>
</div>
</div>
<small>
Relative time from last confirmed block.
</small>
</div>
<div class="form-group">
<label for="privkey">Private Key:</label>
<input class="form-control" type='text' name='privkey' id='privkey' value='<?php echo $_POST['privkey']?>'>
</div>
<input type='submit' class="btn btn-success btn-block"/>
</form>
<?php
include_once("html_iframe_footer.php");
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: core/Tron.proto
namespace Protocol\Transaction;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>protocol.Transaction.raw</code>
*/
class raw extends \Google\Protobuf\Internal\Message
{
/**
* Generated from protobuf field <code>bytes ref_block_bytes = 1;</code>
*/
protected $ref_block_bytes = '';
/**
* Generated from protobuf field <code>int64 ref_block_num = 3;</code>
*/
protected $ref_block_num = 0;
/**
* Generated from protobuf field <code>bytes ref_block_hash = 4;</code>
*/
protected $ref_block_hash = '';
/**
* Generated from protobuf field <code>int64 expiration = 8;</code>
*/
protected $expiration = 0;
/**
* Generated from protobuf field <code>repeated .protocol.authority auths = 9;</code>
*/
private $auths;
/**
* transaction note
*
* Generated from protobuf field <code>bytes data = 10;</code>
*/
protected $data = '';
/**
*only support size = 1, repeated list here for extension
*
* Generated from protobuf field <code>repeated .protocol.Transaction.Contract contract = 11;</code>
*/
private $contract;
/**
* scripts not used
*
* Generated from protobuf field <code>bytes scripts = 12;</code>
*/
protected $scripts = '';
/**
* Generated from protobuf field <code>int64 timestamp = 14;</code>
*/
protected $timestamp = 0;
/**
* Generated from protobuf field <code>int64 fee_limit = 18;</code>
*/
protected $fee_limit = 0;
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* @type string $ref_block_bytes
* @type int|string $ref_block_num
* @type string $ref_block_hash
* @type int|string $expiration
* @type \Protocol\authority[]|\Google\Protobuf\Internal\RepeatedField $auths
* @type string $data
* transaction note
* @type \Protocol\Transaction\Contract[]|\Google\Protobuf\Internal\RepeatedField $contract
* only support size = 1, repeated list here for extension
* @type string $scripts
* scripts not used
* @type int|string $timestamp
* @type int|string $fee_limit
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\Core\Tron::initOnce();
parent::__construct($data);
}
/**
* Generated from protobuf field <code>bytes ref_block_bytes = 1;</code>
* @return string
*/
public function getRefBlockBytes()
{
return $this->ref_block_bytes;
}
/**
* Generated from protobuf field <code>bytes ref_block_bytes = 1;</code>
* @param string $var
* @return $this
*/
public function setRefBlockBytes($var)
{
GPBUtil::checkString($var, False);
$this->ref_block_bytes = $var;
return $this;
}
/**
* Generated from protobuf field <code>int64 ref_block_num = 3;</code>
* @return int|string
*/
public function getRefBlockNum()
{
return $this->ref_block_num;
}
/**
* Generated from protobuf field <code>int64 ref_block_num = 3;</code>
* @param int|string $var
* @return $this
*/
public function setRefBlockNum($var)
{
GPBUtil::checkInt64($var);
$this->ref_block_num = $var;
return $this;
}
/**
* Generated from protobuf field <code>bytes ref_block_hash = 4;</code>
* @return string
*/
public function getRefBlockHash()
{
return $this->ref_block_hash;
}
/**
* Generated from protobuf field <code>bytes ref_block_hash = 4;</code>
* @param string $var
* @return $this
*/
public function setRefBlockHash($var)
{
GPBUtil::checkString($var, False);
$this->ref_block_hash = $var;
return $this;
}
/**
* Generated from protobuf field <code>int64 expiration = 8;</code>
* @return int|string
*/
public function getExpiration()
{
return $this->expiration;
}
/**
* Generated from protobuf field <code>int64 expiration = 8;</code>
* @param int|string $var
* @return $this
*/
public function setExpiration($var)
{
GPBUtil::checkInt64($var);
$this->expiration = $var;
return $this;
}
/**
* Generated from protobuf field <code>repeated .protocol.authority auths = 9;</code>
* @return \Google\Protobuf\Internal\RepeatedField
*/
public function getAuths()
{
return $this->auths;
}
/**
* Generated from protobuf field <code>repeated .protocol.authority auths = 9;</code>
* @param \Protocol\authority[]|\Google\Protobuf\Internal\RepeatedField $var
* @return $this
*/
public function setAuths($var)
{
$arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Protocol\authority::class);
$this->auths = $arr;
return $this;
}
/**
* transaction note
*
* Generated from protobuf field <code>bytes data = 10;</code>
* @return string
*/
public function getData()
{
return $this->data;
}
/**
* transaction note
*
* Generated from protobuf field <code>bytes data = 10;</code>
* @param string $var
* @return $this
*/
public function setData($var)
{
GPBUtil::checkString($var, False);
$this->data = $var;
return $this;
}
/**
*only support size = 1, repeated list here for extension
*
* Generated from protobuf field <code>repeated .protocol.Transaction.Contract contract = 11;</code>
* @return \Google\Protobuf\Internal\RepeatedField
*/
public function getContract()
{
return $this->contract;
}
/**
*only support size = 1, repeated list here for extension
*
* Generated from protobuf field <code>repeated .protocol.Transaction.Contract contract = 11;</code>
* @param \Protocol\Transaction\Contract[]|\Google\Protobuf\Internal\RepeatedField $var
* @return $this
*/
public function setContract($var)
{
$arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Protocol\Transaction\Contract::class);
$this->contract = $arr;
return $this;
}
/**
* scripts not used
*
* Generated from protobuf field <code>bytes scripts = 12;</code>
* @return string
*/
public function getScripts()
{
return $this->scripts;
}
/**
* scripts not used
*
* Generated from protobuf field <code>bytes scripts = 12;</code>
* @param string $var
* @return $this
*/
public function setScripts($var)
{
GPBUtil::checkString($var, False);
$this->scripts = $var;
return $this;
}
/**
* Generated from protobuf field <code>int64 timestamp = 14;</code>
* @return int|string
*/
public function getTimestamp()
{
return $this->timestamp;
}
/**
* Generated from protobuf field <code>int64 timestamp = 14;</code>
* @param int|string $var
* @return $this
*/
public function setTimestamp($var)
{
GPBUtil::checkInt64($var);
$this->timestamp = $var;
return $this;
}
/**
* Generated from protobuf field <code>int64 fee_limit = 18;</code>
* @return int|string
*/
public function getFeeLimit()
{
return $this->fee_limit;
}
/**
* Generated from protobuf field <code>int64 fee_limit = 18;</code>
* @param int|string $var
* @return $this
*/
public function setFeeLimit($var)
{
GPBUtil::checkInt64($var);
$this->fee_limit = $var;
return $this;
}
}
// Adding a class alias for backwards compatibility with the previous class name.
class_alias(raw::class, \Protocol\Transaction_raw::class);
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: core/Tron.proto
namespace Protocol;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>protocol.Transaction</code>
*/
class Transaction extends \Google\Protobuf\Internal\Message
{
/**
* Generated from protobuf field <code>.protocol.Transaction.raw raw_data = 1;</code>
*/
protected $raw_data = null;
/**
* only support size = 1, repeated list here for muti-sig extension
*
* Generated from protobuf field <code>repeated bytes signature = 2;</code>
*/
private $signature;
/**
* Generated from protobuf field <code>repeated .protocol.Transaction.Result ret = 5;</code>
*/
private $ret;
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* @type \Protocol\Transaction\raw $raw_data
* @type string[]|\Google\Protobuf\Internal\RepeatedField $signature
* only support size = 1, repeated list here for muti-sig extension
* @type \Protocol\Transaction\Result[]|\Google\Protobuf\Internal\RepeatedField $ret
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\Core\Tron::initOnce();
parent::__construct($data);
}
/**
* Generated from protobuf field <code>.protocol.Transaction.raw raw_data = 1;</code>
* @return \Protocol\Transaction\raw
*/
public function getRawData()
{
return isset($this->raw_data) ? $this->raw_data : null;
}
public function hasRawData()
{
return isset($this->raw_data);
}
public function clearRawData()
{
unset($this->raw_data);
}
/**
* Generated from protobuf field <code>.protocol.Transaction.raw raw_data = 1;</code>
* @param \Protocol\Transaction\raw $var
* @return $this
*/
public function setRawData($var)
{
GPBUtil::checkMessage($var, \Protocol\Transaction\raw::class);
$this->raw_data = $var;
return $this;
}
/**
* only support size = 1, repeated list here for muti-sig extension
*
* Generated from protobuf field <code>repeated bytes signature = 2;</code>
* @return \Google\Protobuf\Internal\RepeatedField
*/
public function getSignature()
{
return $this->signature;
}
/**
* only support size = 1, repeated list here for muti-sig extension
*
* Generated from protobuf field <code>repeated bytes signature = 2;</code>
* @param string[]|\Google\Protobuf\Internal\RepeatedField $var
* @return $this
*/
public function setSignature($var)
{
$arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::BYTES);
$this->signature = $arr;
return $this;
}
/**
* Generated from protobuf field <code>repeated .protocol.Transaction.Result ret = 5;</code>
* @return \Google\Protobuf\Internal\RepeatedField
*/
public function getRet()
{
return $this->ret;
}
/**
* Generated from protobuf field <code>repeated .protocol.Transaction.Result ret = 5;</code>
* @param \Protocol\Transaction\Result[]|\Google\Protobuf\Internal\RepeatedField $var
* @return $this
*/
public function setRet($var)
{
$arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Protocol\Transaction\Result::class);
$this->ret = $arr;
return $this;
}
}