BlockChianService.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. <?php
  2. namespace crmeb\services\blockchain;
  3. use app\models\lala\WalletLog;
  4. use app\models\user\UserMoney;
  5. use crmeb\basic\BaseModel;
  6. use GuzzleHttp\Client;
  7. use think\db\exception\DataNotFoundException;
  8. use think\db\exception\DbException;
  9. use think\db\exception\ModelNotFoundException;
  10. use think\facade\Log;
  11. use Tron\Address;
  12. use Tron\Api;
  13. use Tron\Exceptions\TronErrorException;
  14. use Tron\TRC20;
  15. use Tron\TRX;
  16. class BlockChianService
  17. {
  18. //TODO main
  19. /**
  20. * 生成地址
  21. * @param string $code
  22. * @return array
  23. */
  24. public static function createAddress(string $code): array
  25. {
  26. $code = strtoupper($code);
  27. if (method_exists(self::class, $code . 'CreateAddress')) {
  28. $function = $code . 'CreateAddress';
  29. return self::$function();
  30. } else {
  31. return ["privateKey" => "",
  32. "address" => "",
  33. "hexAddress" => ""];
  34. }
  35. }
  36. /**
  37. * 生成地址
  38. * @param string $code
  39. * @param string $address
  40. * @return float
  41. */
  42. public static function balance(string $code, string $address): float
  43. {
  44. $code = strtoupper($code);
  45. if (method_exists(self::class, $code . 'Balance')) {
  46. $function = $code . 'Balance';
  47. return self::$function($address);
  48. } else {
  49. return 0;
  50. }
  51. }
  52. /**
  53. * 归集
  54. * @param array $money_info
  55. * @param string $summary_address
  56. * @param Address $gas_address
  57. * @return bool
  58. */
  59. public static function summary(array $money_info, string $summary_address, Address $gas_address): bool
  60. {
  61. $code = strtoupper($money_info['money_type']);
  62. if (method_exists(self::class, $code . 'Summary')) {
  63. $function = $code . 'Summary';
  64. return self::$function($money_info, $summary_address, $gas_address);
  65. } else {
  66. Log::error('币种' . $code . '尚不支持归集');
  67. return true;
  68. }
  69. }
  70. /**
  71. * 交易
  72. * @param string $code
  73. * @param Address $from
  74. * @param Address $to
  75. * @param float $amount
  76. * @return bool
  77. */
  78. public static function transfer(string $code, Address $from, Address $to, float $amount): bool
  79. {
  80. $code = strtoupper($code);
  81. if (method_exists(self::class, $code . 'Transfer')) {
  82. $function = $code . 'Transfer';
  83. return self::$function($from, $to, $amount);
  84. } else {
  85. Log::error('币种' . $code . '尚不支持交易');
  86. return true;
  87. }
  88. }
  89. /**
  90. * 获取交易
  91. * @param string $code
  92. * @param $address
  93. * @param $start_time
  94. * @return array
  95. */
  96. public static function checkTransfer(string $code, $address, $start_time): array
  97. {
  98. $code = strtoupper($code);
  99. if (method_exists(self::class, $code . 'CheckTransfer')) {
  100. $function = $code . 'CheckTransfer';
  101. return self::$function($address, $start_time);
  102. } else {
  103. return [];
  104. }
  105. }
  106. /**
  107. * 查询交易
  108. * @param string $code
  109. * @param string $txID
  110. * @return array
  111. */
  112. public static function getTransfer(string $code, string $txID): array
  113. {
  114. $code = strtoupper($code);
  115. if (method_exists(self::class, $code . 'GetTransfer')) {
  116. $function = $code . 'GetTransfer';
  117. return self::$function($txID);
  118. } else {
  119. return [];
  120. }
  121. }
  122. //TODO main - end
  123. //TODO auto
  124. /**
  125. * 自动获取交易记录
  126. * @return bool
  127. */
  128. public static function autoGetTransfer(): bool
  129. {
  130. $address = UserMoney::select();
  131. foreach ($address as $v) {
  132. if ($v['address']) {
  133. $res = BlockChianService::checkTransfer($v['money_type'], $v['address'], $v['max_timestamp'] + 1);
  134. if ($res) {
  135. switch ($v['money_type']) {
  136. case 'USDT':
  137. if (isset($res['data'])) {
  138. foreach ($res['data'] as $vv) {
  139. $data = [
  140. 'symbol' => $v['money_type'],
  141. 'transaction_id' => $vv['transaction_id'],
  142. 'from' => $vv['from'],
  143. 'to' => $vv['to'],
  144. 'type' => $vv['type'],
  145. 'amount' => bcdiv($vv['value'], bcpow(10, $vv['token_info']['decimals']), 8),
  146. 'block_timestamp' => $vv['block_timestamp'],
  147. ];
  148. if (!WalletLog::be($data)) {
  149. WalletLog::beginTrans();
  150. try {
  151. WalletLog::create($data);
  152. WalletLog::commitTrans();
  153. } catch (\Exception $e) {
  154. WalletLog::rollbackTrans();
  155. Log::error('AutoLoadTransferFail:' . $e->getMessage());
  156. return false;
  157. }
  158. }
  159. }
  160. }
  161. break;
  162. default:
  163. break;
  164. }
  165. }
  166. UserMoney::beginTrans();
  167. try {
  168. UserMoney::where('address', $v['address'])->update(['max_timestamp' => time() * 1000]);
  169. UserMoney::commitTrans();
  170. } catch (\Exception $e) {
  171. UserMoney::rollbackTrans();
  172. Log::error('AutoLoadTransferFail:' . $e->getMessage());
  173. return false;
  174. }
  175. }
  176. }
  177. return true;
  178. }
  179. /**
  180. * 自动确认交易
  181. * @return bool
  182. */
  183. public static function autoCheckTransfer(): bool
  184. {
  185. BaseModel::beginTrans();
  186. try {
  187. $list = WalletLog::where('state', 0)->select();
  188. foreach ($list as $v) {
  189. $res = BlockChianService::getTransfer($v['symbol'], $v['transaction_id']);
  190. if ($res['status']) {
  191. switch ($v['symbol']) {
  192. case 'USDT':
  193. if (isset($res['data'])) {
  194. if (isset($res['data']->contractRet)) {
  195. if ($res['data']->contractRet === 'SUCCESS') {
  196. $update['state'] = 1;
  197. WalletLog::where('id', $v['id'])->update($update);
  198. $user = UserMoney::where('address', $v['to'])->where('money_type', $v['symbol'])->value('uid');
  199. if ($user) {
  200. UserMoney::incomeMoney($user, $v['symbol'], $v['amount'], 'recharge', '用户充值', '用户充值' . $v['amount'] . $v['symbol'], $v['id']);
  201. }
  202. } else {
  203. $update['state'] = 2;
  204. WalletLog::where('id', $v['id'])->update($update);
  205. }
  206. }
  207. }
  208. break;
  209. default:
  210. break;
  211. }
  212. }
  213. }
  214. BaseModel::commitTrans();
  215. return true;
  216. } catch (\Exception $e) {
  217. BaseModel::rollbackTrans();
  218. Log::error('AutoCheckTransferFail:' . $e->getMessage());
  219. return false;
  220. }
  221. }
  222. /**
  223. * 自动归集
  224. * @return bool
  225. * @throws DataNotFoundException
  226. * @throws DbException
  227. * @throws ModelNotFoundException
  228. */
  229. public function autoSummary(): bool
  230. {
  231. $money_types = sys_data('money_type');
  232. $res = true;
  233. foreach ($money_types as $v) {
  234. if ($v['charge']) {
  235. //TODO 接入链上充值
  236. $addresses = UserMoney::where('max_timestamp', '>', 0)->select()->toArray();
  237. foreach ($addresses as $vv) {
  238. $res = $res && self::summary($vv, $v['summary_address'], new Address($v['gas_address'], $v['gas_key']));
  239. }
  240. }
  241. }
  242. return $res;
  243. }
  244. //TODO auto - end
  245. //TODO USDT
  246. /**
  247. * USDT 创建地址
  248. * @return array
  249. */
  250. public static function USDTCreateAddress(): array
  251. {
  252. try {
  253. $uri = 'https://api.trongrid.io';
  254. $api = new Api(new Client(['base_uri' => $uri]));
  255. $config = [
  256. 'contract_address' => 'TB5UtaMMv9apgcKxxYBLkjqmixab9N7EsR',// USDT TRC20
  257. 'decimals' => 6,
  258. ];
  259. $trxWallet = new TRC20($api, $config);
  260. $address = $trxWallet->generateAddress();
  261. return [
  262. 'privateKey' => $address->privateKey,
  263. 'address' => $address->address,
  264. 'hexAddress' => $address->hexAddress
  265. ];
  266. } catch (\Exception $e) {
  267. return [
  268. 'privateKey' => '',
  269. 'address' => '',
  270. 'hexAddress' => ''
  271. ];
  272. }
  273. }
  274. /**
  275. * USDT 余额
  276. * @param $address
  277. * @return float
  278. */
  279. public static function USDTBalance($address): float
  280. {
  281. try {
  282. $uri = 'https://api.trongrid.io';
  283. $api = new Api(new Client(['base_uri' => $uri]));
  284. $config = [
  285. 'contract_address' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',// USDT TRC20
  286. 'decimals' => 6,
  287. ];
  288. $trxWallet = new TRC20($api, $config);
  289. $balance = $trxWallet->balance(new Address($address));
  290. return (float)$balance;
  291. } catch (\Exception $e) {
  292. return 0;
  293. }
  294. }
  295. /**
  296. * USDT 交易
  297. * @param $from
  298. * @param $to
  299. * @param $amount
  300. * @return array
  301. */
  302. public static function USDTTransfer($from, $to, $amount): array
  303. {
  304. try {
  305. $uri = 'https://api.trongrid.io';
  306. $api = new Api(new Client(['base_uri' => $uri]));
  307. $config = [
  308. 'contract_address' => 'TB5UtaMMv9apgcKxxYBLkjqmixab9N7EsR',// USDT TRC20
  309. 'decimals' => 6,
  310. ];
  311. $trxWallet = new TRC20($api, $config);
  312. $transfer = $trxWallet->transfer($from, $to, $amount);
  313. return ['msg' => 'success', 'data' => $transfer, 'status' => true];
  314. } catch (\Exception $e) {
  315. return ['msg' => $e->getMessage(), 'status' => false, 'data' => $e->getTrace()];
  316. }
  317. }
  318. /**
  319. * USDT 获取交易记录
  320. * @param $address
  321. * @param $start_time
  322. * @return array|mixed
  323. */
  324. public static function USDTCheckTransfer($address, $start_time)
  325. {
  326. try {
  327. $uri = "https://api.trongrid.io/v1/accounts/{$address}/transactions/trc20";
  328. $contract_address = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';// USDT TRC20
  329. return json_decode(do_request($uri, [
  330. 'min_timestamp' => $start_time,
  331. 'limit' => 200,
  332. 'contract_address' => $contract_address,
  333. 'order_by' => 'block_timestamp,asc'
  334. ], null, false), true);
  335. } catch (\Exception $e) {
  336. return ['msg' => $e->getMessage(), 'status' => false, 'data' => $e->getTrace()];
  337. }
  338. }
  339. /**
  340. * USDT 确认交易记录
  341. * @param $txID
  342. * @return array
  343. */
  344. public static function USDTGetTransfer($txID)
  345. {
  346. try {
  347. $uri = 'https://api.trongrid.io';
  348. $api = new Api(new Client(['base_uri' => $uri]));
  349. $config = [
  350. 'contract_address' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',// USDT TRC20
  351. 'decimals' => 6,
  352. ];
  353. $trxWallet = new TRC20($api, $config);
  354. $transfer = $trxWallet->transactionReceipt($txID);
  355. return ['msg' => 'success', 'data' => $transfer, 'status' => true];
  356. } catch (\Exception $e) {
  357. return ['msg' => $e->getMessage(), 'status' => false, 'data' => $e->getTrace()];
  358. }
  359. }
  360. /**
  361. * USDT 归集
  362. * @param $money_info
  363. * @param $summary_address
  364. * @param $gas_address
  365. * @return bool
  366. * @throws TronErrorException
  367. */
  368. public static function USDTSummary($money_info, $summary_address, $gas_address): bool
  369. {
  370. $uri = 'https://api.trongrid.io';
  371. $api = new Api(new Client(['base_uri' => $uri]));
  372. $config = [
  373. 'contract_address' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',// USDT TRC20
  374. 'decimals' => 6,
  375. ];
  376. $trxWallet = new TRC20($api, $config);
  377. $address = new Address($money_info['address'], $money_info['privateKey'], $money_info['hexAddress']);
  378. $balance = $trxWallet->balance($address);
  379. //var_dump($balance);
  380. if ($balance > 0) {
  381. $trx = new TRX($api);
  382. $trx_balance = $trx->balance($address);
  383. if ($trx_balance < sys_config('trx_gas', 10)) {
  384. $trade_trx = ceil(10 - $trx_balance);
  385. //执行手续费转账
  386. $gas_trx_balance = $trx->balance($gas_address);
  387. if ($gas_trx_balance < $trade_trx + 2) {
  388. Log::error('USDT手续费账户余额不足');
  389. return false;
  390. }
  391. try {
  392. $res = $trx->transfer($gas_address, $address, $trade_trx);
  393. // var_dump($res);
  394. if (isset($res->txID)) {
  395. Log::error('转账TRX交易哈希:' . $res->txID);
  396. return true;
  397. } else {
  398. Log::error('转账TRX失败');
  399. return false;
  400. }
  401. } catch (\Exception $e) {
  402. Log::error('转账TRX失败' . $e->getMessage());
  403. return false;
  404. }
  405. } else {
  406. //执行转账
  407. try {
  408. $res = self::USDTTransfer($address, new Address($summary_address), $balance);
  409. if ($res['status']) {
  410. Log::error('转账交易哈希:' . $res['data']->txID);
  411. return true;
  412. } else {
  413. Log::error('转账失败' . $res['msg']);
  414. return false;
  415. }
  416. } catch (\Exception $e) {
  417. Log::error('转账失败' . $e->getMessage());
  418. return false;
  419. }
  420. }
  421. }
  422. return true;
  423. }
  424. //TODO USDT - end
  425. //TODO LALA
  426. /**
  427. * LALA 创建地址
  428. * @return array
  429. */
  430. public static function LALACreateAddress(): array
  431. {
  432. try {
  433. $uri = 'https://api.trongrid.io';
  434. $api = new Api(new Client(['base_uri' => $uri]));
  435. $config = [
  436. 'contract_address' => 'TLuwWZ8MkB6FFJDLYc19u7d4tFq2GV3rPL',// LALA TRC20
  437. 'decimals' => 8,
  438. ];
  439. $trxWallet = new TRC20($api, $config);
  440. $address = $trxWallet->generateAddress();
  441. return [
  442. 'privateKey' => $address->privateKey,
  443. 'address' => $address->address,
  444. 'hexAddress' => $address->hexAddress
  445. ];
  446. } catch (\Exception $e) {
  447. return [
  448. 'privateKey' => '',
  449. 'address' => '',
  450. 'hexAddress' => ''
  451. ];
  452. }
  453. }
  454. /**
  455. * USDT 余额
  456. * @param $address
  457. * @return float
  458. */
  459. public static function LALABalance($address): float
  460. {
  461. try {
  462. $uri = 'https://api.trongrid.io';
  463. $api = new Api(new Client(['base_uri' => $uri]));
  464. $config = [
  465. 'contract_address' => 'TLuwWZ8MkB6FFJDLYc19u7d4tFq2GV3rPL',// USDT TRC20
  466. 'decimals' => 8,
  467. ];
  468. $trxWallet = new TRC20($api, $config);
  469. $balance = $trxWallet->balance(new Address($address));
  470. return (float)$balance;
  471. } catch (\Exception $e) {
  472. return 0;
  473. }
  474. }
  475. /**
  476. * LALA 交易
  477. * @param $from
  478. * @param $to
  479. * @param $amount
  480. * @return array
  481. */
  482. public static function LALATransfer($from, $to, $amount): array
  483. {
  484. try {
  485. $uri = 'https://api.trongrid.io';
  486. $api = new Api(new Client(['base_uri' => $uri]));
  487. $config = [
  488. 'contract_address' => 'TLuwWZ8MkB6FFJDLYc19u7d4tFq2GV3rPL',// LALA TRC20
  489. 'decimals' => 8,
  490. ];
  491. $trxWallet = new TRC20($api, $config);
  492. $transfer = $trxWallet->transfer($from, $to, $amount);
  493. return ['msg' => 'success', 'data' => $transfer, 'status' => true];
  494. } catch (\Exception $e) {
  495. return ['msg' => $e->getMessage(), 'status' => false, 'data' => $e->getTrace()];
  496. }
  497. }
  498. /**
  499. * LALA 获取交易记录
  500. * @param $address
  501. * @param $start_time
  502. * @return array
  503. */
  504. public static function LALACheckTransfer($address, $start_time)
  505. {
  506. try {
  507. $uri = "https://api.trongrid.io/v1/accounts/{$address}/transactions/trc20";
  508. $contract_address = 'TLuwWZ8MkB6FFJDLYc19u7d4tFq2GV3rPL';// LALA TRC20
  509. return json_decode(do_request($uri, [
  510. 'min_timestamp' => $start_time,
  511. 'limit' => 200,
  512. 'contract_address' => $contract_address,
  513. 'order_by' => 'block_timestamp,asc'
  514. ], null, false), true);
  515. } catch (\Exception $e) {
  516. return ['msg' => $e->getMessage(), 'status' => false, 'data' => $e->getTrace()];
  517. }
  518. }
  519. /**
  520. * LALA 确认交易
  521. * @param $txID
  522. * @return array
  523. */
  524. public static function LALAGetTransfer($txID)
  525. {
  526. try {
  527. $uri = 'https://api.trongrid.io';
  528. $api = new Api(new Client(['base_uri' => $uri]));
  529. $config = [
  530. 'contract_address' => 'TLuwWZ8MkB6FFJDLYc19u7d4tFq2GV3rPL',// LALA TRC20
  531. 'decimals' => 8,
  532. ];
  533. $trxWallet = new TRC20($api, $config);
  534. $transfer = $trxWallet->transactionReceipt($txID);
  535. return ['msg' => 'success', 'data' => $transfer, 'status' => true];
  536. } catch (\Exception $e) {
  537. return ['msg' => $e->getMessage(), 'status' => false, 'data' => $e->getTrace()];
  538. }
  539. }
  540. /**
  541. * LALA 归集
  542. * @param $money_info
  543. * @param $summary_address
  544. * @param $gas_address
  545. * @return bool
  546. * @throws TronErrorException
  547. */
  548. public static function LALASummary($money_info, $summary_address, $gas_address): bool
  549. {
  550. $uri = 'https://api.trongrid.io';
  551. $api = new Api(new Client(['base_uri' => $uri]));
  552. $config = [
  553. 'contract_address' => 'TLuwWZ8MkB6FFJDLYc19u7d4tFq2GV3rPL',// LALA TRC20
  554. 'decimals' => 8,
  555. ];
  556. $trxWallet = new TRC20($api, $config);
  557. $address = new Address($money_info['address'], $money_info['privateKey'], $money_info['hexAddress']);
  558. $balance = $trxWallet->balance($address);
  559. //var_dump($balance);
  560. if ($balance > 0) {
  561. $trx = new TRX($api);
  562. $trx_balance = $trx->balance($address);
  563. //var_dump($trx_balance);
  564. if ($trx_balance < sys_config('trx_gas', 10)) {
  565. $trade_trx = ceil(10 - $trx_balance);
  566. //执行手续费转账
  567. $gas_trx_balance = $trx->balance($gas_address);
  568. if ($gas_trx_balance < $trade_trx + 2) {
  569. Log::error('LALA手续费账户余额不足');
  570. return false;
  571. }
  572. try {
  573. $res = $trx->transfer($gas_address, $address, $trade_trx);
  574. // var_dump($res);
  575. if (isset($res->txID)) {
  576. Log::error('转账TRX交易哈希:' . $res->txID);
  577. return true;
  578. } else {
  579. Log::error('转账TRX失败');
  580. return false;
  581. }
  582. } catch (\Exception $e) {
  583. Log::error('转账TRX失败' . $e->getMessage());
  584. return false;
  585. }
  586. } else {
  587. //执行转账
  588. try {
  589. $res = self::LALATransfer($address, new Address($summary_address), $balance);
  590. // var_dump($res);
  591. if ($res['status']) {
  592. Log::error('转账交易哈希:' . $res['data']->txID);
  593. return true;
  594. } else {
  595. Log::error('转账失败' . $res['msg']);
  596. return false;
  597. }
  598. } catch (\Exception $e) {
  599. Log::error('转账失败' . $e->getMessage());
  600. return false;
  601. }
  602. }
  603. }
  604. return true;
  605. }
  606. //TODO LALA - end
  607. }