From dd256108e56cabca1df49b49d85d355ed5a72099 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Wed, 30 Dec 2020 21:37:41 +0100 Subject: [PATCH 01/20] added and improved comments and error messages for clarity Attempting to make some of the comments and errors easier to understand and clearer in what it does / what is required --- src/Contract.php | 65 ++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index 87ad1fc4..cc34c184 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -1,7 +1,7 @@ * @@ -267,6 +267,7 @@ public function setDefaultBlock($defaultBlock) /** * getFunctions + * get an array of all methods in the loaded contract * * @return array */ @@ -277,6 +278,7 @@ public function getFunctions() /** * getEvents + * get an array of all events (and their inputs) in the loaded contract * * @return array */ @@ -368,6 +370,7 @@ public function setToAddress($address) /** * at + * set the address of the deployed contract to make calls to * * @param string $address * @return $this @@ -375,7 +378,7 @@ public function setToAddress($address) public function at($address) { if (AddressValidator::validate($address) === false) { - throw new InvalidArgumentException('Please make sure address is valid.'); + throw new InvalidArgumentException('Please make sure the contract address is valid.'); } $this->toAddress = AddressFormatter::format($address); @@ -391,7 +394,7 @@ public function at($address) public function bytecode($bytecode) { if (HexValidator::validate($bytecode) === false) { - throw new InvalidArgumentException('Please make sure bytecode is valid.'); + throw new InvalidArgumentException('Please make sure the bytecode input is valid.'); } $this->bytecode = Utils::stripZero($bytecode); @@ -407,7 +410,7 @@ public function bytecode($bytecode) public function abi($abi) { if (StringValidator::validate($abi) === false) { - throw new InvalidArgumentException('Please make sure abi is valid.'); + throw new InvalidArgumentException('Please make sure the abi input is valid.'); } $abiArray = []; if (is_string($abi)) { @@ -438,7 +441,7 @@ public function abi($abi) /** * new - * Deploy a contruct with params. + * Deploy a new contract, along with any relevant parameters for its constructor. * * @param mixed * @return void @@ -452,10 +455,10 @@ public function new() $input_count = isset($constructor['inputs']) ? count($constructor['inputs']) : 0; if (count($arguments) < $input_count) { - throw new InvalidArgumentException('Please make sure you have put all constructor params and callback.'); + throw new InvalidArgumentException('Please make sure you have included all constructor parameters and a callback function.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last param must be callback function.'); + throw new \InvalidArgumentException('The last parameter must be a callback function.'); } if (!isset($this->bytecode)) { throw new \InvalidArgumentException('Please call bytecode($bytecode) before new().'); @@ -480,7 +483,8 @@ public function new() /** * send - * Send function method. + * Send inputs to a specific method of the deployed contract + * (interacts with chain data and can alter it: costs gas) * * @param mixed * @return void @@ -493,7 +497,7 @@ public function send() $callback = array_pop($arguments); if (!is_string($method)) { - throw new InvalidArgumentException('Please make sure the method is string.'); + throw new InvalidArgumentException('Please make sure the method name is supplied as a string as the first parameter.'); } $functions = []; @@ -503,10 +507,10 @@ public function send() } }; if (count($functions) < 1) { - throw new InvalidArgumentException('Please make sure the method exists.'); + throw new InvalidArgumentException('Please make sure the named method exists in the contract.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last param must be callback function.'); + throw new \InvalidArgumentException('The last parameter must be a callback function.'); } // check the last one in arguments is transaction object @@ -558,7 +562,7 @@ public function send() break; } if (empty($data) || empty($functionName)) { - throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + throw new InvalidArgumentException('Please make sure you have included all parameters of the method and a callback function.'); } $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $transaction['to'] = $this->toAddress; @@ -575,7 +579,8 @@ public function send() /** * call - * Call function method. + * Call a specific method of the deployed contract + * (read-only, cannot alter chain data: does not cost gas) * * @param mixed * @return void @@ -588,7 +593,7 @@ public function call() $callback = array_pop($arguments); if (!is_string($method)) { - throw new InvalidArgumentException('Please make sure the method is string.'); + throw new InvalidArgumentException('Please make sure the method name is supplied as a string as the first parameter.'); } $functions = []; @@ -598,10 +603,10 @@ public function call() } }; if (count($functions) < 1) { - throw new InvalidArgumentException('Please make sure the method exists.'); + throw new InvalidArgumentException('Please make sure the named method exists in the contract.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last param must be callback function.'); + throw new \InvalidArgumentException('The last parameter must be a callback function.'); } // check the arguments @@ -623,7 +628,7 @@ public function call() break; } if (empty($data) || empty($functionName)) { - throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + throw new InvalidArgumentException('Please make sure you have included all parameters of the method and a callback function.'); } // remove arguments array_splice($arguments, 0, $paramsLen); @@ -686,10 +691,10 @@ public function estimateGas() $constructor = $this->constructor; if (count($arguments) < count($constructor['inputs'])) { - throw new InvalidArgumentException('Please make sure you have put all constructor params and callback.'); + throw new InvalidArgumentException('Please make sure you have included all constructor parameters and a callback function.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last param must be callback function.'); + throw new \InvalidArgumentException('The last parameter must be a callback function.'); } if (!isset($this->bytecode)) { throw new \InvalidArgumentException('Please call bytecode($bytecode) before estimateGas().'); @@ -706,7 +711,7 @@ public function estimateGas() $method = array_splice($arguments, 0, 1)[0]; if (!is_string($method)) { - throw new InvalidArgumentException('Please make sure the method is string.'); + throw new InvalidArgumentException('Please make sure the method name is supplied as a string as the first parameter.'); } $functions = []; @@ -716,10 +721,10 @@ public function estimateGas() } }; if (count($functions) < 1) { - throw new InvalidArgumentException('Please make sure the method exists.'); + throw new InvalidArgumentException('Please make sure the named method exists in the contract.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last param must be callback function.'); + throw new \InvalidArgumentException('The last parameter must be a callback function.'); } // check the last one in arguments is transaction object @@ -771,7 +776,7 @@ public function estimateGas() break; } if (empty($data) || empty($functionName)) { - throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + throw new InvalidArgumentException('Please make sure you have included all parameters of the method and a callback function.'); } $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $transaction['to'] = $this->toAddress; @@ -789,9 +794,9 @@ public function estimateGas() /** * getData - * Get the function method call data. - * With this function, you can send signed contract function transaction. - * 1. Get the funtion data with params. + * Get the contract method's call data. + * With this function, you can send signed contract method transactions. + * 1. Get the method data with parameters. * 2. Sign the data with user private key. * 3. Call sendRawTransaction. * @@ -808,7 +813,7 @@ public function getData() $constructor = $this->constructor; if (count($arguments) < count($constructor['inputs'])) { - throw new InvalidArgumentException('Please make sure you have put all constructor params and callback.'); + throw new InvalidArgumentException('Please make sure you have included all constructor parameters and a callback function.'); } if (!isset($this->bytecode)) { throw new \InvalidArgumentException('Please call bytecode($bytecode) before getData().'); @@ -820,7 +825,7 @@ public function getData() $method = array_splice($arguments, 0, 1)[0]; if (!is_string($method)) { - throw new InvalidArgumentException('Please make sure the method is string.'); + throw new InvalidArgumentException('Please make sure the method name is supplied as a string as the first parameter.'); } $functions = []; @@ -830,7 +835,7 @@ public function getData() } }; if (count($functions) < 1) { - throw new InvalidArgumentException('Please make sure the method exists.'); + throw new InvalidArgumentException('Please make sure the named method exists in the contract.'); } $params = $arguments; @@ -849,7 +854,7 @@ public function getData() break; } if (empty($data) || empty($functionName)) { - throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + throw new InvalidArgumentException('Please make sure you have included all parameters of the method and a callback function.'); } $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $functionData = Utils::stripZero($functionSignature) . Utils::stripZero($data); From fd1c09b9138a947de6e65d418e05b7d69575804b Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Wed, 30 Dec 2020 22:33:29 +0100 Subject: [PATCH 02/20] setting composer package name to run my own package --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6e51e8fc..c1c72f9a 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "sc0vu/web3.php", + "name": "nanolucas/web3.php", "description": "Ethereum web3 interface.", "type": "library", "license": "MIT", From 56b50e62540138a68ac841bcb22feeaa88a433d0 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Wed, 30 Dec 2020 22:37:02 +0100 Subject: [PATCH 03/20] change install package in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c97e944c..88388a24 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ composer require sc0vu/web3.php dev-master Or you can add this line in composer.json ``` -"sc0vu/web3.php": "dev-master" +"nanolucas/web3.php": "dev-master" ``` From bffe4cfc8c6ff0dceb25f245d47caa5094556c2b Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Wed, 30 Dec 2020 22:39:15 +0100 Subject: [PATCH 04/20] undo previous changes, using VCS package instead --- README.md | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 88388a24..c97e944c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ composer require sc0vu/web3.php dev-master Or you can add this line in composer.json ``` -"nanolucas/web3.php": "dev-master" +"sc0vu/web3.php": "dev-master" ``` diff --git a/composer.json b/composer.json index c1c72f9a..6e51e8fc 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "nanolucas/web3.php", + "name": "sc0vu/web3.php", "description": "Ethereum web3 interface.", "type": "library", "license": "MIT", From 486d8498975ea7c93cd30af08326d380e8488ff0 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 2 Jan 2021 18:39:15 +0100 Subject: [PATCH 05/20] make exception namespacing consistent --- src/Contract.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index cc34c184..8885fa5b 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -11,7 +11,8 @@ namespace Web3; -use InvalidArgumentException; +use \InvalidArgumentException; +use \RuntimeException; use Web3\Providers\Provider; use Web3\Providers\HttpProvider; use Web3\RequestManagers\RequestManager; @@ -175,7 +176,7 @@ public function __construct($provider, $abi, $defaultBlock = 'latest') // public function __call($name, $arguments) // { // if (empty($this->provider)) { - // throw new \RuntimeException('Please set provider first.'); + // throw new RuntimeException('Please set provider first.'); // } // $class = explode('\\', get_class()); // if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { @@ -458,10 +459,10 @@ public function new() throw new InvalidArgumentException('Please make sure you have included all constructor parameters and a callback function.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last parameter must be a callback function.'); + throw new InvalidArgumentException('The last parameter must be a callback function.'); } if (!isset($this->bytecode)) { - throw new \InvalidArgumentException('Please call bytecode($bytecode) before new().'); + throw new InvalidArgumentException('Please call bytecode($bytecode) before new().'); } $params = array_splice($arguments, 0, $input_count); $data = $this->ethabi->encodeParameters($constructor, $params); @@ -510,7 +511,7 @@ public function send() throw new InvalidArgumentException('Please make sure the named method exists in the contract.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last parameter must be a callback function.'); + throw new InvalidArgumentException('The last parameter must be a callback function.'); } // check the last one in arguments is transaction object @@ -606,7 +607,7 @@ public function call() throw new InvalidArgumentException('Please make sure the named method exists in the contract.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last parameter must be a callback function.'); + throw new InvalidArgumentException('The last parameter must be a callback function.'); } // check the arguments @@ -694,10 +695,10 @@ public function estimateGas() throw new InvalidArgumentException('Please make sure you have included all constructor parameters and a callback function.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last parameter must be a callback function.'); + throw new InvalidArgumentException('The last parameter must be a callback function.'); } if (!isset($this->bytecode)) { - throw new \InvalidArgumentException('Please call bytecode($bytecode) before estimateGas().'); + throw new InvalidArgumentException('Please call bytecode($bytecode) before estimateGas().'); } $params = array_splice($arguments, 0, count($constructor['inputs'])); $data = $this->ethabi->encodeParameters($constructor, $params); @@ -724,7 +725,7 @@ public function estimateGas() throw new InvalidArgumentException('Please make sure the named method exists in the contract.'); } if (is_callable($callback) !== true) { - throw new \InvalidArgumentException('The last parameter must be a callback function.'); + throw new InvalidArgumentException('The last parameter must be a callback function.'); } // check the last one in arguments is transaction object @@ -816,7 +817,7 @@ public function getData() throw new InvalidArgumentException('Please make sure you have included all constructor parameters and a callback function.'); } if (!isset($this->bytecode)) { - throw new \InvalidArgumentException('Please call bytecode($bytecode) before getData().'); + throw new InvalidArgumentException('Please call bytecode($bytecode) before getData().'); } $params = array_splice($arguments, 0, count($constructor['inputs'])); $data = $this->ethabi->encodeParameters($constructor, $params); From 665b15b261a74f73a6a103e6fac37800f4629521 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 2 Jan 2021 18:53:20 +0100 Subject: [PATCH 06/20] update variable name for consistency --- src/Contracts/Ethabi.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Contracts/Ethabi.php b/src/Contracts/Ethabi.php index 694a5635..6f5334ba 100644 --- a/src/Contracts/Ethabi.php +++ b/src/Contracts/Ethabi.php @@ -102,15 +102,15 @@ public function encodeFunctionSignature($functionName) * encodeEventSignature * TODO: Fix same event name with different params * - * @param string|stdClass|array $functionName + * @param string|stdClass|array $eventName * @return string */ - public function encodeEventSignature($functionName) + public function encodeEventSignature($eventName) { - if (!is_string($functionName)) { - $functionName = Utils::jsonMethodToString($functionName); + if (!is_string($eventName)) { + $eventName = Utils::jsonMethodToString($eventName); } - return Utils::sha3($functionName); + return Utils::sha3($eventName); } /** From af71b4d08158bf177d0c2435b0cb6eb455fe4b4c Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 2 Jan 2021 20:29:10 +0100 Subject: [PATCH 07/20] implement event log getter --- src/Contract.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Contract.php b/src/Contract.php index 8885fa5b..1be941dd 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -863,4 +863,29 @@ public function getData() return $functionData; } } + + /** + * getEventLogs + * + * @param string $eventName + * @return string + */ + public function getEventLogs($eventName) + { + $eventSignature = $this->ethabi->encodeEventSignature($eventName); + $eventInputParametersStringArray = $this->ethabi->encodeParameters($this->events[$eventName]); + + $this->eth->getLogs([ + 'topics' => array($eventSignature), + 'address' => $this->toAddress + ], + function ($error, $result) use (&$output) { + if ($error !== null) { + throw new RuntimeException($error->getMessage()); + } + $output = $result; + }); + + return $this->ethabi->decodeParameters($eventInputParametersStringArray, $output[0]->data); + } } From 121c80ffb01498d71aaad75a7ca7cccb214635ea Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 2 Jan 2021 20:39:32 +0100 Subject: [PATCH 08/20] update $eventInputParametersStringArray --- src/Contract.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Contract.php b/src/Contract.php index 1be941dd..a4f9bc03 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -873,7 +873,7 @@ public function getData() public function getEventLogs($eventName) { $eventSignature = $this->ethabi->encodeEventSignature($eventName); - $eventInputParametersStringArray = $this->ethabi->encodeParameters($this->events[$eventName]); + $eventInputParametersStringArray = array_column($this->events[$eventName]['inputs'], 'type'); $this->eth->getLogs([ 'topics' => array($eventSignature), From af3bd4c3733e89850f667073f2d61302b239892f Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sun, 3 Jan 2021 00:00:53 +0100 Subject: [PATCH 09/20] remove new function before sending pull request --- src/Contract.php | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index a4f9bc03..8885fa5b 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -863,29 +863,4 @@ public function getData() return $functionData; } } - - /** - * getEventLogs - * - * @param string $eventName - * @return string - */ - public function getEventLogs($eventName) - { - $eventSignature = $this->ethabi->encodeEventSignature($eventName); - $eventInputParametersStringArray = array_column($this->events[$eventName]['inputs'], 'type'); - - $this->eth->getLogs([ - 'topics' => array($eventSignature), - 'address' => $this->toAddress - ], - function ($error, $result) use (&$output) { - if ($error !== null) { - throw new RuntimeException($error->getMessage()); - } - $output = $result; - }); - - return $this->ethabi->decodeParameters($eventInputParametersStringArray, $output[0]->data); - } } From 816c2238a37cb5a6587ba6859c11e37aad510b6e Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sun, 3 Jan 2021 00:05:06 +0100 Subject: [PATCH 10/20] working implementation of getEventLogs A working, improved and deciphered version of the function alluded to in https://github.com/web3p/web3.php/issues/93 --- src/Contract.php | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/Contract.php b/src/Contract.php index 8885fa5b..3adf7bcf 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -863,4 +863,52 @@ public function getData() return $functionData; } } + + /** + * getEventLogs + * + * @param string $eventName + * @param string|int $fromBlock + * @param string|int $toBlock + * @return string + */ + public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock = 'latest') + { + if ($fromBlock != 'latest') { + if (!is_int($fromBlock) || $fromBlock < 1) { + throw new InvalidArgumentException('Please make sure fromBlock is a valid block number'); + } else if ($toBlock != 'latest' && $fromBlock > $toBlock) { + throw new InvalidArgumentException('Please make sure fromBlock is equal or less than toBlock'); + } + } + + if ($toBlock != 'latest') { + if (!is_int($toBlock) || $toBlock < 1) { + throw new InvalidArgumentException('Please make sure toBlock is a valid block number'); + } else if ($fromBlock == 'latest') { + throw new InvalidArgumentException('Please make sure toBlock is equal or greater than fromBlock'); + } + } + + $eventSignature = $this->ethabi->encodeEventSignature($this->events[$eventName]); + $eventInputParametersStringArray = array_column($this->events[$eventName]['inputs'], 'type'); + + $this->eth->getLogs([ + 'fromBlock' => (is_int($fromBlock)) ? '0x' . dechex($fromBlock) : $fromBlock, + 'toBlock' => (is_int($toBlock)) ? '0x' . dechex($toBlock) : $toBlock, + 'topics' => [$eventSignature], + 'address' => $this->toAddress + ], + function ($error, $result) use (&$output, $eventInputParametersStringArray) { + if ($error !== null) { + throw new RuntimeException($error->getMessage()); + } + + foreach ($result as $object) { + $output[] = $this->ethabi->decodeParameters($eventInputParametersStringArray, $object->data); + } + }); + + return $output; + } } From aade350900677f84f276533ea5c54e9b2495f0b4 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sun, 3 Jan 2021 16:24:36 +0100 Subject: [PATCH 11/20] return data with named keys also improve general comments throughout function --- src/Contract.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index 3adf7bcf..f7758391 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -874,6 +874,7 @@ public function getData() */ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock = 'latest') { + //try to ensure block numbers are valid together if ($fromBlock != 'latest') { if (!is_int($fromBlock) || $fromBlock < 1) { throw new InvalidArgumentException('Please make sure fromBlock is a valid block number'); @@ -890,22 +891,29 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock } } - $eventSignature = $this->ethabi->encodeEventSignature($this->events[$eventName]); - $eventInputParametersStringArray = array_column($this->events[$eventName]['inputs'], 'type'); + //retrieve event input types and names from the contract abi + $eventParameterTypes = array_column($this->events[$eventName]['inputs'], 'type'); + $eventParameterNames = array_column($this->events[$eventName]['inputs'], 'name'); + //filter through log data to find any logs which match this event (topic) from + //this contract, between these specified blocks (defaulting to the latest block only) $this->eth->getLogs([ 'fromBlock' => (is_int($fromBlock)) ? '0x' . dechex($fromBlock) : $fromBlock, 'toBlock' => (is_int($toBlock)) ? '0x' . dechex($toBlock) : $toBlock, - 'topics' => [$eventSignature], + 'topics' => [$this->ethabi->encodeEventSignature($this->events[$eventName])], 'address' => $this->toAddress ], - function ($error, $result) use (&$output, $eventInputParametersStringArray) { + function ($error, $result) use (&$output, $eventParameterTypes, $eventParameterNames) { if ($error !== null) { throw new RuntimeException($error->getMessage()); } foreach ($result as $object) { - $output[] = $this->ethabi->decodeParameters($eventInputParametersStringArray, $object->data); + //decode the data from the log into the expected formats + $decodedData = $this->ethabi->decodeParameters($eventParameterTypes, $object->data); + + //return the data with its named array keys + $output[] = array_combine($eventParameterNames, $decodedData); } }); From 125aea05cdf73b8560f56c471cd0ad2f77d31e40 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sun, 3 Jan 2021 21:41:21 +0100 Subject: [PATCH 12/20] include block metadata in returned array --- src/Contract.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index f7758391..725f88c4 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -870,7 +870,7 @@ public function getData() * @param string $eventName * @param string|int $fromBlock * @param string|int $toBlock - * @return string + * @return array */ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock = 'latest') { @@ -903,7 +903,7 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock 'topics' => [$this->ethabi->encodeEventSignature($this->events[$eventName])], 'address' => $this->toAddress ], - function ($error, $result) use (&$output, $eventParameterTypes, $eventParameterNames) { + function ($error, $result) use (&$eventLogData, $eventParameterTypes, $eventParameterNames) { if ($error !== null) { throw new RuntimeException($error->getMessage()); } @@ -912,11 +912,16 @@ function ($error, $result) use (&$output, $eventParameterTypes, $eventParameterN //decode the data from the log into the expected formats $decodedData = $this->ethabi->decodeParameters($eventParameterTypes, $object->data); - //return the data with its named array keys - $output[] = array_combine($eventParameterNames, $decodedData); + //include block metadata for context, along with event data + $eventLogData[] = [ + 'blockHash' => $object->blockHash, + 'blockNumber' => hexdec($object->blockNumber), + //return the data with its named array keys + 'data' => array_combine($eventParameterNames, $decodedData) + ]; } }); - return $output; + return $eventLogData; } } From 46dc78e0d60d9927ca34a1c75e16c237790a927f Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 9 Jan 2021 18:10:54 +0100 Subject: [PATCH 13/20] handle indexed parameters --- src/Contract.php | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index 725f88c4..424729d4 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -891,9 +891,23 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock } } - //retrieve event input types and names from the contract abi - $eventParameterTypes = array_column($this->events[$eventName]['inputs'], 'type'); - $eventParameterNames = array_column($this->events[$eventName]['inputs'], 'name'); + //indexed and non-indexed event parameters must be treated separately + //indexed parameters are stored in the 'topics' array + //non-indexed parameters are stored in the 'data' value + $eventParameterNames = []; + $eventParameterTypes = []; + $eventIndexedParameterNames = []; + $eventIndexedParameterTypes = []; + + foreach ($this->events[$eventName]['inputs'] as $input) { + if ($input['indexed']) { + $eventIndexedParameterNames[] = $input['name']; + $eventIndexedParameterTypes[] = $input['type']; + } else { + $eventParameterNames[] = $input['name']; + $eventParameterTypes[] = $input['type']; + } + } //filter through log data to find any logs which match this event (topic) from //this contract, between these specified blocks (defaulting to the latest block only) @@ -903,21 +917,25 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock 'topics' => [$this->ethabi->encodeEventSignature($this->events[$eventName])], 'address' => $this->toAddress ], - function ($error, $result) use (&$eventLogData, $eventParameterTypes, $eventParameterNames) { + function ($error, $result) use (&$eventLogData, $eventParameterTypes, $eventParameterNames, $eventIndexedParameterTypes, $eventIndexedParameterNames) { if ($error !== null) { throw new RuntimeException($error->getMessage()); } foreach ($result as $object) { - //decode the data from the log into the expected formats - $decodedData = $this->ethabi->decodeParameters($eventParameterTypes, $object->data); + //decode the data from the log into the expected formats, with its corresponding named key + $decodedData = array_combine($eventParameterNames, $this->ethabi->decodeParameters($eventParameterTypes, $object->data)); + + //decode the indexed parameter data + for ($i = 0; $i < count($eventIndexedParameterNames); $i++) { + $decodedData[$eventIndexedParameterNames[$i]] = $this->ethabi->decodeParameters([$eventIndexedParameterTypes[$i]], $object->topics[$i + 1])[0]; + } //include block metadata for context, along with event data $eventLogData[] = [ 'blockHash' => $object->blockHash, 'blockNumber' => hexdec($object->blockNumber), - //return the data with its named array keys - 'data' => array_combine($eventParameterNames, $decodedData) + 'data' => $decodedData ]; } }); From bb9f9f08e7066da752f7ab8cc2d3b5ba33e587a5 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sun, 21 Feb 2021 23:04:25 +0100 Subject: [PATCH 14/20] added comment --- src/Contract.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Contract.php b/src/Contract.php index 424729d4..d98c7caa 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -928,6 +928,7 @@ function ($error, $result) use (&$eventLogData, $eventParameterTypes, $eventPara //decode the indexed parameter data for ($i = 0; $i < count($eventIndexedParameterNames); $i++) { + //topics[0] is the event signature, so we start from $i + 1 for the indexed parameter data $decodedData[$eventIndexedParameterNames[$i]] = $this->ethabi->decodeParameters([$eventIndexedParameterTypes[$i]], $object->topics[$i + 1])[0]; } From b26da9a83b6de068f0a899fa32763ead8e04e90a Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Wed, 17 Mar 2021 21:53:58 +0100 Subject: [PATCH 15/20] initialise empty array --- src/Contract.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Contract.php b/src/Contract.php index d98c7caa..dd682c82 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -891,6 +891,8 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock } } + $eventLogData = []; + //indexed and non-indexed event parameters must be treated separately //indexed parameters are stored in the 'topics' array //non-indexed parameters are stored in the 'data' value From 91ef36fa4cca7aee2141a305aa60705e0c3b3c3b Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Fri, 26 Mar 2021 23:48:51 +0100 Subject: [PATCH 16/20] add key check to prevent PHP notice --- src/Contract.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Contract.php b/src/Contract.php index dd682c82..7f585c40 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -893,6 +893,11 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock $eventLogData = []; + //if there's no data for this event, no need to proceed further + if (!array_key_exists($eventName, $this->events)) { + return $eventLogData; + } + //indexed and non-indexed event parameters must be treated separately //indexed parameters are stored in the 'topics' array //non-indexed parameters are stored in the 'data' value From d4e9e4a5335cf02447ffc64aec87af3e77d02f13 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 27 Mar 2021 00:01:37 +0100 Subject: [PATCH 17/20] throw an exception if event name isn't found --- src/Contract.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index 7f585c40..aa38f34e 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -893,9 +893,9 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock $eventLogData = []; - //if there's no data for this event, no need to proceed further + //ensure the event actually exists before trying to filter for it if (!array_key_exists($eventName, $this->events)) { - return $eventLogData; + throw new InvalidArgumentException("'{$eventName}' does not exist in the ABI for this contract"); } //indexed and non-indexed event parameters must be treated separately From 935e91916ce342bc0cea76b6003019ed2ea802e9 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Wed, 7 Apr 2021 23:32:53 +0200 Subject: [PATCH 18/20] return the transactionHash from event data --- src/Contract.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Contract.php b/src/Contract.php index aa38f34e..8d717153 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -941,6 +941,7 @@ function ($error, $result) use (&$eventLogData, $eventParameterTypes, $eventPara //include block metadata for context, along with event data $eventLogData[] = [ + 'transactionHash' => $object->transactionHash, 'blockHash' => $object->blockHash, 'blockNumber' => hexdec($object->blockNumber), 'data' => $decodedData From 87f8976cc8dc37e62dfe26ffdc6cdc324a13976e Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 17 Apr 2021 20:59:11 +0200 Subject: [PATCH 19/20] minor efficiency improvements --- src/Contract.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index 8d717153..0d92a361 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -875,18 +875,18 @@ public function getData() public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock = 'latest') { //try to ensure block numbers are valid together - if ($fromBlock != 'latest') { + if ($fromBlock !== 'latest') { if (!is_int($fromBlock) || $fromBlock < 1) { throw new InvalidArgumentException('Please make sure fromBlock is a valid block number'); - } else if ($toBlock != 'latest' && $fromBlock > $toBlock) { + } else if ($toBlock !== 'latest' && $fromBlock > $toBlock) { throw new InvalidArgumentException('Please make sure fromBlock is equal or less than toBlock'); } } - if ($toBlock != 'latest') { + if ($toBlock !== 'latest') { if (!is_int($toBlock) || $toBlock < 1) { throw new InvalidArgumentException('Please make sure toBlock is a valid block number'); - } else if ($fromBlock == 'latest') { + } else if ($fromBlock === 'latest') { throw new InvalidArgumentException('Please make sure toBlock is equal or greater than fromBlock'); } } @@ -916,6 +916,8 @@ public function getEventLogs(string $eventName, $fromBlock = 'latest', $toBlock } } + $numEventIndexedParameterNames = count($eventIndexedParameterNames); + //filter through log data to find any logs which match this event (topic) from //this contract, between these specified blocks (defaulting to the latest block only) $this->eth->getLogs([ @@ -934,7 +936,7 @@ function ($error, $result) use (&$eventLogData, $eventParameterTypes, $eventPara $decodedData = array_combine($eventParameterNames, $this->ethabi->decodeParameters($eventParameterTypes, $object->data)); //decode the indexed parameter data - for ($i = 0; $i < count($eventIndexedParameterNames); $i++) { + for ($i = 0; $i < $numEventIndexedParameterNames; $i++) { //topics[0] is the event signature, so we start from $i + 1 for the indexed parameter data $decodedData[$eventIndexedParameterNames[$i]] = $this->ethabi->decodeParameters([$eventIndexedParameterTypes[$i]], $object->topics[$i + 1])[0]; } From 0377c6c5dc6f507289e9e6b629191e580ccf81a3 Mon Sep 17 00:00:00 2001 From: Nanolucas Date: Sat, 17 Apr 2021 21:14:47 +0200 Subject: [PATCH 20/20] add contract::getEventLogs --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c97e944c..a31f12fd 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,10 @@ $constructorData = $contract->bytecode($bytecode)->getData($params); // get function data $functionData = $contract->at($contractAddress)->getData($functionName, $params); + +//get event log data +//$fromBlock and $toBlock are optional, default to 'latest' and accept block numbers integers +$events = $contract->getEventLogs($eventName, $fromBlock, $toBlock); ``` # Assign value to outside scope(from callback scope to outside scope)