Ethereum PHP
PHP interface to Ethereum JSON-RPC API.
Abi.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Ethereum;
4 
8 
9 class Abi extends EthereumStatic
10 {
11 
15  private $abi;
16 
17 
22  public function __construct(array $abi)
23  {
24  $this->abi = $abi;
25  }
26 
27 
36  public function encodeFunction(string $methodName, array $values)
37  {
38  $m = $this->getParamDefinition($methodName);
39 
40  if (count($m->inputs) !== count($values)) {
41  throw new \InvalidArgumentException('Expected ' . count($m->inputs) . ' params but got ' . count($values));
42  }
43 
44  // [METHOD 4bytes] + [PARAMS]
45  $params = $this->getSignature($m);
46  foreach ($values as $i => $val) {
47  $expectedType = $m->inputs[$i]->type;
48  $validAbiType = self::convertByAbi($expectedType, $val);
49  $params .= EthereumStatic::removeHexPrefix($validAbiType->encodedHexVal());
50  }
51  return new EthD($params);
52  }
53 
54 
65  public function decodeMethod(string $method, EthD $rawReturn)
66  {
67  $params = $this->getParamDefinition($method);
68 
69  $return = $this->decode($params->outputs, self::removeHexPrefix($rawReturn->hexVal()));
70 
71  // Only return array if we expect multiple params.
72  if (count($return) === 1) {
73  $return = $return[0];
74  }
75  return $return;
76  }
77 
78 
90  public static function decode(array $params, string $msgData)
91  {
92  if (self::hasHexPrefix($msgData) || !ctype_xdigit($msgData)) {
93  throw new \Exception('msgData must be a unprefixed hex value.');
94  }
95  $return = [];
96  $pos = 0;
97 
98  foreach ($params as $p => $param) {
99 
101  $class = EthD::getClassByAbi($param->type);
102 
103  $lengthType = $class::getdataLengthType($param->type);
104 
105  if ($lengthType === 'static') {
106  // Fixed length type.
107  $thisValue = substr($msgData, $pos, 64);
108  $return[$p] = new $class(self::ensureHexPrefix($thisValue), ['abi' => $param->type]);
109  }
110  elseif ($lengthType === 'dynamic') {
111  // Dynamic length type.
112  $offsetInChars = 2 * Rlp::getByteValueAtOffsetPos($msgData, $pos);
113  $rlpDecoded = Rlp::decode(substr($msgData, $offsetInChars));
114 
115  if (count($rlpDecoded) === 1) {
116  $return[$p] = new $class(self::ensureHexPrefix($rlpDecoded[0]->get()), ['abi' => $param->type]);
117  }
118  else {
119  foreach ($rlpDecoded as $rlpItem) {
120  $return[$p][] = new $class(self::ensureHexPrefix($rlpItem->get()), ['abi' => $param->type]);
121  }
122  }
123  }
124  else {
125  throw new \Exception('Length type must be "dynamic" or "static".');
126  }
127  $pos += 64;
128  }
129  return $return;
130  }
131 
132 
141  public function getParamDefinition(string $methodName)
142  {
143  foreach ($this->abi as $item) {
144  if (isset($item->name)
145  && isset($item->type)
146  && $item->type === 'function'
147  && $item->name === $methodName
148  ) {
149  return $item;
150  }
151  }
152  throw new \Exception('Called undefined contract method: ' . $methodName . '.');
153  }
154 
155 
162  private static function getSignature($m)
163  {
164  $sign = $m->name . '(';
165  foreach ($m->inputs as $i => $item) {
166  $sign .= $item->type;
167  if ($i < count($m->inputs) - 1) {
168  $sign .= ',';
169  }
170  }
171  $sign .= ')';
172  return self::getMethodSignature($sign);
173  }
174 
175 
206  public static function convertByAbi(string $abiType, EthD $value)
207  {
208 
209  // T[k] for any dynamic T and any k > 0
210  // <type>[]: a variable-length array of elements of the given type.
211  if (strpos($abiType, '[')) {
212  // @todo
213  }
214 
215  // (T1,...,Tk) if any Ti is dynamic for 1 <= i <= k
216  // (T1,T2,...,Tn): tuple consisting of the types T1, …, Tn, n >= 0
217  if (strpos($abiType, '(')) {
218  // @todo
219  }
220 
221  $class = EthDataType::getClassByAbi($abiType);
222  if ($class) {
223  return new $class($value->hexVal(), ['abi' => $abiType]);
224  }
225 
226  throw new \Exception('Can not convert to unknown type ' . $abiType . '. Might be not implemented yet.');
227  }
228 
229 
235  public function getEvents()
236  {
237  $events = [];
238  foreach ($this->abi as $item) {
239  if (isset($item->type)
240  && $item->type === 'event'
241  ) {
242  $events[] = new Event($item);
243  }
244  }
245  return $events;
246  }
247 
248 
256  public function getEventsByTopic()
257  {
258  $events = [];
259  foreach ($this->getEvents() as $event) {
260  $events[$event->getTopic()] = $event;
261  }
262  return $events;
263  }
264 
265 
266 }
encodeFunction(string $methodName, array $values)
Definition: Abi.php:36
getEvents()
Definition: Abi.php:235
getParamDefinition(string $methodName)
Definition: Abi.php:141
decodeMethod(string $method, EthD $rawReturn)
Definition: Abi.php:65
getEventsByTopic()
Definition: Abi.php:256
static convertByAbi(string $abiType, EthD $value)
Definition: Abi.php:206
Definition: Abi.php:3
__construct(array $abi)
Definition: Abi.php:22