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