BroadcastServer.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. <?php
  2. namespace App\Servers;
  3. use App\Models\Broadcast;
  4. use App\Models\CoinTypes;
  5. use App\Models\Config;
  6. use App\Models\MemberCoins;
  7. use App\Models\Withdraw;
  8. use App\Servers\Eth\Utils;
  9. use Illuminate\Support\Facades\Log;
  10. use Web3p\EthereumTx\Transaction;
  11. /**
  12. * 广播接口
  13. */
  14. class BroadcastServer
  15. {
  16. public static $type_str=[
  17. 1=>'BTC充值',
  18. 2=>'ETH充值',
  19. 3=>'USDT充值',
  20. 4=>'BTC提现',
  21. 5=>'ETH提现',
  22. 6=>'USDT提现',
  23. 7=>'ETH手续费',
  24. ];
  25. /**
  26. * @param $eth_address ## 用户钱包地址
  27. * @return bool|mixed|string|null
  28. *
  29. * 查询用户NYTR余额
  30. */
  31. public static function getUsdtNum($eth_address)
  32. {
  33. $eth = EthereumRPCServer::getInstance();
  34. $tx = [];
  35. $tx['from'] = $eth_address; //用户地址
  36. // $tx['to'] = config('muc.hy_address', ''); //系统钱包地址
  37. $tx['to'] =Config::where('key','hy_address')->value('value'); //系统钱包地址
  38. $tx['data'] = Utils::decodeSolMethod('balanceOf(address)', [$eth_address]);
  39. $balance = $eth->call($tx);
  40. $balance = Utils::hex2dec($balance);
  41. $balance = Utils::int2fund($balance,6); // 18位小数
  42. $balance = Utils::fund2int($balance, 2);
  43. $balance = round($balance / 100, 2);
  44. return $balance;
  45. }
  46. /**
  47. * 获取ETH余额
  48. * @param $eth_address ##用户钱包地址
  49. * @return bool|mixed|string
  50. */
  51. public static function getEthNum($eth_address)
  52. {
  53. $eth = EthereumRPCServer::getInstance();
  54. $eth_num = $eth->getBalance($eth_address);
  55. $eth_num = Utils::hex2dec($eth_num);
  56. $eth_num = Utils::int2fund($eth_num, 18);
  57. return $eth_num;
  58. }
  59. /**
  60. * 获取BTC数量
  61. * @param $btc_address
  62. * @return string
  63. */
  64. public static function getBtcNum($btc_address)
  65. {
  66. $btc = BtcereumRPCServer::getInstance();
  67. $btc_num = $btc->bitcoinBalanceOfAddress($btc_address);
  68. return $btc_num;
  69. }
  70. public static function addBroadcast($coin_id, $money, $from, $from_key, $to, $member_id, $order_id = 0, $status_type = 0, $from_id = 0, $status = 1)
  71. {
  72. //保留数量
  73. $retain_num = CoinTypes::where('id', $coin_id)->value('retain_num');
  74. $transaction = [
  75. 'depend' => 0,
  76. 'from' => $from,
  77. 'to' => $to,
  78. 'nonce' => '',
  79. 'coin_id' => $coin_id,
  80. 'value' => '0x0',
  81. 'data' => !empty($tx['data']) ? $tx['data'] : Utils::fixHex(bin2hex('NYT')),
  82. 'chain_id' => 0,
  83. 'gas_price' => '',
  84. 'gas_limit' => '',
  85. 'to_address' => $to,
  86. 'sign' => '',
  87. 'hash' => $status == 5 ? '系统内部转账,直接成功,无需链上广播' : '',
  88. 'from_key' => $from_key,
  89. 'money' =>$coin_id!=4? round($money - $retain_num,6):$money,
  90. 'status' => $status,
  91. 'type' => $status_type,
  92. 'm_id' => $member_id,
  93. 'order_id' => $order_id,
  94. // 'from_id' => $from_id,
  95. 'retain_num' => $retain_num,
  96. 'send_money' => $money,
  97. ];
  98. if ($coin_id == 1) {
  99. // $btc = BtcereumRPCServer::getInstance();
  100. // $feeAddressUXTO = $btc->listunspent(0, 999999, [$from]);
  101. // if (empty($feeAddressUXTO)) {
  102. // $transaction['error'] = "手续费地址uxto为空";
  103. // $transaction['status'] = 4;
  104. // } else {
  105. // $inputs = [];
  106. // foreach ($feeAddressUXTO as $item) {
  107. // $inputs[] = [
  108. // 'txid' => $item['txid'],
  109. // 'vout' => $item['vout'],
  110. // ];
  111. // }
  112. // $output = [$to => $transaction['money']];
  113. // $rawtx = $btc->createrawtransaction($inputs, $output);
  114. // if ($rawtx === false) {
  115. // $transaction['error'] = "创建裸交易失败";
  116. // $transaction['status'] = 4;
  117. // } else {
  118. // $transaction['data'] = $rawtx;
  119. // }
  120. // }
  121. } else {
  122. $eth = EthereumRPCServer::getInstance();
  123. $transaction['chain_id'] = $eth->chainId;
  124. $depend = 0;
  125. if ($coin_id == 4) {
  126. $hy_address=Config::where('key','hy_address')->value('value');
  127. $tx = static::build(
  128. $from,
  129. $hy_address,//系统钱包地址
  130. '0x0',
  131. Utils::decodeSolMethod(
  132. 'transfer(address,uint256)',
  133. [$to, Utils::fund2int($money, 6)]
  134. // [$to, Utils::fund2int($money, 18)]
  135. )
  136. );
  137. $transaction['from'] = $tx['from'];
  138. $transaction['to'] = $tx['to'];
  139. $transaction['data'] = $tx['data'];
  140. $my_eth = static::getEthNum($tx['from']);
  141. if ($my_eth * 1 < 0.002) {
  142. $depend = static::withholdServiceCharge($tx, $member_id);
  143. }
  144. }else{
  145. $transaction['value'] =Utils::dec2hex(Utils::fund2int($transaction['money'] * 1)); // 转账金额
  146. $transaction['data'] = '0x0';
  147. }
  148. $transaction['depend'] = $depend;
  149. // $nonce = $eth->getTransactionCount($transaction['from']);
  150. // $transaction['nonce'] = $nonce;//交易顺序十六进制。由eth_getTransactionCount获取
  151. // $gas_price = $eth->getGasPrice();
  152. // $transaction['gas_price'] = Utils::dec2hex(Utils::toWei("1gwei") + Utils::hex2dec($gas_price));//燃料十六进制。由eth_estimateGas获取
  153. // $gas_limit = static::getGasLimit($transaction);
  154. // $transaction['gas_limit'] = $gas_limit;
  155. //写入数据库代码 $transaction
  156. }
  157. $transaction = Broadcast::create($transaction);
  158. // //写入 返回成功
  159. return $transaction->{'id'};
  160. }
  161. /**
  162. * 导入广播
  163. * @param $from
  164. * @param $to
  165. * @param $nonce
  166. * @param $value
  167. * @param $data
  168. * @param $chain_id
  169. * @param $gas_price
  170. * @param $gas_limit
  171. * @param $sign
  172. * @param $hash
  173. * @param $money
  174. * @param $order_id
  175. * @param $coin_id
  176. * @param $status
  177. * @return mixed
  178. */
  179. public static function addSysBroadcast($from, $to, $nonce, $value, $data, $chain_id, $gas_price, $gas_limit, $sign, $hash, $money, $order_id, $coin_id, $status)
  180. {
  181. $withdraws = Withdraw::where('id', $order_id)->first();
  182. if (empty($withdraws)) {
  183. Log::info('导入提现信息不存在,提现ID:' . $order_id);
  184. return false;
  185. }
  186. if($withdraws->{'status'}!=1)return false;
  187. $transaction_id = Broadcast::where('order_id', $order_id)->where('type', '2')->value('id');
  188. //保留数量
  189. $transaction = [
  190. 'depend' => 0,
  191. 'from' => $from,
  192. 'to' => $to,
  193. 'nonce' => $nonce,
  194. 'coin_id' => $coin_id,
  195. 'value' => $value,
  196. 'data' => $data,
  197. 'chain_id' => $chain_id,
  198. 'gas_price' => $gas_price,
  199. 'gas_limit' => $gas_limit,
  200. 'sign' => $sign,
  201. 'hash' => $hash,
  202. 'from_key' => '',
  203. 'money' => $money,
  204. 'status' => 3,
  205. 'type' => $status,
  206. 'm_id' => $withdraws->{'m_id'},
  207. 'order_id' => $order_id,
  208. 'created_at' => date('Y-m-d H:i:s'),
  209. 'updated_at' => date('Y-m-d H:i:s'),
  210. ];
  211. if (empty($transaction_id)) {
  212. $transaction_id = Broadcast::insertGetId($transaction);
  213. } else {
  214. Broadcast::where('id', $transaction_id)->update($transaction);
  215. }
  216. Withdraw::where('id', $order_id)->update(['updated_at' => date('Y-m-d H:i:s'),'hash'=>$hash, 'status' => '2']);
  217. // //写入 返回成功
  218. return $transaction_id;
  219. }
  220. /**
  221. * 广播签名
  222. * @return bool
  223. */
  224. public static function signBroadcast()
  225. {
  226. $btc = BtcereumRPCServer::getInstance();
  227. $list = Broadcast::where('status', '1')->select(['id', 'depend', 'chain_id', 'm_id', 'from', 'to', 'nonce', 'value', 'data', 'gas_price', 'gas_limit', 'sign', 'money', 'from_key', 'coin_id'])->groupBy('m_id')->limit(10)->get()->toArray();
  228. // $service_money='4000000000000000';
  229. // 手续费交易 转账0.0015个
  230. // $serviceFee = Utils::dec2hex($service_money); // 手续费
  231. if (empty($list)) return false;
  232. $sys_wei=Config::where('key','service_charge')->value('value');
  233. $sys_wei=empty($sys_wei)?'15':$sys_wei;
  234. $eth = EthereumRPCServer::getInstance();
  235. foreach ($list as $value) {
  236. $update_info = [];
  237. $from_key = PassServer::getSecretKey($value['from_key'], $value['m_id']);
  238. $send_num = Broadcast::whereIn('status', ['2'])->where('from', $value['from'])->count();
  239. if ($send_num > 0) continue;
  240. if ($value['depend']) {
  241. $num = Broadcast::where('id', $value['depend'])->where('status', 5)->where('updated_at','<',date('Y-m-d H:i:s',time()-15))->count();
  242. if ($num <= 0) continue;
  243. }
  244. if ($value['coin_id'] == 1) {
  245. $feeAddressUXTO = $btc->listunspent(0, 999999, [$value['from']]);
  246. if (empty($feeAddressUXTO)) {
  247. $update_info['error'] = "手续费地址uxto为空";
  248. $update_info['status'] = 4;
  249. } else {
  250. $inputs = [];
  251. foreach ($feeAddressUXTO as $item) {
  252. $inputs[] = [
  253. 'txid' => $item['txid'],
  254. 'vout' => $item['vout'],
  255. ];
  256. }
  257. $output = [$value['to'] => $value['money']*1];
  258. $rawtx = $btc->createrawtransaction($inputs, $output);
  259. if ($rawtx === false) {
  260. $update_info['error'] = "创建裸交易失败";
  261. $update_info['status'] = 4;
  262. } else {
  263. $value['data'] = $rawtx;
  264. $privKeys[] = $from_key;
  265. $sign = $btc->signrawtransactionwithkey($value['data'], $privKeys,null);
  266. if ($sign === false) {
  267. $update_info['error'] = '签名失败';
  268. $update_info['status'] = 5;
  269. } else {
  270. $update_info['sign'] = $sign['hex'];
  271. $update_info['status'] = 2;
  272. }
  273. }
  274. }
  275. } else {
  276. if($value['coin_id']==4){
  277. $my_eth = static::getEthNum($value['from']);
  278. //dd( $my_eth);
  279. if ($my_eth * 1 < 0.005) {
  280. $depend = static::withholdServiceCharge($value, $value['m_id']);
  281. Broadcast::where('id', $value['id'])->update(['depend' => $depend]);
  282. continue;
  283. }
  284. }else{
  285. // $value['value'] =Utils::dec2hex(Utils::fund2int($value['money'] * 1)); // 转账金额
  286. }
  287. if(empty($value['nonce']) || true){
  288. $nonce = $eth->getTransactionCount($value['from']);
  289. //交易顺序十六进制。由eth_getTransactionCount获取
  290. $nonce_count = Broadcast::where('nonce', $nonce)->where('from',$value['from'])->where('id','<>', $value['id'])->count();
  291. if ($nonce_count >= 1) {
  292. continue;
  293. // $nonce=Utils::dec2hex(Utils::hex2dec($nonce) + 1);
  294. }
  295. $value['nonce'] = $nonce;
  296. }
  297. $gas_price = $eth->getGasPrice();
  298. $value['gas_price'] = Utils::dec2hex(Utils::toWei($sys_wei."gwei") + Utils::hex2dec($gas_price));//燃料十六进制。由eth_estimateGas获取
  299. // $value['gas_price'] = $gas_price;//燃料十六进制。由eth_estimateGas获取
  300. $gas_limit = static::getGasLimit($value);
  301. $value['gas_limit'] = $gas_limit;
  302. $sign = static::sign($value, $from_key); //交易签名
  303. if ($sign === false) {
  304. $update_info['error'] = '签名失败';
  305. $update_info['status'] = 4;
  306. } else {
  307. $update_info['sign'] = $sign;
  308. $update_info['status'] = 2;
  309. $update_info['nonce'] = $value['nonce'];
  310. $update_info['gas_limit'] = $value['gas_limit'];
  311. $update_info['gas_price'] = $value['gas_price'];
  312. }
  313. }
  314. Broadcast::where('id', $value['id'])->update($update_info);
  315. sleep(5);
  316. }
  317. return true;
  318. }
  319. /**
  320. * 广播交易
  321. * @return bool
  322. */
  323. public static function sendBroadcast()
  324. {
  325. $btc = BtcereumRPCServer::getInstance();
  326. $eth = EthereumRPCServer::getInstance();
  327. $list = Broadcast::where('status', '2')->select(['id', 'm_id', 'from', 'to', 'nonce', 'value', 'data', 'gas_price', 'gas_limit', 'sign', 'money', 'from_key', 'coin_id'])->limit(10)->get()->toArray();
  328. if (empty($list)) return false;
  329. foreach ($list as $value) {
  330. $update_info = [];
  331. if ($value['coin_id'] == 1) {
  332. $txid = $btc->sendrawtransaction($value['sign']);
  333. if ($txid == false) {
  334. $update_info['error'] = '广播失败';
  335. $update_info['status'] = 4;
  336. } else {
  337. $update_info['hash'] = $txid;
  338. $update_info['status'] = 3;
  339. }
  340. } else {
  341. $hash = $eth->sendRawTransaction($value['sign'], $error);//广播交易
  342. if ($hash == false) {
  343. $update_info['error'] = json_encode($error);
  344. $update_info['status'] = 4;
  345. } else {
  346. $update_info['hash'] = $hash;
  347. $update_info['status'] = 3;
  348. }
  349. }
  350. Broadcast::where('id', $value['id'])->update($update_info);
  351. }
  352. return true;
  353. }
  354. /**
  355. * 广播查询
  356. */
  357. public static function getBroadcast(){
  358. $btc = BtcereumRPCServer::getInstance();
  359. $eth = EthereumRPCServer::getInstance();
  360. $list = Broadcast::where('status', '3')->select(['id', 'm_id', 'from', 'to', 'send_money', 'value', 'coin_id', 'type', 'sign', 'money', 'from_key', 'coin_id','hash','send_money','order_id'])->limit(10)->get()->toArray();
  361. if (empty($list)) return false;
  362. foreach ($list as $value) {
  363. $update_info = [];
  364. if ($value['coin_id'] == 1) {
  365. $deal_info=$btc->getrawtransaction ($value['hash'],true);
  366. if(empty($deal_info['confirmations']) || $deal_info['confirmations'] <=3){
  367. continue;
  368. }else{
  369. $update_info['success'] = json_encode($deal_info);
  370. $update_info['status'] = 5;
  371. }
  372. } else {
  373. $receipt = $eth->getTransactionReceipt($value['hash']);//交易查询
  374. if (empty($receipt)) {
  375. //交易正在广播中,下次继续查询
  376. continue;
  377. } else {
  378. //交易已完成 返回数据格式数据 ,将数据json保存
  379. if ($receipt['status'] != '0x1') {
  380. $update_info['error'] = json_encode($receipt);
  381. $update_info['status'] = 4;
  382. }else{
  383. $update_info['success'] = json_encode($receipt);
  384. $update_info['status'] = 5;
  385. }
  386. }
  387. }
  388. if(!empty($update_info)){
  389. Broadcast::where('id', $value['id'])->update($update_info);
  390. if($update_info['status']==5){
  391. //广播成功,增加会员余额
  392. if($value['type']<=3){
  393. $member_coin= MemberCoins::where('m_id',$value['m_id'])->where('coin_id',$value['coin_id'])->select(['id','coin_name','coin_id','num','m_id'])->first();
  394. MemberCoins::where('id',$member_coin->{'id'})->update(['num'=>$member_coin->{'num'}+$value['send_money']]);
  395. MoneyDetailServer::write($member_coin['coin_id'], MoneyDetailServer::$status['coin_recharge'], $value['send_money'], MoneyDetailServer::$add_reduce['add'], $member_coin->{'m_id'}, $member_coin->{'mobile'}, '充币' .$value['send_money'] .''. $member_coin->{'coin_name'}, '', $value['id'], $member_coin->{'num'}, 'Recharge ' . $member_coin->{'money'} . $member_coin->{'coin_name'});
  396. }elseif($value['type']<=6){
  397. $withdraws = Withdraw::where('id', $value['order_id'])->where('m_id', $value['m_id'])->first();
  398. if (!empty($withdraws)) {
  399. Withdraw::where('id', $value['order_id'])->update([ 'status' => '3']);
  400. $member_coin = MemberCoins::where('m_id', $value['m_id'])->where('coin_id', $value['coin_id'])->select(['id', 'coin_name', 'coin_id', 'num', 'm_id','lock_num'])->first();
  401. $lock_num = $member_coin->{'lock_num'} - $withdraws->{'money'};
  402. if ($lock_num < 0) $lock_num = 0;
  403. MemberCoins::where('id', $member_coin->{'id'})->update(['lock_num' => $lock_num]);
  404. }
  405. }
  406. }
  407. }
  408. }
  409. return true;
  410. }
  411. /**
  412. * 系统手续费转账
  413. * @param $tx
  414. * @param $member_id
  415. * @return mixed
  416. */
  417. public static function withholdServiceCharge($tx, $member_id)
  418. {
  419. $eth = EthereumRPCServer::getInstance();
  420. $gasPrice = $eth->getGasPrice(); // 手续费
  421. $cb_address = Config::where('key', 'cb_address')->value('value');
  422. $address_key = Config::where('key', 'address_key')->value('value');
  423. $sys_wei=Config::where('key','service_charge')->value('value');
  424. $service_money=Config::where('key','service_money')->value('value');
  425. $sys_wei=empty($sys_wei)?'15':$sys_wei;
  426. $service_money=empty($service_money)?'3000000000000000':$service_money;
  427. $tx = [
  428. 'from' => $cb_address,
  429. 'to' => $tx['from'],
  430. 'value' => '0x0',
  431. 'data' => $tx['data'],
  432. ];
  433. $gasLimit = $eth->estimateGas($tx);
  434. // 手续费交易 转账0.0015个
  435. $serviceFee = Utils::dec2hex($service_money); // 手续费
  436. // 广播手续费
  437. $transaction = [
  438. 'depend' => 0,
  439. 'from' => $cb_address,
  440. 'to' => $tx['to'],
  441. 'nonce' => '',
  442. 'value' => $serviceFee,//转账0.004个
  443. 'data' => '0x0',
  444. 'money' => round($service_money/1000000000000000000,6),
  445. 'gas_limit' => $gasLimit,
  446. 'chain_id' => $eth->chainId,
  447. 'gas_price' => '',
  448. 'to_address' => $tx['to'],
  449. 'coin_id' => 2,
  450. 'sign' => '',
  451. 'hash' => '',
  452. 'from_key' => $address_key,//系统支付秘钥
  453. 'status' => 1,//系统支付秘钥
  454. 'type' => 7,//系统支付秘钥
  455. 'm_id' => 0,
  456. 'send_money' => round($service_money/1000000000000000000,6),
  457. ];
  458. $nonce = $eth->getTransactionCount($transaction['from']);
  459. $transaction['nonce'] = $nonce;//交易顺序十六进制。由eth_getTransactionCount获取
  460. $transaction['gas_price'] = Utils::dec2hex(Utils::toWei($sys_wei."gwei") + Utils::hex2dec($gasPrice));//燃料十六进制。由eth_estimateGas获取
  461. $transaction['gas_limit'] = $gasLimit;
  462. //写入数据库代码 $transaction
  463. $nytr = Broadcast::create($transaction);
  464. return $nytr->{'id'};
  465. }
  466. /**
  467. * 转账内容拼接
  468. * @param $from
  469. * @param $to
  470. * @param $value
  471. * @param $data
  472. * @return array
  473. * @throws
  474. */
  475. protected static function build($from, $to, $value, $data)
  476. {
  477. $tx = [];
  478. $tx['from'] = $from;
  479. $tx['to'] = $to;
  480. $tx['value'] = $value;
  481. $tx['data'] = $data;
  482. return $tx;
  483. }
  484. /**
  485. * @param $transaction
  486. * @return string
  487. */
  488. public static function getGasLimit($transaction)
  489. {
  490. $tx = [
  491. 'from' => $transaction['from'],
  492. 'to' => $transaction['to'],
  493. 'nonce' => $transaction['nonce'],
  494. 'value' => $transaction['value'],
  495. 'data' => $transaction['data'],
  496. 'chainId' => 1,
  497. 'gasPrice' => $transaction['gas_price'],
  498. ];
  499. $eth = EthereumRPCServer::getInstance();
  500. $gasLimit = $eth->estimateGas($tx);
  501. if (!$gasLimit) {
  502. $gasLimit = 10 * 10000; // 预估失败, 最高10万
  503. }
  504. return $gasLimit;
  505. }
  506. protected static function sign($transaction, $key)
  507. {
  508. $tx = [
  509. 'from' => $transaction['from'],
  510. 'to' => $transaction['to'],
  511. 'nonce' => $transaction['nonce'],
  512. 'value' => $transaction['value'],
  513. 'data' => $transaction['data'],
  514. 'chainId' => $transaction['chain_id'],
  515. 'gasPrice' => $transaction['gas_price'],
  516. 'gasLimit' => $transaction['gas_limit'],
  517. ];
  518. // 数据签名
  519. $tr = new Transaction($tx);
  520. $tr->sign($key);
  521. return Utils::fixHex($tr->serialize()->toString('hex'));
  522. }
  523. }