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); $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">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;?>"></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; } }