Web3Service.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. <?php
  2. namespace blockchain;
  3. use BI\BigInteger;
  4. use blockchain\web3\Bep20;
  5. use blockchain\web3\Bep721;
  6. use blockchain\web3\Callback;
  7. use blockchain\web3\Credential;
  8. use blockchain\web3\Kit;
  9. use blockchain\web3\NodeClient;
  10. use think\Config;
  11. use think\Exception;
  12. use Web3p\EthereumUtil\Util;
  13. class Web3Service
  14. {
  15. /** @var Bep20|Bep721 $token */
  16. private static $token;
  17. private static $decimal;
  18. private static $address;
  19. private static $kit;
  20. private static $blockTimeArea;
  21. public function __construct($chain, $contact, $decimal, $type, $private_key)
  22. {
  23. $node = NodeClient::create($chain, 'mainNet');
  24. $credential = Credential::fromKey($private_key);
  25. self::$kit = new Kit($node, $credential);
  26. self::$address = $credential->getAddress();
  27. self::$token = self::$kit->$type($contact);
  28. self::$decimal = $decimal;
  29. self::$blockTimeArea = Config::get('blockchain.' . $chain)['blockTimeArea'] ?? 1;
  30. }
  31. /**
  32. * @param $token
  33. * @return Web3Service
  34. * @throws Exception
  35. */
  36. public static function instance($chain, $token, $private_key = ''): Web3Service
  37. {
  38. if ($chain != 'bsc' && $chain != 'eth') {
  39. throw new Exception('暂不支持该链');
  40. }
  41. if (!get_token_info($chain, $token, 'address')) {
  42. throw new Exception('未定义Token');
  43. }
  44. return new self($chain, get_token_info($chain, $token, 'address'), get_token_info($chain, $token, 'decimal'), get_token_info($chain, $token, 'type'), $private_key ?: get_token_info($chain, $token, 'private_key'));
  45. }
  46. public function createAddress()
  47. {
  48. $address = Credential::create();
  49. return [
  50. 'address' => $address->getAddress(),
  51. 'private_key' => $address->getPrivateKey()
  52. ];
  53. }
  54. /**
  55. * 签名转地址
  56. * @param $str
  57. * @param $sign
  58. * @return string
  59. * @throws Exception
  60. */
  61. public function signToAddress($str, $sign)
  62. {
  63. $util = new Util();
  64. if (strlen($sign) != 132) {
  65. throw new Exception('签名格式错误,类型:001');
  66. }
  67. if (!$util->isHex($sign)) {
  68. throw new Exception('签名格式错误,类型:002');
  69. }
  70. $sign_str = $util->stripZero($sign);
  71. $r = substr($sign_str, 0, 64);
  72. $s = substr($sign_str, 64, 64);
  73. $v = substr($sign_str, 128, 2);
  74. $message = $util->hashPersonalMessage($str);
  75. $public_key = $util->recoverPublicKey($message, $r, $s, hexdec($v) - 27);
  76. return strtolower($util->publicKeyToAddress($public_key));
  77. }
  78. /**
  79. * 获取地址
  80. * @return string
  81. */
  82. public function getAddress()
  83. {
  84. return self::$address;
  85. }
  86. public function getBalance($address)
  87. {
  88. return bcdiv(self::$kit->balanceOf($address)->toString(), bcpow(10, 18), 18);
  89. }
  90. public function getTokenBalance($address)
  91. {
  92. return bcdiv(self::$token->balanceOf($address)->toString(), bcpow(10, self::$decimal), self::$decimal);
  93. }
  94. public function getLastTransfer($from = [], $to = [], $last_time = 0)
  95. {
  96. if ($last_time <= 0) $last_time = time() - 5000;
  97. $time_area = bcsub(time(), $last_time);
  98. $num = bcdiv($time_area, self::$blockTimeArea, 0);
  99. if ($num <= 0) $num = 0;
  100. if ($num > 10000) $num = 10000;
  101. return self::$token->getTransferEvents($from, $to, $num ? bcsub(self::$token->getBlockNumber(), $num) : 'latest');
  102. }
  103. public function getBlockTransfer($block, $transferHash)
  104. {
  105. $res = self::$token->getTransferEvents([], [], $block, $block);
  106. $ress = [];
  107. foreach ($res as $v) {
  108. if ($v->transactionHash == $transferHash) {
  109. $ress[] = $v;
  110. }
  111. }
  112. return $ress;
  113. }
  114. public function getTransferInfo($tx_hash)
  115. {
  116. return self::$token->getTransactionReceipt($tx_hash);
  117. }
  118. public function transferToken($to, $amount)
  119. {
  120. return self::$token->transfer($to, bcmul($amount, bcpow(10, self::$decimal)));
  121. }
  122. public function transfer($to, $amount)
  123. {
  124. $num = bcmul($amount, bcpow(10, 18));
  125. $num = '0x' . (new BigInteger($num))->toHex();
  126. return self::$kit->transfer($to, $num);
  127. }
  128. public function getBNBTransferInfo($tx_hash)
  129. {
  130. return self::$token->getTransactionReceipt($tx_hash);
  131. }
  132. public function estimateTransferGas($to, $amount)
  133. {
  134. $cb = new Callback();
  135. self::$token->estimateGas("transfer", self::$address, $to, bcmul($amount, bcpow(10, self::$decimal)), $cb);
  136. return $cb->result->toString();
  137. }
  138. public function getData($name, ...$args)
  139. {
  140. $data = self::$token->getData($name, ...$args);
  141. $tx = [
  142. 'data' => '0x' . $data,
  143. 'to' => self::$token->getToAddress()
  144. ];
  145. return $tx;
  146. }
  147. public function totalSupply()
  148. {
  149. $data = self::$token->totalSupply();
  150. $value = $data->toString();
  151. return bcdiv($value, bcpow(10, self::$decimal), self::$decimal);
  152. }
  153. }