SmartContract.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. namespace qiniu\services\blockchain\bsc;
  3. use Exception;
  4. use qiniu\services\blockchain\bsc\src\Contract;
  5. use qiniu\services\blockchain\bsc\src\Utils;
  6. class SmartContract extends Contract
  7. {
  8. protected $web3;
  9. //context
  10. protected $gasPrice;
  11. protected $gasLimit;
  12. protected $value;
  13. protected $credential;
  14. protected $txMethods = [];
  15. protected $viewMethods = [];
  16. protected $eventSignatures = [];
  17. public function __construct($web3, $credential, $abi)
  18. {
  19. parent::__construct($web3->provider, $abi);
  20. $this->web3 = $web3;
  21. $this->credential = $credential;
  22. foreach ($this->events as $name => $event) {
  23. $sig = $this->ethabi->encodeEventSignature($event);
  24. $this->eventSignatures[$sig] = $event;
  25. }
  26. foreach ($this->functions as $function) {
  27. if ($function['stateMutability'] == 'view') {
  28. $this->viewMethods[] = $function['name'];
  29. } else {
  30. $this->txMethods[] = $function['name'];
  31. }
  32. }
  33. }
  34. public function setGasPrice($price)
  35. {
  36. if (!$price) {
  37. unset($this->gasPrice);
  38. } else {
  39. $this->gasPrice = $price;
  40. }
  41. return $this;
  42. }
  43. public function setGasLimit($limit)
  44. {
  45. if (!$limit) {
  46. unset($this->gasLimit);
  47. } else {
  48. $this->gasLimit = $limit;
  49. }
  50. return $this;
  51. }
  52. public function setValue($value)
  53. {
  54. if (!$value) {
  55. unset($this->value);
  56. } else {
  57. $this->value = $value;
  58. }
  59. return $this;
  60. }
  61. public function setCredential($credential)
  62. {
  63. $this->credential = $credential;
  64. return $this;
  65. }
  66. protected function transact($tx): string
  67. {
  68. if (!isset($this->credential)) {
  69. throw new \Exception('credential not set');
  70. }
  71. $transactor = new Transactor($this->web3, $this->credential);
  72. $tx['to'] = $this->getToAddress();
  73. return $transactor->transact($tx);
  74. }
  75. protected function isTxMethod($name)
  76. {
  77. return in_array($name, $this->txMethods);
  78. }
  79. protected function isViewMethod($name)
  80. {
  81. return in_array($name, $this->viewMethods);
  82. }
  83. public function instantiate()
  84. {
  85. $args = func_get_args();
  86. $data = $this->getData(...$args);
  87. $tx = [
  88. 'data' => '0x' . $data
  89. ];
  90. return $this->transact($tx);
  91. }
  92. public function __call($name, $args)
  93. {
  94. if ($this->isTxMethod($name)) {
  95. $data = $this->getData($name, ...$args);
  96. $tx = [
  97. 'data' => '0x' . $data
  98. ];
  99. return $this->transact($tx);
  100. }
  101. if ($this->isViewMethod($name)) {
  102. $cb = new Callback;
  103. $args[] = $cb;
  104. $this->call($name, ...$args);
  105. $values = array_values($cb->result);
  106. return count($values) > 1 ? $values : $values[0];
  107. }
  108. throw new Exception('method not supported');
  109. }
  110. public function queryEvents($topics = [], $fromBlock = 'latest', $toBlock = 'latest')
  111. {
  112. if (is_numeric($fromBlock)) {
  113. $fromBlock = Utils::toHex($fromBlock, true);
  114. }
  115. if (is_numeric($toBlock)) {
  116. $toBlock = Utils::toHex($toBlock, true);
  117. }
  118. $filter = [
  119. 'fromBlock' => $fromBlock,
  120. 'toBlock' => $toBlock,
  121. 'address' => $this->getToAddress(),
  122. 'topics' => $topics
  123. ];
  124. $cb = new Callback;
  125. $this->web3->eth->getLogs($filter, $cb);
  126. $decodedEvents = [];
  127. foreach ($cb->result as $log) {
  128. if (!array_key_exists($log->topics[0], $this->eventSignatures)) {
  129. $msg = sprintf("unknown event: %s, skip", $log->topics[0]);
  130. echo $msg . PHP_EOL;
  131. continue;
  132. }
  133. $eventAbi = $this->eventSignatures[$log->topics[0]];
  134. $types = [];
  135. $names = [];
  136. for ($i = 0; $i < count($eventAbi['inputs']); $i++) {
  137. $types[] = $eventAbi['inputs'][$i]['type'];
  138. $names[] = $eventAbi['inputs'][$i]['name'];
  139. }
  140. $params = '';
  141. for ($i = 1; $i < count($log->topics); $i++) {
  142. $params = $params . Utils::stripZero($log->topics[$i]);
  143. }
  144. $params = $params . Utils::stripZero($log->data);
  145. //echo $eventAbi['name'] . ' => ' . $params . PHP_EOL;
  146. $decodedParams = $this->ethabi->decodeParameters($types, $params);
  147. $decodedEvent = (object)[
  148. 'blockHash' => $log->blockHash,
  149. 'blockNumber' => $log->blockNumber,
  150. 'transactionHash' => $log->transactionHash,
  151. 'removed' => $log->removed,
  152. 'address' => $log->address,
  153. 'name' => $eventAbi['name'],
  154. 'params' => []
  155. ];
  156. for ($i = 0; $i < count($names); $i++) {
  157. $decodedEvent->params[$names[$i]] = $decodedParams[$i];
  158. }
  159. $decodedEvents[] = $decodedEvent;
  160. }
  161. return $decodedEvents;
  162. }
  163. }