commit
bb87dcf866
@ -3,6 +3,8 @@
|
||||
[](https://travis-ci.org/sc0Vu/web3.php)
|
||||
[](https://codecov.io/gh/sc0Vu/web3.php)
|
||||
[](https://gitter.im/web3-php/web3.php)
|
||||
[](https://github.com/sc0Vu/web3.php/blob/master/LICENSE)
|
||||
|
||||
|
||||
A php interface for interacting with the Ethereum blockchain and ecosystem.
|
||||
|
||||
@ -173,7 +175,7 @@ $contract->at($contractAddress)->send($functionName, $params, $callback);
|
||||
$contract->bytecode($bytecode)->estimateGas($params, $callback);
|
||||
|
||||
// estimate function gas
|
||||
$contract->bytecode($bytecode)->estimateGas($functionName, $params, $callback);
|
||||
$contract->at($contractAddress)->estimateGas($functionName, $params, $callback);
|
||||
```
|
||||
|
||||
# Assign value to outside scope(from callback scope to outside scope)
|
||||
|
@ -153,7 +153,7 @@ class Ethabi
|
||||
throw new InvalidArgumentException('encodeParameters number of types must equal to number of params.');
|
||||
}
|
||||
$typesLength = count($types);
|
||||
$solidityTypes = array_fill(0, $typesLength, 0);
|
||||
$solidityTypes = $this->getSolidityTypes($types);
|
||||
|
||||
foreach ($types as $key => $type) {
|
||||
$match = [];
|
||||
@ -189,6 +189,101 @@ class Ethabi
|
||||
return '0x' . $this->encodeMultiWithOffset($types, $solidityTypes, $encodes, $dynamicOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* decodeParameter
|
||||
*
|
||||
* @param string $type
|
||||
* @param mixed $param
|
||||
* @return string
|
||||
*/
|
||||
public function decodeParameter($type, $param)
|
||||
{
|
||||
if (!is_string($type)) {
|
||||
throw new InvalidArgumentException('The type to decodeParameter must be string.');
|
||||
}
|
||||
return $this->decodeParameters([$type], $param)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* decodeParameters
|
||||
*
|
||||
* @param stdClass|array $type
|
||||
* @param string $param
|
||||
* @return string
|
||||
*/
|
||||
public function decodeParameters($types, $param)
|
||||
{
|
||||
if (!is_string($param)) {
|
||||
throw new InvalidArgumentException('The type or param to decodeParameters must be string.');
|
||||
}
|
||||
|
||||
// change json to array
|
||||
if ($types instanceof stdClass && isset($types->outputs)) {
|
||||
$types = Utils::jsonToArray($types, 2);
|
||||
}
|
||||
if (is_array($types) && isset($types['outputs'])) {
|
||||
$outputTypes = $types;
|
||||
$types = [];
|
||||
|
||||
foreach ($outputTypes['outputs'] as $output) {
|
||||
if (isset($output['type'])) {
|
||||
$types[] = $output['type'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$typesLength = count($types);
|
||||
$solidityTypes = $this->getSolidityTypes($types);
|
||||
$offsets = array_fill(0, $typesLength, 0);
|
||||
|
||||
for ($i=0; $i<$typesLength; $i++) {
|
||||
$offsets[$i] = $solidityTypes[$i]->staticPartLength($types[$i]);
|
||||
}
|
||||
for ($i=1; $i<$typesLength; $i++) {
|
||||
$offsets[$i] += $offsets[$i - 1];
|
||||
}
|
||||
for ($i=0; $i<$typesLength; $i++) {
|
||||
$offsets[$i] -= $solidityTypes[$i]->staticPartLength($types[$i]);
|
||||
}
|
||||
$result = [];
|
||||
$param = mb_strtolower(Utils::stripZero($param));
|
||||
|
||||
for ($i=0; $i<$typesLength; $i++) {
|
||||
$result[$i] = $solidityTypes[$i]->decode($param, $offsets[$i], $types[$i]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* getSolidityTypes
|
||||
*
|
||||
* @param array $types
|
||||
* @return array
|
||||
*/
|
||||
protected function getSolidityTypes($types)
|
||||
{
|
||||
if (!is_array($types)) {
|
||||
throw new InvalidArgumentException('Types must be array');
|
||||
}
|
||||
$solidityTypes = array_fill(0, count($types), 0);
|
||||
|
||||
foreach ($types as $key => $type) {
|
||||
$match = [];
|
||||
|
||||
if (preg_match('/^([a-zA-Z]+)/', $type, $match) === 1) {
|
||||
if (isset($this->types[$match[0]])) {
|
||||
$className = $this->types[$match[0]];
|
||||
|
||||
if (call_user_func([$this->types[$match[0]], 'isType'], $type) === false) {
|
||||
throw new InvalidArgumentException('Unsupport solidity parameter type: ' . $type);
|
||||
}
|
||||
$solidityTypes[$key] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $solidityTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* encodeWithOffset
|
||||
*
|
||||
|
@ -184,6 +184,16 @@ class SolidityType
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* isDynamicType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDynamicType()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* encode
|
||||
*
|
||||
@ -215,4 +225,54 @@ class SolidityType
|
||||
}
|
||||
return $this->inputFormat($value, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* decode
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $offset
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function decode($value, $offset, $name)
|
||||
{
|
||||
if ($this->isDynamicArray($name)) {
|
||||
$arrayOffset = (int) Utils::toBn('0x' . mb_substr($value, $offset * 2, 64))->toString();
|
||||
$length = (int) Utils::toBn('0x' . mb_substr($value, $arrayOffset * 2, 64))->toString();
|
||||
$arrayStart = $arrayOffset + 32;
|
||||
|
||||
$nestedName = $this->nestedName($name);
|
||||
$nestedStaticPartLength = $this->staticPartLength($nestedName);
|
||||
$roundedNestedStaticPartLength = floor(($nestedStaticPartLength + 31) / 32) * 32;
|
||||
$result = [];
|
||||
|
||||
for ($i=0; $i<$length * $roundedNestedStaticPartLength; $i+=$roundedNestedStaticPartLength) {
|
||||
$result[] = $this->decode($value, $arrayStart + $i, $nestedName);
|
||||
}
|
||||
return $result;
|
||||
} elseif ($this->isStaticArray($name)) {
|
||||
$length = $this->staticArrayLength($name);
|
||||
$arrayStart = $offset;
|
||||
|
||||
$nestedName = $this->nestedName($name);
|
||||
$nestedStaticPartLength = $this->staticPartLength($nestedName);
|
||||
$roundedNestedStaticPartLength = floor(($nestedStaticPartLength + 31) / 32) * 32;
|
||||
$result = [];
|
||||
|
||||
for ($i=0; $i<$length * $roundedNestedStaticPartLength; $i+=$roundedNestedStaticPartLength) {
|
||||
$result[] = $this->decode($value, $arrayStart + $i, $nestedName);
|
||||
}
|
||||
return $result;
|
||||
} elseif ($this->isDynamicType()) {
|
||||
$dynamicOffset = (int) Utils::toBn('0x' . mb_substr($value, $offset * 2, 64))->toString();
|
||||
$length = (int) Utils::toBn('0x' . mb_substr($value, $dynamicOffset * 2, 64))->toString();
|
||||
$roundedLength = floor(($length + 31) / 32);
|
||||
$param = mb_substr($value, $dynamicOffset * 2, ( 1 + $roundedLength) * 64);
|
||||
return $this->outputFormat($param, $name);
|
||||
}
|
||||
$length = $this->staticPartLength($name);
|
||||
$param = mb_substr($value, $offset * 2, $length * 2);
|
||||
|
||||
return $this->outputFormat($param, $name);
|
||||
}
|
||||
}
|
@ -73,4 +73,16 @@ class Address extends SolidityType implements IType
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* outputFormat
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function outputFormat($value, $name)
|
||||
{
|
||||
return '0x' . mb_substr($value, 24, 40);
|
||||
}
|
||||
}
|
@ -64,4 +64,18 @@ class Boolean extends SolidityType implements IType
|
||||
|
||||
return '000000000000000000000000000000000000000000000000000000000000000' . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* outputFormat
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function outputFormat($value, $name)
|
||||
{
|
||||
$value = (int) mb_substr($value, 63, 1);
|
||||
|
||||
return (bool) $value;
|
||||
}
|
||||
}
|
@ -75,4 +75,21 @@ class Bytes extends SolidityType implements IType
|
||||
|
||||
return $value . implode('', array_fill(0, $padding, '0'));
|
||||
}
|
||||
|
||||
/**
|
||||
* outputFormat
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function outputFormat($value, $name)
|
||||
{
|
||||
$checkZero = str_replace('0', '', $value);
|
||||
|
||||
if (empty($checkZero)) {
|
||||
return '0';
|
||||
}
|
||||
return '0x' . $value;
|
||||
}
|
||||
}
|
@ -74,4 +74,21 @@ class DynamicBytes extends SolidityType implements IType
|
||||
|
||||
return implode('', array_fill(0, 64-mb_strlen($bnHex), $padded)) . $bnHex . $value . implode('', array_fill(0, $padding, '0'));
|
||||
}
|
||||
|
||||
/**
|
||||
* outputFormat
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function outputFormat($value, $name)
|
||||
{
|
||||
$checkZero = str_replace('0', '', $value);
|
||||
|
||||
if (empty($checkZero)) {
|
||||
return '0';
|
||||
}
|
||||
return '0x' . $value;
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ use Web3\Utils;
|
||||
use Web3\Contracts\SolidityType;
|
||||
use Web3\Contracts\Types\IType;
|
||||
use Web3\Formatters\Integer as IntegerFormatter;
|
||||
use Web3\Formatters\BigNumberFormatter;
|
||||
|
||||
class Integer extends SolidityType implements IType
|
||||
{
|
||||
@ -60,4 +61,22 @@ class Integer extends SolidityType implements IType
|
||||
{
|
||||
return IntegerFormatter::format($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* outputFormat
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function outputFormat($value, $name)
|
||||
{
|
||||
$match = [];
|
||||
|
||||
if (preg_match('/^[0]+([a-f0-9]+)$/', $value, $match) === 1) {
|
||||
// due to value without 0x prefix, we will parse as decimal
|
||||
$value = '0x' . $match[1];
|
||||
}
|
||||
return BigNumberFormatter::format($value);
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ use Web3\Utils;
|
||||
use Web3\Contracts\SolidityType;
|
||||
use Web3\Contracts\Types\IType;
|
||||
use Web3\Formatters\Integer as IntegerFormatter;
|
||||
use Web3\Formatters\BigNumberFormatter;
|
||||
|
||||
class Str extends SolidityType implements IType
|
||||
{
|
||||
@ -65,4 +66,25 @@ class Str extends SolidityType implements IType
|
||||
|
||||
return $prefix . $value . implode('', array_fill(0, $padding, '0'));
|
||||
}
|
||||
|
||||
/**
|
||||
* outputFormat
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function outputFormat($value, $name)
|
||||
{
|
||||
$strLen = mb_substr($value, 0, 64);
|
||||
$strValue = mb_substr($value, 64, 64);
|
||||
$match = [];
|
||||
|
||||
if (preg_match('/^[0]+([a-f0-9]+)$/', $strLen, $match) === 1) {
|
||||
$strLen = BigNumberFormatter::format('0x' . $match[1])->toString();
|
||||
}
|
||||
$strValue = mb_substr($strValue, 0, (int) $strLen * 2);
|
||||
|
||||
return Utils::hexToBin($strValue);
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ use Web3\Utils;
|
||||
use Web3\Contracts\SolidityType;
|
||||
use Web3\Contracts\Types\IType;
|
||||
use Web3\Formatters\Integer as IntegerFormatter;
|
||||
use Web3\Formatters\BigNumberFormatter;
|
||||
|
||||
class Uinteger extends SolidityType implements IType
|
||||
{
|
||||
@ -60,4 +61,22 @@ class Uinteger extends SolidityType implements IType
|
||||
{
|
||||
return IntegerFormatter::format($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* outputFormat
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function outputFormat($value, $name)
|
||||
{
|
||||
$match = [];
|
||||
|
||||
if (preg_match('/^[0]+([a-f0-9]+)$/', $value, $match) === 1) {
|
||||
// due to value without 0x prefix, we will parse as decimal
|
||||
$value = '0x' . $match[1];
|
||||
}
|
||||
return BigNumberFormatter::format($value);
|
||||
}
|
||||
}
|
@ -56,13 +56,14 @@ class EthabiTest extends TestCase
|
||||
}';
|
||||
|
||||
/**
|
||||
* tests
|
||||
* from web3 eth.abi.encodeParameters test
|
||||
* encodingTests
|
||||
* from web3 abi.encodeParameter.js test
|
||||
* and web3 eth.abi.encodeParameters test
|
||||
* and web3 eth.abi.encodeParameter test
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
protected $tests = [
|
||||
protected $encodingTests = [
|
||||
[
|
||||
'params' => [['uint256','string'], ['2345675643', 'Hello!%']],
|
||||
'result' => '0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000'
|
||||
@ -108,6 +109,54 @@ class EthabiTest extends TestCase
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* decodingTests
|
||||
* from web3 abi.decodeParameter.js test
|
||||
* and web3 eth.abi.decodeParameters test
|
||||
* and web3 eth.abi.decodeParameter test
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
protected $decodingTests = [
|
||||
[
|
||||
'params' => [['uint256'], '0x0000000000000000000000000000000000000000000000000000000000000010'],
|
||||
'result' => ['16']
|
||||
], [
|
||||
'params' => [['string'], '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000848656c6c6f212521000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['Hello!%!']
|
||||
], [
|
||||
'params' => [['uint256','string'], '0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['2345675643', 'Hello!%']
|
||||
], [
|
||||
'params' => [['string'], '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['']
|
||||
], [
|
||||
'params' => [['int256'], '0x0000000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['0']
|
||||
], [
|
||||
'params' => [['uint256'], '0x0000000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['0']
|
||||
], [
|
||||
'params' => [['address'], '0x0000000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['0x0000000000000000000000000000000000000000']
|
||||
], [
|
||||
'params' => [['bool'], '0x0000000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => [false]
|
||||
], [
|
||||
'params' => [['bytes'], '0x0000000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['0']
|
||||
], [
|
||||
'params' => [['bytes32'], '0x0000000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['0']
|
||||
], [
|
||||
'params' => [['bytes32'], '0xdf32340000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['0xdf32340000000000000000000000000000000000000000000000000000000000']
|
||||
], [
|
||||
'params' => [['bytes32[]'], '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002df32340000000000000000000000000000000000000000000000000000000000fdfd000000000000000000000000000000000000000000000000000000000000'],
|
||||
'result' => ['0xdf32340000000000000000000000000000000000000000000000000000000000', '0xfdfd000000000000000000000000000000000000000000000000000000000000']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* setUp
|
||||
*
|
||||
@ -182,6 +231,18 @@ class EthabiTest extends TestCase
|
||||
$this->assertEquals($str, '0x095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba');
|
||||
}
|
||||
|
||||
/**
|
||||
* testEncodeParameter
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEncodeParameter()
|
||||
{
|
||||
$abi = $this->abi;
|
||||
|
||||
$this->assertEquals('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', $abi->encodeParameter('int256', '-1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* testEncodeParameters
|
||||
*
|
||||
@ -191,8 +252,45 @@ class EthabiTest extends TestCase
|
||||
{
|
||||
$abi = $this->abi;
|
||||
|
||||
foreach ($this->tests as $test) {
|
||||
foreach ($this->encodingTests as $test) {
|
||||
$this->assertEquals($test['result'], $abi->encodeParameters($test['params'][0], $test['params'][1]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* testDecodeParameter
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDecodeParameter()
|
||||
{
|
||||
$abi = $this->abi;
|
||||
|
||||
$this->assertEquals('16', $abi->decodeParameter('uint256', '0x0000000000000000000000000000000000000000000000000000000000000010')->toString());
|
||||
$this->assertEquals('16', $abi->decodeParameter('uint256', '0x0000000000000000000000000000000000000000000000000000000000000010')->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* testDecodeParameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDecodeParameters()
|
||||
{
|
||||
$abi = $this->abi;
|
||||
|
||||
foreach ($this->decodingTests as $test) {
|
||||
$decoded = $abi->decodeParameters($test['params'][0], $test['params'][1]);
|
||||
|
||||
foreach ($decoded as $key => $decoding) {
|
||||
if (!is_array($decoding)) {
|
||||
$this->assertEquals($test['result'][$key], $decoding);
|
||||
} else {
|
||||
foreach ($test['result'] as $rKey => $expected) {
|
||||
$this->assertEquals($expected, $decoding[$rKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -123,9 +123,20 @@ class SolidityTypeTest extends TestCase
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEncode()
|
||||
{
|
||||
$type = $this->type;
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
// public function testEncode()
|
||||
// {
|
||||
// $type = $this->type;
|
||||
// $this->assertTrue(true);
|
||||
// }
|
||||
|
||||
/**
|
||||
* testDecode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
// public function testDecode()
|
||||
// {
|
||||
// $type = $this->type;
|
||||
// $this->assertTrue(true);
|
||||
// }
|
||||
}
|
Loading…
Reference in New Issue
Block a user