Merge 877ac0ba94
into cede0cc410
This commit is contained in:
commit
f5fd9bc340
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ composer.phar
|
|||||||
/vendor/
|
/vendor/
|
||||||
.phpintel/
|
.phpintel/
|
||||||
/.idea/
|
/.idea/
|
||||||
|
test.php
|
||||||
|
|
||||||
|
|
||||||
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
|
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
|
||||||
|
217
resources/abi.json
Normal file
217
resources/abi.json
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "name",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_spender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "approve",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "success",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "totalSupply",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transferFrom",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "success",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "decimals",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "balanceOf",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "balance",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "symbol",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transfer",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "success",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_spender",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "allowance",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "remaining",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "_from",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Transfer",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "_spender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Approval",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
229
src/AbiDecoder.php
Normal file
229
src/AbiDecoder.php
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of web3.php package.
|
||||||
|
* @author Halil Beycan <halilbeycan0@gmail.com>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Web3;
|
||||||
|
|
||||||
|
use Web3\Contracts\Ethabi;
|
||||||
|
use Web3\Contracts\Types\Address;
|
||||||
|
use Web3\Contracts\Types\Boolean;
|
||||||
|
use Web3\Contracts\Types\Bytes;
|
||||||
|
use Web3\Contracts\Types\DynamicBytes;
|
||||||
|
use Web3\Contracts\Types\Integer;
|
||||||
|
use Web3\Contracts\Types\Str;
|
||||||
|
use Web3\Contracts\Types\Uinteger;
|
||||||
|
use phpseclib\Math\BigInteger as BN;
|
||||||
|
|
||||||
|
class AbiDecoder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $abi = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Ethabi
|
||||||
|
*/
|
||||||
|
private $ethAbi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $methodIds = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|null $abi
|
||||||
|
*/
|
||||||
|
public function __construct(?array $abi = null)
|
||||||
|
{
|
||||||
|
$this->abi = is_null($abi) ? json_decode(file_get_contents(dirname(__DIR__) . '/resources/abi.json')) : $abi;
|
||||||
|
|
||||||
|
$this->ethAbi = new Ethabi([
|
||||||
|
'address' => new Address,
|
||||||
|
'bool' => new Boolean,
|
||||||
|
'bytes' => new Bytes,
|
||||||
|
'dynamicBytes' => new DynamicBytes,
|
||||||
|
'int' => new Integer,
|
||||||
|
'string' => new Str,
|
||||||
|
'uint' => new Uinteger,
|
||||||
|
]);
|
||||||
|
|
||||||
|
array_map([$this, 'parseMethodIds'], $this->abi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object $input
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function typeToString(object $input) : string
|
||||||
|
{
|
||||||
|
if ($input->type === "tuple") {
|
||||||
|
return "(" . implode(',', array_map([$this, 'typeToString'], $input->components)) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $input->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $inputs
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function parseInputs(array $inputs) : string
|
||||||
|
{
|
||||||
|
return "(" . implode(',', array_map([$this, 'typeToString'], $inputs)) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object $obj
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function parseMethodIds(object $obj)
|
||||||
|
{
|
||||||
|
if (isset($obj->name)) {
|
||||||
|
$signature = Utils::sha3($obj->name . $this->parseInputs($obj->inputs));
|
||||||
|
|
||||||
|
if ($obj->type === "event") {
|
||||||
|
$this->methodIds[substr($signature, 2)] = $obj;
|
||||||
|
} else {
|
||||||
|
$this->methodIds[substr($signature, 2, 8)] = $obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $input
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function decodeInput(string $input) : object
|
||||||
|
{
|
||||||
|
$abiItem = $this->methodIds[substr($input, 2, 8)];
|
||||||
|
$types = array_column($abiItem->inputs, 'type');
|
||||||
|
|
||||||
|
if ($abiItem) {
|
||||||
|
$decoded = $this->ethAbi->decodeParameters($types, substr($input, 10));
|
||||||
|
|
||||||
|
$retData = (object) [
|
||||||
|
"name" => $abiItem->name,
|
||||||
|
"params" => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($decoded); $i++) {
|
||||||
|
$param = $decoded[$i];
|
||||||
|
$parsedParam = $param;
|
||||||
|
$isArray = is_array($param);
|
||||||
|
$isUint = strpos($abiItem->inputs[$i]->type, "uint") !== false;
|
||||||
|
$isInt = strpos($abiItem->inputs[$i]->type, "int") !== false;
|
||||||
|
$isAddress = strpos($abiItem->inputs[$i]->type, "address") !== false;
|
||||||
|
|
||||||
|
if ($isUint || $isInt) {
|
||||||
|
if ($isArray) {
|
||||||
|
$parsedParam = array_map(function($val) {
|
||||||
|
return (new BN($val))->toString();
|
||||||
|
}, $param);
|
||||||
|
} else {
|
||||||
|
$parsedParam = (new BN($param))->toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isAddress) {
|
||||||
|
if ($isArray) {
|
||||||
|
$parsedParam = array_map('strtolower', $param);
|
||||||
|
} else {
|
||||||
|
$parsedParam = strtolower($param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$retData->params[] = [
|
||||||
|
"name" => $abiItem->inputs[$i]->name,
|
||||||
|
"value" => $parsedParam,
|
||||||
|
"type" => $abiItem->inputs[$i]->type,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $retData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $logs
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function decodeLogs(array $logs) : array
|
||||||
|
{
|
||||||
|
$logs = array_filter($logs, function($log) {
|
||||||
|
return count($log->topics) > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return array_map(function($log) {
|
||||||
|
|
||||||
|
$method = $this->methodIds[substr($log->topics[0], 2)];
|
||||||
|
|
||||||
|
if ($method) {
|
||||||
|
|
||||||
|
$logData = $log->data;
|
||||||
|
$decodedParams = [];
|
||||||
|
$dataIndex = 0;
|
||||||
|
$topicsIndex = 1;
|
||||||
|
$dataTypes = [];
|
||||||
|
|
||||||
|
array_map(function($input) use (&$dataTypes) {
|
||||||
|
if (!$input->indexed) {
|
||||||
|
$dataTypes[] = $input->type;
|
||||||
|
}
|
||||||
|
}, $method->inputs);
|
||||||
|
|
||||||
|
$decoded = $this->ethAbi->decodeParameters($dataTypes, substr($logData, 2));
|
||||||
|
|
||||||
|
array_map(function($input) use ($log, &$decodedParams, &$topicsIndex, &$dataIndex, $decoded) {
|
||||||
|
|
||||||
|
$decodedP = (object) [
|
||||||
|
"name" => $input->name,
|
||||||
|
"type" => $input->type,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($input->indexed) {
|
||||||
|
$decodedP->value = $log->topics[$topicsIndex];
|
||||||
|
$topicsIndex++;
|
||||||
|
} else {
|
||||||
|
$decodedP->value = $decoded[$dataIndex];
|
||||||
|
$dataIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->type === "address") {
|
||||||
|
$decodedP->value = strtolower($decodedP->value);
|
||||||
|
if (strlen($decodedP->value) > 42) {
|
||||||
|
$toRemove = strlen($decodedP->value) - 42;
|
||||||
|
$decodedP->value = "0x" . substr($decodedP->value, -40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$input->type === "uint256" ||
|
||||||
|
$input->type === "uint8" ||
|
||||||
|
$input->type === "int"
|
||||||
|
) {
|
||||||
|
if (is_string($decodedP->value) && str_starts_with($decodedP->value, '0x')) {
|
||||||
|
$decodedP->value = (new BN(substr($decodedP->value, 2), 16))->toString(10);
|
||||||
|
} else {
|
||||||
|
$decodedP->value = (new BN($decodedP->value))->toString(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$decodedParams[] = $decodedP;
|
||||||
|
|
||||||
|
}, $method->inputs);
|
||||||
|
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"name" => $method->name,
|
||||||
|
"events" => $decodedParams,
|
||||||
|
"address" => $log->address,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}, $logs);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user