$url, 'timeout' => 10.0, 'headers' => [ 'Accept' => 'application/json', 'Content-Type' => 'application/json' ], 'verify' => false ]); } } /** * @description: 获取网络 * @return {*} */ public static function getTronNetwork() { $netWork = config('app.tron_network'); // 默认是主网 if($netWork == 'main'){ return false; }else{ return true; } } /** * @description: 创建钱包地址和私钥 * @return {private_key,address} */ public static function createAddress($memberId = ''): array { // 生成 TRC20 私钥 $privateKey = self::makePrivateKey($memberId); // 默认不带 memberId // 从私钥生成地址 $address = self::getAddressByPrivateKey($privateKey); // 记录日志 Log::channel('wallet')->info('创建钱包地址', [ 'member_id' => $memberId, 'address' => $address, 'private_key' => $privateKey, ]); return [ 'address' => $address, 'private_key' => $privateKey, ]; } /** * @description: 转账USDT * @param {*} $privateKey 转账秘钥 * @param {*} $toAddress 接收地址 * @param {*} $amount 转账金额 * @return {*} */ public static function transferUSDT($privateKey, $toAddress, $amount) { $nodeScript = base_path('node/index.js'); // Laravel根目录下的路径 $nodeBin = 'node'; // 用 which node 查看,或自定义 if(self::getTronNetwork()){ $fullNodeUrl = self::$nileUrl; }else{ $fullNodeUrl = self::$tronUrl; } $contractAddress = self::getContractAddress('USDT'); $cmd = sprintf( '%s %s %s %s %s %s %s 2>&1', escapeshellcmd($nodeBin), escapeshellarg($nodeScript), escapeshellarg($privateKey), escapeshellarg($toAddress), escapeshellarg($contractAddress), escapeshellarg($fullNodeUrl), escapeshellarg($amount) ); exec($cmd, $outputLines, $status); $output = implode("\n", $outputLines); $result = json_decode($output, true); return $result; // self::init(); // $contractAddress = self::getContractAddress('USDT'); // $fromAddress = self::getAddressByPrivateKey($privateKey); // // 1. 构建转账交易 // $transferData = [ // 'owner_address' => self::base58check2HexString($fromAddress), // 'contract_address' => self::base58check2HexString($contractAddress), // 'function_selector' => 'transfer(address,uint256)', // 'parameter' => self::buildParameter($toAddress, $amount), // 'visible' => false // ]; // $response = self::$client->post('/wallet/triggersmartcontract', [ // 'json' => $transferData // ]); // $data = json_decode($response->getBody(), true); // var_dump($data); // if (empty($data['transaction'])) { // throw new \Exception('构建转账交易失败: ' . json_encode($data)); // } // $txid = $data['transaction']['txID']; // // 2. 签名交易 // // $rawData = $data['transaction']['raw_data']; // // $rawDataBytes = TronRawSerializer::serializeRawData($rawData); // 自定义函数,返回 Protobuf 序列化后的二进制 // // $txHash = hash('sha256', $rawDataBytes); // Tron使用 SHA256,不是 Keccak256 // // // 使用私钥签名 // // $secp256k1 = new Secp256k1(); // // $signature = $secp256k1->sign($txHash, $privateKey); // // $signatureHex = self::formatTronSignature($signature); // $signedTx = self::signTronTransaction($data['transaction'],$privateKey); // // var_dump($signatureHex); // // $signedTx['visible'] = true; // // 3. 广播交易 // // 将 signature 转成 hex 字符串 // // $signatureHex = bin2hex($signature); // // $signatureHex = $signature; // // var_dump($signatureHex); // // 构建签名交易 // // $signedTx = [ // // 'txID' => $data['transaction']['txID'], // // 'raw_data' => $data['transaction']['raw_data'], // // 'signature' => $signatureHex, // // ]; // var_dump($signedTx); // $broadcast = self::$client->post('/wallet/broadcasttransaction', [ // 'json' => $signedTx // ]); // $broadcastData = json_decode($broadcast->getBody(), true); // return $broadcastData; } public static function signTronTransaction(array $transaction, string $privateKey): array { $rawData = $transaction['raw_data'] ?? null; if (!$rawData) { throw new \Exception('交易中没有 raw_data'); } $rawDataBytes = TronRawSerializer::serializeRawData($rawData); $txHash = hash('sha256', $rawDataBytes, true); // 二进制哈希 $privateKeyHex = ltrim($privateKey, '0x'); if (strlen($privateKeyHex) !== 64) { throw new \Exception('私钥格式不正确'); } $privateKeyBin = hex2bin($privateKeyHex); $secp256k1 = new Secp256k1(); $signatureObj = $secp256k1->sign($txHash, $privateKeyBin); // r, s 都是 GMP 对象,转成32字节二进制字符串 $rBin = self::gmpToBin32($signatureObj->getR()); $sBin = self::gmpToBin32($signatureObj->getS()); $signatureBin = $rBin . $sBin; // 64字节二进制签名 $transaction['signature'] = [$signatureBin]; return $transaction; } private static function gmpToBin32($gmpNum): string { $hex = gmp_strval($gmpNum, 16); if (strlen($hex) % 2 !== 0) { $hex = '0' . $hex; } $bin = hex2bin($hex); return str_pad($bin, 32, "\0", STR_PAD_LEFT); } /** * 构建 parameter 字符串(目标地址 + 金额) */ protected static function buildParameter($toAddress, $amount) { $hexAddress = self::base58check2HexString($toAddress); $addr = str_pad(substr($hexAddress, 2), 64, '0', STR_PAD_LEFT); // 去掉 '41' 开头的 Tron 前缀 $amt = str_pad(bcdechex(bcmul($amount, '1000000')), 64, '0', STR_PAD_LEFT); // USDT 精度 6 return $addr . $amt; } // /** // * @param Signature $signature // * @return string 65字节签名的十六进制字符串 // */ // public static function formatTronSignature(Signature $signature): string // { // $r = gmp_strval($signature->getR(), 16); // $s = gmp_strval($signature->getS(), 16); // $v = dechex($signature->getRecoveryParam()); // v 即 recoveryParam // // 补齐 r 和 s 为 64位长度(即32字节) // $r = str_pad($r, 64, '0', STR_PAD_LEFT); // $s = str_pad($s, 64, '0', STR_PAD_LEFT); // $v = str_pad($v, 2, '0', STR_PAD_LEFT); // 1字节 = 2个hex字符 // $v_decimal = hexdec($v); // 0 // $v_decimal += 27; // 27 // $v_hex = dechex($v_decimal); // "1b" // return $r . $s . $v_hex; // 返回最终的签名字符串 // } /** * @description: 获取地址的YRX余额 * @param {*} $addressBase58 * @return {*} */ public static function getTrxBalance($addressBase58) { $hexAddress = TronHelper::base58check2HexString($addressBase58); self::init(); $response = self::$client->post('/wallet/getaccount', [ 'json' => ['address' => $hexAddress] ]); $data = json_decode($response->getBody(), true); if (!empty($data['balance'])) { // TRX 的单位是 sun,1 TRX = 1_000_000 sun return $data['balance'] / 1_000_000; } return 0; } // /** // * @description: 向账户转TRX能量 // * @param {*} $fromAddress // * @param {*} $privateKey // * @param {*} $toAddress // * @param {*} $amount // * @return {*} // */ // public static function transferTRX($fromAddress, $privateKey, $toAddress, $amount) // { // self::init(); // $ownerAddressHex = self::base58check2HexString($fromAddress); // $toAddressHex = self::base58check2HexString($toAddress); // $createData = [ // 'owner_address' => $ownerAddressHex, // 'to_address' => $toAddressHex, // 'amount' => (int)($amount * 1_000_000), // TRX 精度为 6 // ]; // // 构建转账交易 // $response = self::$client->post('/wallet/createtransaction', [ // 'json' => $createData // ]); // $tx = json_decode($response->getBody(), true); // if (!isset($tx['txID'])) { // throw new \Exception('创建转账交易失败: ' . json_encode($tx)); // } // // 签名交易 // $signature = self::signTransaction($tx,$privateKey); // // 广播交易 // $signedTx = [ // 'txID' => $tx['txID'], // 'raw_data' => $tx['raw_data'], // 'raw_data_hex' => $tx['raw_data_hex'], // 'signature' => $signature, // ]; // $response = self::$client->post('/wallet/broadcasttransaction', [ // 'json' => $signedTx // ]); // var_dump($signedTx); // $result = json_decode($response->getBody(), true); // var_dump($result); // die(); // // if (!isset($result['result']) || !$result['result']) { // // throw new \Exception('广播失败: ' . json_encode($result)); // // } // return [ // 'txID' => $tx['txID'], // 'result' => true // ]; // } // /** // * @description: TRX签名 // * @param {array} $transaction // * @param {string} $privateKey // * @return {*} // */ // public static function signTransaction($tx, $privateKey) // { // if (empty($tx['raw_data_hex'])) { // throw new \Exception('原始交易数据为空,无法签名'); // } // $rawDataHex = $tx['raw_data_hex']; // $rawDataBin = hex2bin($rawDataHex); // $hash = hash('sha256', $rawDataBin, true); // 原始数据做 SHA256 // $signatureDer = self::ecdsaSignRaw($hash, $privateKey); // 返回 DER 格式签名 // return [$signatureDer]; // Tron 要求是签名数组 // } // public static function ecdsaSignRaw($hash, $privateKeyHex) // { // $ec = new EC('secp256k1'); // $key = $ec->keyFromPrivate($privateKeyHex); // $signature = $key->sign(bin2hex($hash), ['canonical' => true]); // $r = str_pad($signature->r->toString('hex'), 64, '0', STR_PAD_LEFT); // $s = str_pad($signature->s->toString('hex'), 64, '0', STR_PAD_LEFT); // return hex2bin($r . $s); // 返回64字节的二进制字符串 // } /** * 查询某地址的 TRX 充值记录 * @param string $address 充值地址 * @param int $limit 返回记录数 * @return array */ public static function getTrxTransactions(string $address, int $limit = 30): array { self::init(); $response = self::$client->get("https://nile.trongrid.io/v1/accounts/{$address}/transactions", [ 'query' => [ 'limit' => $limit, 'only_confirmed' => 'true', 'order' => 'desc', ] ]); $data = json_decode($response->getBody()->getContents(), true); return $data; } /** * 查询某地址的 TRC20 USDT 充值记录 * @param string $address 充值地址 * @param int $limit 返回记录数 * @return array */ public static function getTrc20UsdtRecharges(string $address, int $limit = 30): array { self::init(); $usdtContract = self::getContractAddress('USDT'); $url = "/v1/accounts/{$address}/transactions/trc20?limit={$limit}"; // try { $response = self::$client->get($url, [ 'headers' => [ 'Accept' => 'application/json', ], 'timeout' => 10, ]); $data = json_decode($response->getBody()->getContents(), true); $recharges = []; foreach ($data['data'] ?? [] as $item) { $tokenAddress = $item['token_info']['address'] ?? ''; if (strtolower($tokenAddress) !== strtolower($usdtContract)) { continue; // 不是USDT } if($item['to'] == $address){ $recharges[] = [ 'txid' => $item['transaction_id'], 'from_address' => $item['from'], 'to_address' => $item['to'], 'amount' => bcdiv($item['value'], bcpow('10', $item['token_info']['decimals']), 6), 'coin' => $item['token_info']['symbol'], 'block_time' => intval($item['block_timestamp'] / 1000), ]; } } return $recharges; // } catch (\Exception $e) { // return ['error' => $e->getMessage()]; // } } /** * @description: 获取TRC20账户余额 * @param {*} $address * @return {*} */ public static function getTrc20Balance($address) { self::init(); $contractAddress = self::getContractAddress('USDT'); $hexAddress = self::base58check2HexString($address); $postData = [ 'owner_address' => $hexAddress, 'contract_address' => self::base58check2HexString($contractAddress), 'function_selector' => 'balanceOf(address)', 'parameter' => str_pad(substr($hexAddress, 2), 64, '0', STR_PAD_LEFT), ]; // try { $response = self::$client->post('/wallet/triggerconstantcontract', [ 'headers' => [ 'Content-Type' => 'application/json', 'TRON-PRO-API-KEY' => '3b241a9c-0076-49bf-b883-a003c97c19f6', // 添加这一行 ], 'json' => $postData, ]); $body = $response->getBody()->getContents(); $data = json_decode($body, true); $hexBalance = $data['constant_result'][0] ?? '0x0'; return hexdec($hexBalance) / 1e6; // USDT 精度是 6 位 // } catch (\Exception $e) { // // 可以记录日志或抛出异常 // return 0; // } } /** * 查询交易确认数 * @param string $txid 交易哈希 * @return array */ public static function getTransactionConfirmations(string $txid): array { // $client = new Client(['timeout' => 10]); self::init(); try { // 1. 获取交易信息 $txResp = self::$client->post('/wallet/gettransactioninfobyid', [ 'json' => ['value' => $txid], 'headers' => ['Accept' => 'application/json'], ]); $txData = json_decode($txResp->getBody()->getContents(), true); if (!isset($txData['blockNumber'])) { return ['success' => false, 'message' => '交易尚未被打包或无效']; } $txBlock = $txData['blockNumber']; // 2. 获取最新区块高度 $blockResp = self::$client->get('/wallet/getnowblock', [ 'headers' => ['Accept' => 'application/json'], ]); $blockData = json_decode($blockResp->getBody()->getContents(), true); $latestBlock = $blockData['block_header']['raw_data']['number'] ?? 0; if ($latestBlock == 0) { return ['success' => false, 'message' => '无法获取最新区块']; } // 3. 计算确认数 $confirmations = $latestBlock - $txBlock + 1; return [ 'success' => true, 'txid' => $txid, 'block_number' => $txBlock, 'latest_block' => $latestBlock, 'confirmations' => $confirmations, ]; } catch (\Exception $e) { return ['success' => false, 'message' => $e->getMessage()]; } } /** * 加密私钥 */ public static function encryptPrivateKey(string $privateKeyHex): string { return Crypt::encryptString($privateKeyHex); } /** * 解密私钥 */ public static function decryptPrivateKey(string $encrypted): string { return Crypt::decryptString($encrypted); } /** * 获取合约地址 * @param $currency * @return string */ public static function getContractAddress($currency) { $currency = strtoupper($currency); switch ($currency) { case 'USDT'://usdt if(self::getTronNetwork()){ $contractAddress = 'TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf'; // nile测试地址 }else{ $contractAddress = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; } break; case 'USDC'://usdc $contractAddress = 'TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8'; break; case 'USDJ'://USDJ $contractAddress = 'TMwFHYXLJaRUPeW6421aqXL4ZEzPRFGkGT'; break; case 'TUSD'://TUSD $contractAddress = 'TUpMhErZL2fhh4sVNULAbNKLokS4GjC1F4'; break; case 'BTC'://BTC $contractAddress = 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9'; break; case 'ETH'://ETH $contractAddress = 'THb4CqiFdwNHsWsQCs4JhzwjMWys4aqCbF'; break; case 'USDD'://USDD $contractAddress = 'TPYmHEhy5n8TCEfYGqW2rPxsghSfzghPDn'; break; default: $contractAddress = ''; } return $contractAddress; } public static function base58_encode($string) { if (is_string($string) === false) { return false; } if ($string === '') { return ''; } $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; $base = strlen($alphabet); $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 $output; } public static function base58_decode($base58) { if (is_string($base58) === false) { return false; } if ($base58 === '') { return ''; } $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; $base = strlen($alphabet); $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 public static function base58check_en($address) { $hash0 = hash("sha256", $address); $hash1 = hash("sha256", hex2bin($hash0)); $checksum = substr($hash1, 0, 8); $address .= hex2bin($checksum); return self::base58_encode($address); } public static function base58check_de($base58add) { $address = self::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; } public static function hexString2Base58check($hexString) { $address = hex2bin($hexString); return self::base58check_en($address); } public static function base58check2HexString($base58add) { $address = self::base58check_de($base58add); return bin2hex($address); } public static function hexString2Base64($hexString) { $address = hex2bin($hexString); return base64_encode($address); } public static function base642HexString($base64) { $address = base64_decode($base64); return bin2hex($address); } public static function base58check2Base64($base58add) { $address =self::base58check_de($base58add); return base64_encode($address); } public static function base642Base58check($base64) { $address = base64_decode($base64); return self::base58check_en($address); } public static function getrandstr($length) { $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'; $randStr = str_shuffle($str);//打乱字符串 return substr($randStr, 0, $length); } /** * 检测是否有效钱包地址 * @param $address * @return bool */ public static function isValidAddress($address) { if (empty($address)) { return false; } if (strlen($address) != 34) { return false; } if (substr($address, 0, 1) != 'T') { return false; } // 验证地址 $client = new Client([ 'verify' => false, // 关闭 SSL 验证 ]); $response = $client->request('POST', 'https://api.shasta.trongrid.io/wallet/validateaddress', [ 'body' => json_encode(["address" => $address]), 'headers' => [ 'Accept' => 'application/json', 'Content-Type' => 'application/json', ], ]); $resp = $response->getBody()->getContents(); $resp = json_decode($resp); // 失败提示错误 if ($resp->result === false) { return false; } return true; } /** * 获取私钥,通过私钥可以拿到收款地址,各种权限 * 注意:必须拿28位字符串来生成地址,否则无法生成正常的地址 * @param $base64 * @return string */ public function getPrivateKey($base64) { return $this->base642HexString($base64); } /** * 生成波场key * @param $memberId * @return string */ public static function makePrivateKey($memberId) { $fix = 'SSTGsstg';//固定8前缀,避免和其他人同地址 $rand = TronHelper::getrandstr(20);//10位数随机,避免同地址 $base64Sting = $fix.$rand.str_pad($memberId, 15, 0, STR_PAD_LEFT); $tronHelper = new TronHelper(); return $tronHelper->getPrivateKey($base64Sting); } /** * 查询余额 * @param $privateKey * @return int */ public static function getBalance($address, $currency) { bcscale(6); $rate = 1000000; $currency = strtoupper($currency); if(self::getTronNetwork()){ $api = TronApi::testNetNilo(); }else{ $api = TronApi::mainNet(); } $privateKey = self::makePrivateKey(time()); $credential = Credential::fromPrivateKey($privateKey); $kit = new TronKit($api, $credential); if ($currency == 'TRX') { try { $balance = $kit->getTrxBalance($address); return bcdiv($balance, $rate); } catch (\Exception $e) { return bcmul(0, 0); } } try { $contractAddress = self::getContractAddress($currency); $usdt = $kit->Trc20($contractAddress); $balance = $usdt->balanceOf($address); return bcdiv($balance, $rate); } catch (\Exception $e) { return bcmul(0, 0); } } /** * 获取转账信息 * @param $txid */ public static function getSendInfo($txid) { // $api = TronApi::mainNet(); if(self::getTronNetwork()){ $api = TronApi::testNetNilo(); }else{ $api = TronApi::mainNet(); } $ret = $api->getTransaction($txid); $retArr = $ret->ret; if (isset($retArr[0]->contractRet)) { return $retArr[0]->contractRet; } return 'UNKNOWN'; } /** * 完整交易详情,如果交易未完成或者不成功,返回空 * @param $txid * @return string */ public static function getTransactionInfoById($txid) { // $api = TronApi::mainNet(); if(self::getTronNetwork()){ $api = TronApi::testNetNilo(); }else{ $api = TronApi::mainNet(); } $ret = $api->getTransactionInfoById($txid); return json_decode(json_encode($ret), true); } /** * 获取出款账户地址 */ public static function getWithdrawAddress() { // $privateKey = Yii::$app->params['withdraw_address_trx_key']; // $credential = Credential::fromPrivateKey($privateKey); // return $credential->address()->base58(); } /** * 获取地址 * @param $fromKey */ public static function getAddressByPrivateKey($fromKey) { $credential = Credential::fromPrivateKey($fromKey); return $credential->address()->base58(); } /** * 转账给指定地址 * @param $fromKey * @param $toAddress * @param $amount * @throws \Exception */ public static function sendTrx($fromKey, $toAddress, $amount) { if ($amount <= 0) { throw new \Exception('不能低于 0 trx'); } // from 初始化 if(self::getTronNetwork()){ $api = TronApi::testNetNilo(); }else{ $api = TronApi::mainNet(); } $credential = Credential::fromPrivateKey($fromKey); $kit = new TronKit($api, $credential); // from 余额判断 $from = $credential->address()->base58(); $balance = self::getBalance($from, 'TRX'); $rate = 1000000; if ($balance < $amount) { return '余额不足'; } // 发送 try { return $kit->sendTrx($toAddress, 1 * bcmul($amount, $rate, 6)); } catch (\Exception $e) { Log::error('转账错误,fromKey='.$fromKey.'toAddress='.$toAddress.'amount='.$amount); Log::error('转账错误'.$e->getMessage()); return false; } } /**usdt转账 * @param $fromKey * @param $toAddress * @param $amount * @param $currency * @return bool|object * @throws \Exception */ public static function sendTrc20($fromKey, $toAddress, $amount, $currency) { if ($amount <= 0) { throw new \Exception('不能低于 0 USDT'); } $credential = Credential::fromPrivateKey($fromKey); $credential->address()->hex(); if(self::getTronNetwork()){ $api = TronApi::testNetNilo(); }else{ $api = TronApi::mainNet(); } $kit = new TronKit($api, $credential); $contractAddress = self::getContractAddress($currency); $rate = 1000000;//换算汇率 // $balance = self::getBalance(self::getAddressByPrivateKey($fromKey), $currency); //查询Trc20代币余额 $balance = self::getTrc20Balance(self::getAddressByPrivateKey($fromKey)); //查询Trc20代币余额 if ($balance < $amount) { throw new \Exception('余额不足'); } $trc20 = $kit->Trc20($contractAddress); //创建Trc20代币合约实例 try { $ret = $trc20->transfer($toAddress, (int) bcmul($amount, $rate)); //转账Trc20代币 return (object) [ 'txid' => $ret->tx->txID, 'result' => $ret->result, ]; } catch (\Exception $e) { Log::error('Trc20转账错误,fromKey='.$fromKey.'toAddress='.$toAddress.'amount='.$amount); Log::error('Trc20转账错误'.$e->getMessage()); return false; } } /** * 归集账户资金校验 * trade_status: * - failed 转账失败 * - waiting 转账成功,尚未校验 * - closed waiting 之后,脚本查询时,交易验证成功 * - aborted waiting 之后,脚本查询时,交易验证失败 * @param $fromKey * @param $toAddress * @param $amount * @param $memberId * @param string $currency * @param string $net * @return bool * @throws \Exception */ public function actionFundsGather( $fromKey, $toAddress, $amount, $memberId, $currency = 'TRX', $net = 'TRC20' ) { // $currency = strtoupper($currency); // if ($amount <= 0) { // throw new \Exception('不能低于 0 trx'); // } // if (empty($toAddress)) { // throw new \Exception('收款地址不能为空'); // } // $fromAddress = self::getAddressByPrivateKey($fromKey); // $remainAmount = self::getBalance($fromAddress, $currency); // if ($remainAmount < $amount) { // throw new \Exception('余额不足,归集失败'); // } // $min = MinRechargeEnum::getValue($currency); // if ($currency == 'TRX') { // $ret = self::sendTrx($fromKey, $toAddress, $amount); // } else { // // 发送 // //校验trx是否足够,不够就充值10燃烧费用进来 // if (self::getBalance($fromAddress, 'TRX') < 10) { // $withdrawAddressTrxKey = Yii::$app->params['withdraw_address_trx_key']; // self::sendTrx($withdrawAddressTrxKey, self::getAddressByPrivateKey($fromKey), 10); // } // $ret = self::sendTrc20($fromKey, $toAddress, $amount, $currency); // } // //少于最低可入款额度,不入账,只做归集 // if ($min > $remainAmount) { // return false; // } // try { // // 记录 // $fundsGather = new Recharge(); // $fundsGather->member_id = $memberId; // $fundsGather->txid = $ret->txid; // $fundsGather->amount = $amount; // $fundsGather->address = $toAddress; // $fundsGather->created_at = time(); // $fundsGather->currency = $currency; // $fundsGather->usdt_balance = bcmul($amount, ExchangeRateService::getExchangeRate($currency), // CommonEnum::DIGITS); // $fundsGather->is_auto = 1; // $fundsGather->count = Recharge::find()->where(['member_id' => $memberId])->count() + 1; // $fundsGather->net = $net; // $fundsGather->ordernum = StrHelper::createOrdernum(); // // 记录「充值请求次数」 // $member = Member::findOne($memberId); // $member->recharge_request_count = $fundsGather->count; // $member->save(); // if ($ret->result == 1) { // 成功 // $fundsGather->trade_status = 'waiting'; // if ($fundsGather->save()) { // return true; // } // } else { // 失败 // $fundsGather->trade_status = 'failed'; // $fundsGather->save(); // } // } catch (Exception $e) { // throw new \Exception('转账异常'.$e->getMessage()); // } return false; } }