wallet tron transactions
This commit is contained in:
33
sdk/Wallet/Http.php
Normal file
33
sdk/Wallet/Http.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace Sdk\Wallet;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class Http
|
||||
{
|
||||
protected Client $httpClient;
|
||||
|
||||
function __construct($client)
|
||||
{
|
||||
$this->httpClient = $client;
|
||||
}
|
||||
|
||||
function getClient()
|
||||
{
|
||||
return $this->httpClient;
|
||||
}
|
||||
|
||||
function get(string $endpoint, array $data = [])
|
||||
{
|
||||
$stream = (string)$this->getClient()->get($endpoint, ['query' => $data])->getBody();
|
||||
$body = json_decode($stream, false);
|
||||
return $body;
|
||||
}
|
||||
|
||||
function post(string $endpoint, array $data = [])
|
||||
{
|
||||
$stream = (string)$this->getClient()->post($endpoint, ['json' => $data])->getBody();
|
||||
$body = json_decode($stream, false);
|
||||
return $body;
|
||||
}
|
||||
|
||||
}
|
||||
72
sdk/Wallet/TokenAddress.php
Normal file
72
sdk/Wallet/TokenAddress.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Sdk\Wallet;
|
||||
|
||||
use IEXBase\TronAPI\Support\Base58Check;
|
||||
use IEXBase\TronAPI\Support\Hash;
|
||||
use IEXBase\TronAPI\TronAwareTrait;
|
||||
|
||||
class TokenAddress
|
||||
{
|
||||
use TronAwareTrait;
|
||||
|
||||
public $privateKey = '';
|
||||
public $address = '';
|
||||
public $hexAddress = '';
|
||||
|
||||
const ADDRESS_SIZE = 34;
|
||||
const ADDRESS_PREFIX = "41";
|
||||
const ADDRESS_PREFIX_BYTE = 0x41;
|
||||
|
||||
public function __construct(string $address = '', string $privateKey = '', string $hexAddress = '')
|
||||
{
|
||||
if (strlen($address) === 0) {
|
||||
throw new \InvalidArgumentException('Address can not be empty');
|
||||
}
|
||||
|
||||
if (!empty($address) && empty($hexAddress)) $hexAddress = $this->address2HexString($address);
|
||||
if (!empty($hexAddress) && empty($address)) $address = $this->hexString2Address($hexAddress);
|
||||
|
||||
$this->privateKey = $privateKey;
|
||||
$this->address = $address;
|
||||
$this->hexAddress = $hexAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dont rely on this. Always use Wallet::validateAddress to double check
|
||||
* against tronGrid.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
if (strlen($this->address) !== self::ADDRESS_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$address = Base58Check::decode($this->address, false, 0, false);
|
||||
$utf8 = hex2bin($address);
|
||||
|
||||
if (strlen($utf8) !== 25) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($utf8, chr(self::ADDRESS_PREFIX_BYTE)) !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$checkSum = substr($utf8, 21);
|
||||
$address = substr($utf8, 0, 21);
|
||||
|
||||
$hash0 = Hash::SHA256($address);
|
||||
$hash1 = Hash::SHA256($hash0);
|
||||
$checkSum1 = substr($hash1, 0, 4);
|
||||
|
||||
if ($checkSum === $checkSum1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
57
sdk/Wallet/Tools/Formatter.php
Normal file
57
sdk/Wallet/Tools/Formatter.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Sdk\Wallet\Tools;
|
||||
|
||||
/**
|
||||
* 数据签名
|
||||
* Class Formatter
|
||||
* @package Ethereum
|
||||
*/
|
||||
class Formatter
|
||||
{
|
||||
|
||||
/**
|
||||
* 对于方法名和参数类型做签名
|
||||
* @param $method
|
||||
* @return string
|
||||
*/
|
||||
public static function toMethodFormat($method)
|
||||
{
|
||||
return Utils::stripZero(substr(Utils::sha3($method), 0, 10));
|
||||
}
|
||||
|
||||
/**
|
||||
* 地址签名
|
||||
* @param $address
|
||||
* @return string
|
||||
*/
|
||||
public static function toAddressFormat($address)
|
||||
{
|
||||
if (Utils::isAddress($address)) {
|
||||
$address = strtolower($address);
|
||||
|
||||
if (Utils::isZeroPrefixed($address)) {
|
||||
$address = Utils::stripZero($address);
|
||||
}
|
||||
}
|
||||
return implode('', array_fill(0, 64 - strlen($address), 0)) . $address;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字签名
|
||||
* @param $value
|
||||
* @param int $digit
|
||||
* @return string
|
||||
*/
|
||||
public static function toIntegerFormat($value, $digit = 64)
|
||||
{
|
||||
$bn = Utils::toBn($value);
|
||||
$bnHex = $bn->toHex(true);
|
||||
$padded = mb_substr($bnHex, 0, 1);
|
||||
|
||||
if ($padded !== 'f') {
|
||||
$padded = '0';
|
||||
}
|
||||
return implode('', array_fill(0, $digit - mb_strlen($bnHex), $padded)) . $bnHex;
|
||||
}
|
||||
}
|
||||
82
sdk/Wallet/Tools/Key.php
Normal file
82
sdk/Wallet/Tools/Key.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Sdk\Wallet\Tools;
|
||||
|
||||
use Elliptic\EC;
|
||||
use IEXBase\TronAPI\Support\Base58;
|
||||
use IEXBase\TronAPI\Support\Crypto;
|
||||
use IEXBase\TronAPI\Support\Hash;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Key
|
||||
{
|
||||
/**
|
||||
* Generate the Address of the provided Public key
|
||||
*
|
||||
* @param string $publicKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function publicKeyToAddress(string $publicKey)
|
||||
{
|
||||
if (Utils::isHex($publicKey) === false) {
|
||||
throw new InvalidArgumentException('Invalid public key format.');
|
||||
}
|
||||
$publicKey = Utils::stripZero($publicKey);
|
||||
if (strlen($publicKey) !== 130) {
|
||||
throw new InvalidArgumentException('Invalid public key length.');
|
||||
}
|
||||
return substr(Utils::sha3(substr(hex2bin($publicKey), 1)), 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Address of the provided Private key
|
||||
*
|
||||
* @param string $privateKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function privateKeyToAddress(string $privateKey)
|
||||
{
|
||||
return self::publicKeyToAddress(
|
||||
self::privateKeyToPublicKey($privateKey)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Public key for provided Private key
|
||||
*
|
||||
* @param string $privateKey Private Key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function privateKeyToPublicKey(string $privateKey)
|
||||
{
|
||||
if (Utils::isHex($privateKey) === false) {
|
||||
throw new InvalidArgumentException('Invalid private key format.');
|
||||
}
|
||||
$privateKey = Utils::stripZero($privateKey);
|
||||
|
||||
if (strlen($privateKey) !== 64) {
|
||||
throw new InvalidArgumentException('Invalid private key length.');
|
||||
}
|
||||
|
||||
$secp256k1 = new EC('secp256k1');
|
||||
$privateKey = $secp256k1->keyFromPrivate($privateKey, 'hex');
|
||||
$publicKey = $privateKey->getPublic(false, 'hex');
|
||||
|
||||
return $publicKey;
|
||||
}
|
||||
|
||||
public static function getBase58CheckAddress(string $addressHex): string
|
||||
{
|
||||
$addressBin = hex2bin($addressHex);
|
||||
$hash0 = Hash::SHA256($addressBin);
|
||||
$hash1 = Hash::SHA256($hash0);
|
||||
$checksum = substr($hash1, 0, 4);
|
||||
$checksum = $addressBin . $checksum;
|
||||
|
||||
return Base58::encode(Crypto::bin2bc($checksum));
|
||||
}
|
||||
}
|
||||
9
sdk/Wallet/Tools/Tools.php
Normal file
9
sdk/Wallet/Tools/Tools.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Sdk\Wallet\Tools;
|
||||
|
||||
|
||||
class Tools
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
330
sdk/Wallet/Tools/Utils.php
Normal file
330
sdk/Wallet/Tools/Utils.php
Normal file
@ -0,0 +1,330 @@
|
||||
<?php
|
||||
|
||||
namespace Sdk\Wallet\Tools;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use kornrunner\Keccak;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
class Utils
|
||||
{
|
||||
/**
|
||||
* SHA3_NULL_HASH
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const SHA3_NULL_HASH = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
|
||||
|
||||
/**
|
||||
* NEGATIVE1
|
||||
* Cannot work, see: http://php.net/manual/en/language.constants.syntax.php
|
||||
*
|
||||
* @const
|
||||
*/
|
||||
// const NEGATIVE1 = new BigInteger(-1);
|
||||
|
||||
/**
|
||||
* construct
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
// public function __construct() {}
|
||||
|
||||
/**
|
||||
* toHex
|
||||
* Encoding string or integer or numeric string(is not zero prefixed) or big number to hex.
|
||||
*
|
||||
* @param string|int|BigInteger $value
|
||||
* @param bool $isPrefix
|
||||
* @return string
|
||||
*/
|
||||
public static function toHex($value, $isPrefix = false)
|
||||
{
|
||||
if (is_numeric($value)) {
|
||||
// turn to hex number
|
||||
$bn = self::toBn($value);
|
||||
$hex = $bn->toHex(true);
|
||||
$hex = preg_replace('/^0+(?!$)/', '', $hex);
|
||||
} elseif (is_string($value)) {
|
||||
$value = self::stripZero($value);
|
||||
$hex = implode('', unpack('H*', $value));
|
||||
} elseif ($value instanceof BigInteger) {
|
||||
$hex = $value->toHex(true);
|
||||
$hex = preg_replace('/^0+(?!$)/', '', $hex);
|
||||
} else {
|
||||
throw new InvalidArgumentException('The value to toHex function is not support.');
|
||||
}
|
||||
if ($isPrefix) {
|
||||
return '0x' . $hex;
|
||||
}
|
||||
return $hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* hexToBin
|
||||
*
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
public static function hexToBin($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidArgumentException('The value to hexToBin function must be string.');
|
||||
}
|
||||
if (self::isZeroPrefixed($value)) {
|
||||
$count = 1;
|
||||
$value = str_replace('0x', '', $value, $count);
|
||||
}
|
||||
return pack('H*', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* isZeroPrefixed
|
||||
*
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public static function isZeroPrefixed($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidArgumentException('The value to isZeroPrefixed function must be string.');
|
||||
}
|
||||
return (strpos($value, '0x') === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* stripZero
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function stripZero($value)
|
||||
{
|
||||
if (self::isZeroPrefixed($value)) {
|
||||
$count = 1;
|
||||
return str_replace('0x', '', $value, $count);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* isNegative
|
||||
*
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public static function isNegative($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidArgumentException('The value to isNegative function must be string.');
|
||||
}
|
||||
return (strpos($value, '-') === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* isAddress
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAddress($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidArgumentException('The value to isAddress function must be string.');
|
||||
}
|
||||
if (preg_match('/^(0x|0X)?[a-f0-9A-F]{40}$/', $value) !== 1) {
|
||||
return false;
|
||||
} elseif (preg_match('/^(0x|0X)?[a-f0-9]{40}$/', $value) === 1 || preg_match('/^(0x|0X)?[A-F0-9]{40}$/', $value) === 1) {
|
||||
return true;
|
||||
}
|
||||
return self::isAddressChecksum($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* isAddressChecksum
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAddressChecksum($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidArgumentException('The value to isAddressChecksum function must be string.');
|
||||
}
|
||||
$value = self::stripZero($value);
|
||||
$hash = self::stripZero(self::sha3(mb_strtolower($value)));
|
||||
|
||||
for ($i = 0; $i < 40; $i++) {
|
||||
if (
|
||||
(intval($hash[$i], 16) > 7 && mb_strtoupper($value[$i]) !== $value[$i]) ||
|
||||
(intval($hash[$i], 16) <= 7 && mb_strtolower($value[$i]) !== $value[$i])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* isHex
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function isHex($value)
|
||||
{
|
||||
return (is_string($value) && preg_match('/^(0x)?[a-f0-9A-F]*$/', $value) === 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* sha3
|
||||
* keccak256
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function sha3($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidArgumentException('The value to sha3 function must be string.');
|
||||
}
|
||||
if (strpos($value, '0x') === 0) {
|
||||
$value = self::hexToBin($value);
|
||||
}
|
||||
$hash = Keccak::hash($value, 256);
|
||||
|
||||
if ($hash === self::SHA3_NULL_HASH) {
|
||||
return null;
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* toBn
|
||||
* Change number or number string to BigInteger.
|
||||
*
|
||||
* @param BigInteger|string|int $number
|
||||
* @return array|BigInteger
|
||||
*/
|
||||
public static function toBn($number)
|
||||
{
|
||||
if ($number instanceof BigInteger) {
|
||||
$bn = $number;
|
||||
} elseif (is_int($number)) {
|
||||
$bn = new BigInteger($number);
|
||||
} elseif (is_numeric($number)) {
|
||||
$number = (string) $number;
|
||||
|
||||
if (self::isNegative($number)) {
|
||||
$count = 1;
|
||||
$number = str_replace('-', '', $number, $count);
|
||||
$negative1 = new BigInteger(-1);
|
||||
}
|
||||
if (strpos($number, '.') > 0) {
|
||||
$comps = explode('.', $number);
|
||||
|
||||
if (count($comps) > 2) {
|
||||
throw new InvalidArgumentException('toBn number must be a valid number.');
|
||||
}
|
||||
$whole = $comps[0];
|
||||
$fraction = $comps[1];
|
||||
|
||||
return [
|
||||
new BigInteger($whole),
|
||||
new BigInteger($fraction),
|
||||
strlen($comps[1]),
|
||||
isset($negative1) ? $negative1 : false
|
||||
];
|
||||
} else {
|
||||
$bn = new BigInteger($number);
|
||||
}
|
||||
if (isset($negative1)) {
|
||||
$bn = $bn->multiply($negative1);
|
||||
}
|
||||
} elseif (is_string($number)) {
|
||||
$number = mb_strtolower($number);
|
||||
|
||||
if (self::isNegative($number)) {
|
||||
$count = 1;
|
||||
$number = str_replace('-', '', $number, $count);
|
||||
$negative1 = new BigInteger(-1);
|
||||
}
|
||||
if (self::isZeroPrefixed($number) || preg_match('/[a-f]+/', $number) === 1) {
|
||||
$number = self::stripZero($number);
|
||||
$bn = new BigInteger($number, 16);
|
||||
} elseif (empty($number)) {
|
||||
$bn = new BigInteger(0);
|
||||
} else {
|
||||
throw new InvalidArgumentException('toBn number must be valid hex string.');
|
||||
}
|
||||
if (isset($negative1)) {
|
||||
$bn = $bn->multiply($negative1);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidArgumentException('toBn number must be BigInteger, string or int.');
|
||||
}
|
||||
return $bn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据精度展示资产
|
||||
* @param $number
|
||||
* @param int $decimals
|
||||
* @return string
|
||||
*/
|
||||
public static function toDisplayAmount($number, int $decimals)
|
||||
{
|
||||
$number = number_format($number,0,'.','');//格式化
|
||||
$bn = self::toBn($number);
|
||||
$bnt = self::toBn(pow(10, $decimals));
|
||||
|
||||
return self::divideDisplay($bn->divide($bnt), $decimals);
|
||||
}
|
||||
|
||||
public static function divideDisplay(array $divResult, int $decimals)
|
||||
{
|
||||
list($bnq, $bnr) = $divResult;
|
||||
$ret = "$bnq->value";
|
||||
if ($bnr->value > 0) {
|
||||
$ret .= '.' . rtrim(sprintf("%0{$decimals}d", $bnr->value), '0');
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function toMinUnitByDecimals($number, int $decimals)
|
||||
{
|
||||
$bn = self::toBn($number);
|
||||
$bnt = self::toBn(pow(10, $decimals));
|
||||
|
||||
if (is_array($bn)) {
|
||||
// fraction number
|
||||
list($whole, $fraction, $fractionLength, $negative1) = $bn;
|
||||
|
||||
$whole = $whole->multiply($bnt);
|
||||
|
||||
switch (MATH_BIGINTEGER_MODE) {
|
||||
case $whole::MODE_GMP:
|
||||
static $two;
|
||||
$powerBase = gmp_pow(gmp_init(10), (int) $fractionLength);
|
||||
break;
|
||||
case $whole::MODE_BCMATH:
|
||||
$powerBase = bcpow('10', (string) $fractionLength, 0);
|
||||
break;
|
||||
default:
|
||||
$powerBase = pow(10, (int) $fractionLength);
|
||||
break;
|
||||
}
|
||||
$base = new BigInteger($powerBase);
|
||||
$fraction = $fraction->multiply($bnt)->divide($base)[0];
|
||||
|
||||
if ($negative1 !== false) {
|
||||
return $whole->add($fraction)->multiply($negative1);
|
||||
}
|
||||
return $whole->add($fraction);
|
||||
}
|
||||
|
||||
return $bn->multiply($bnt);
|
||||
}
|
||||
}
|
||||
252
sdk/Wallet/Trc20Sdk.php
Normal file
252
sdk/Wallet/Trc20Sdk.php
Normal file
@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace Sdk\Wallet;
|
||||
|
||||
use App\Bean\Sdk\Wallet\Tron\NowBlockBean;
|
||||
use App\Bean\Sdk\Wallet\Tron\ResponseBean;
|
||||
use App\Bean\Sdk\Wallet\Tron\TokenAddressBean;
|
||||
use App\Bean\Sdk\Wallet\Tron\TransferTransactionBean;
|
||||
use App\Exceptions\WalletSdkException;
|
||||
use App\Tools\Math;
|
||||
use GuzzleHttp\Client;
|
||||
use IEXBase\TronAPI\Exception\TronException;
|
||||
use IEXBase\TronAPI\Provider\HttpProvider;
|
||||
use IEXBase\TronAPI\Tron;
|
||||
use Sdk\Wallet\Tools\Formatter;
|
||||
use Sdk\Wallet\Tools\Utils;
|
||||
|
||||
class Trc20Sdk
|
||||
{
|
||||
protected $tron;
|
||||
protected $tronContract;
|
||||
protected $config = [];
|
||||
protected TokenAddress $contractAddress;
|
||||
protected Http $http;
|
||||
const DECIMALS = 6;
|
||||
|
||||
function __construct(Client $_api, string $contractAddress)
|
||||
{
|
||||
$this->http = new Http($_api);
|
||||
$this->contractAddress = new TokenAddress($contractAddress);
|
||||
|
||||
$host = $this->getHttpClient()->getClient()->getConfig('base_uri')->getScheme() . '://' . $this->getHttpClient()->getClient()->getConfig('base_uri')->getHost();
|
||||
$fullNode = new HttpProvider($host);
|
||||
$solidityNode = new HttpProvider($host);
|
||||
$eventServer = new HttpProvider($host);
|
||||
try {
|
||||
$this->tron = new Tron($fullNode, $solidityNode, $eventServer);
|
||||
// $this->tronContract = new TRC20Contract($this->tron,$contractAddress);
|
||||
} catch (\Exception $e) {
|
||||
throw new WalletSdkException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
function getHttpClient(): Http
|
||||
{
|
||||
return $this->http;
|
||||
}
|
||||
|
||||
//获取当前最新区块事件
|
||||
function getLastBlockEvent(): NowBlockBean|bool
|
||||
{
|
||||
$data = [
|
||||
'limit' => 1,
|
||||
'order_by' => 'block_timestamp,desc',
|
||||
];
|
||||
$res = $this->getHttpClient()->get('/v1/blocks/latest/events', $data);
|
||||
if (!isset($res['data'][0])) return false;
|
||||
$ResponseBean = new ResponseBean($res);
|
||||
$item = $ResponseBean->getData()[0];
|
||||
return new NowBlockBean($item);
|
||||
}
|
||||
|
||||
//获取当前最新区块事件
|
||||
function getNowBlockNumber(): NowBlockBean|bool
|
||||
{
|
||||
return $this->getLastBlockEvent();
|
||||
}
|
||||
|
||||
//根据区块高度获取交易信息
|
||||
public function getTrc20BlockEventsByBlockNumber(string|int $blockNumber, $fingerprint = null, $limit = 200): bool|ResponseBean
|
||||
{
|
||||
$path = '/v1/contracts/' . $this->contractAddress->address . '/events';
|
||||
$data = [
|
||||
'block_number' => $blockNumber,
|
||||
'limit' => $limit,
|
||||
'only_confirmed' => true,
|
||||
];
|
||||
if (!empty($fingerprint)) $data['fingerprint'] = $fingerprint;
|
||||
$res = $this->getHttpClient()->get($path, $data);
|
||||
if (!isset($res['success'])) return false;
|
||||
if ($res['success'] !== true) return false;
|
||||
return new ResponseBean($res);
|
||||
}
|
||||
|
||||
//根据区块高度获取交易信息
|
||||
public function getTrc20BlockEventsTransferByBlockNumber(string|int $blockNumber, $fingerprint = null, $limit = 200): bool|ResponseBean
|
||||
{
|
||||
$path = '/v1/contracts/' . $this->contractAddress->address . '/events';
|
||||
$data = [
|
||||
'event_name' => 'Transfer',
|
||||
'block_number' => $blockNumber,
|
||||
'limit' => $limit,
|
||||
'only_confirmed' => true,
|
||||
'order_by' => 'block_timestamp,asc',
|
||||
];
|
||||
if (!empty($fingerprint)) $data['fingerprint'] = $fingerprint;
|
||||
$res = $this->getHttpClient()->get($path, $data);
|
||||
if (!isset($res['success'])) return false;
|
||||
if ($res['success'] !== true) return false;
|
||||
return new ResponseBean($res);
|
||||
}
|
||||
|
||||
//获取账户合约交易记录
|
||||
public function getAccountTrc20Events(string $address, $fingerprint = null, $limit = 200): bool|ResponseBean
|
||||
{
|
||||
$path = '/v1/accounts/' . $address . '//transactions/trc20';
|
||||
$data = [
|
||||
'limit' => $limit,
|
||||
'contract_address' => $this->contractAddress->address,
|
||||
'only_confirmed' => true,
|
||||
'order_by' => 'block_timestamp,desc',
|
||||
];
|
||||
if (!empty($fingerprint)) $data['fingerprint'] = $fingerprint;
|
||||
$res = $this->getHttpClient()->get($path, $data);
|
||||
if (!isset($res['success'])) return false;
|
||||
if ($res['success'] !== true) return false;
|
||||
return new ResponseBean($res);
|
||||
}
|
||||
|
||||
//获取TRC20余额
|
||||
function getTrc20Balance(string $address): float|string
|
||||
{
|
||||
$address = new TokenAddress($address);
|
||||
$format = Formatter::toAddressFormat($address->hexAddress);
|
||||
$res = $this->getHttpClient()->post('/wallet/triggersmartcontract', [
|
||||
'contract_address' => $this->contractAddress->hexAddress,
|
||||
'function_selector' => 'balanceOf(address)',
|
||||
'parameter' => $format,
|
||||
'owner_address' => $address->hexAddress,
|
||||
]);
|
||||
|
||||
if (!isset($res['result']['result'])) {
|
||||
throw new WalletSdkException('getTrc20Balance error');
|
||||
}
|
||||
if ($res['result']['result'] !== true) {
|
||||
throw new WalletSdkException('getTrc20Balance error');
|
||||
}
|
||||
|
||||
try {
|
||||
$hex_value = $res['constant_result'][0];
|
||||
$balance = Math::valueToAmount(hexdec($hex_value), self::DECIMALS);
|
||||
} catch (\Exception $e) {
|
||||
throw new WalletSdkException($e->getMessage());
|
||||
}
|
||||
return $balance;
|
||||
}
|
||||
|
||||
//获取TRX余额
|
||||
function getTrxBalance(string $address): float
|
||||
{
|
||||
$this->tron->setAddress($address);
|
||||
return $this->tron->getBalance($address, true);
|
||||
}
|
||||
|
||||
//TRC20转账
|
||||
function trc20Transfer(TokenAddress $from, TokenAddress $to, float $amount): TransferTransactionBean|bool
|
||||
{
|
||||
$this->tron->setAddress($from->address);
|
||||
$this->tron->setPrivateKey($from->privateKey);
|
||||
|
||||
$toFormat = Formatter::toAddressFormat($to->hexAddress);
|
||||
try {
|
||||
$amount = Utils::toMinUnitByDecimals($amount, self::DECIMALS);
|
||||
} catch (\Exception $e) {
|
||||
throw new WalletSdkException($e->getMessage());
|
||||
}
|
||||
$numberFormat = Formatter::toIntegerFormat($amount);
|
||||
|
||||
$body = $this->getHttpClient()->post('/wallet/triggersmartcontract', [
|
||||
'contract_address' => $this->contractAddress->hexAddress,
|
||||
'function_selector' => 'transfer(address,uint256)',
|
||||
'parameter' => "{$toFormat}{$numberFormat}",
|
||||
'fee_limit' => 30000000,
|
||||
'call_value' => 0,
|
||||
'owner_address' => $from->hexAddress,
|
||||
]);
|
||||
|
||||
if (!isset($body['result']['result'])) {
|
||||
throw new WalletSdkException(hex2bin($body['result']['message']));
|
||||
}
|
||||
|
||||
try {
|
||||
$tradeobj = $this->tron->signTransaction($body['transaction']);
|
||||
$response = $this->tron->sendRawTransaction($tradeobj);
|
||||
} catch (\Exception $e) {
|
||||
throw new WalletSdkException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
if (isset($response['result']) && $response['result'] == true) {
|
||||
return new TransferTransactionBean($body['transaction']);
|
||||
} else {
|
||||
throw new WalletSdkException(hex2bin($response['result']['message']));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//TRX转账
|
||||
function trxTransfer(TokenAddress $from, TokenAddress $to, float $amount): TransferTransactionBean
|
||||
{
|
||||
$this->tron->setAddress($from->address);
|
||||
$this->tron->setPrivateKey($from->privateKey);
|
||||
|
||||
try {
|
||||
$transaction = $this->tron->getTransactionBuilder()->sendTrx($to->address, $amount, $from->address);
|
||||
$signedTransaction = $this->tron->signTransaction($transaction);
|
||||
$response = $this->tron->sendRawTransaction($signedTransaction);
|
||||
} catch (\Exception $e) {
|
||||
throw new WalletSdkException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
if (isset($response['result']) && $response['result'] == true) {
|
||||
$TransferTransactionBean = new TransferTransactionBean($signedTransaction);
|
||||
return $TransferTransactionBean;
|
||||
} else {
|
||||
throw new WalletSdkException(hex2bin($response['message']));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//根据交易hash获取交易信息
|
||||
function getTransactionByTid(string $tid): array
|
||||
{
|
||||
try {
|
||||
return $this->tron->getTransaction($tid);
|
||||
} catch (\Exception $e) {
|
||||
throw new WalletSdkException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
//检测交易是否成功
|
||||
function checkTransactionByTid(string $tid): bool
|
||||
{
|
||||
$res = $this->getTransactionByTid($tid);
|
||||
if (empty($res)) return false;
|
||||
if (!isset($res['ret']['contractRet'])) return false;
|
||||
if ($res['ret']['contractRet'] === 'SUCCESS') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//生成新地址
|
||||
function createAccount(): TokenAddressBean
|
||||
{
|
||||
try {
|
||||
$res = $this->tron->createAccount();
|
||||
} catch (TronException $e) {
|
||||
throw new WalletSdkException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
return new TokenAddressBean($res->getRawData());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
32
sdk/Wallet/UsdtTrc20.php
Normal file
32
sdk/Wallet/UsdtTrc20.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Sdk\Wallet;
|
||||
|
||||
use App\Exceptions\WalletSdkException;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class UsdtTrc20
|
||||
{
|
||||
const RPC_URL = 'https://api.trongrid.io';
|
||||
const CONTRACT_ADDRESS = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
|
||||
const API_SECRET = 'b484f01e-d21d-449d-8d3a-df2646536486';
|
||||
|
||||
protected Trc20Sdk $sdk;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$headers = [
|
||||
'TRON-PRO-API-KEY' => self::API_SECRET,
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
$client = new Client(['base_uri' => self::RPC_URL, 'headers' => $headers]);
|
||||
$trc20Wallet = new Trc20Sdk($client, self::CONTRACT_ADDRESS);
|
||||
$this->sdk = $trc20Wallet;
|
||||
}
|
||||
|
||||
function getHandler(): Trc20Sdk
|
||||
{
|
||||
return $this->sdk;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user