From f74bbdef4c52d76a0f11ff327c0f30dfd49f607e Mon Sep 17 00:00:00 2001 From: Ashish Bhungani <89311557+ashishbhungani97@users.noreply.github.com> Date: Mon, 1 Sep 2025 09:51:47 +0530 Subject: [PATCH] Update Contract.php --- src/Contract.php | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/Contract.php b/src/Contract.php index b75e2402..1c52a0e2 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -856,4 +856,95 @@ public function getData() return $functionData; } } + + + /** + * getEventLogs + * + * @param string $eventName + * @param string|int $fromBlock + * @param string|int $toBlock + * @return array + */ + 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'); + } 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'); + } + } + + $eventLogData = []; + + //ensure the event actually exists before trying to filter for it + if (!array_key_exists($eventName, $this->events)) { + throw new InvalidArgumentException("'{$eventName}' does not exist in the ABI for this contract"); + } + + //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) + $this->eth->getLogs([ + 'fromBlock' => (is_int($fromBlock)) ? '0x' . dechex($fromBlock) : $fromBlock, + 'toBlock' => (is_int($toBlock)) ? '0x' . dechex($toBlock) : $toBlock, + 'topics' => [$this->ethabi->encodeEventSignature($this->events[$eventName])], + 'address' => $this->toAddress + ], + function ($error, $result) use (&$eventLogData, $eventParameterTypes, $eventParameterNames, $eventIndexedParameterTypes, $eventIndexedParameterNames) { + if ($error !== null) { + throw new RuntimeException($error->getMessage()); + } + + $numEventIndexedParameterNames = count($eventIndexedParameterNames); + + foreach ($result as $object) { + //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 < $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]; + } + + //include block metadata for context, along with event data + $eventLogData[] = [ + 'transactionHash' => $object->transactionHash, + 'blockHash' => $object->blockHash, + 'blockNumber' => hexdec($object->blockNumber), + 'data' => $decodedData + ]; + } + }); + + return $eventLogData; + } }