From d35ecef5f76383bf5920e8860f2b4854f2101bc7 Mon Sep 17 00:00:00 2001 From: sc0Vu Date: Mon, 11 Feb 2019 00:10:14 +0800 Subject: [PATCH 1/3] Fix bytes and dynamic bytes format --- src/Contracts/Types/Bytes.php | 7 ++++--- src/Contracts/Types/DynamicBytes.php | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Contracts/Types/Bytes.php b/src/Contracts/Types/Bytes.php index 542ba74..f85fa03 100644 --- a/src/Contracts/Types/Bytes.php +++ b/src/Contracts/Types/Bytes.php @@ -63,9 +63,10 @@ class Bytes extends SolidityType implements IType } $value = Utils::stripZero($value); - // if (mb_strlen($value) % 2 !== 0) { - // throw new InvalidArgumentException('The value to inputFormat has invalid length. Value: ' . $value); - // } + if (mb_strlen($value) % 2 !== 0) { + $value = "0" . $value; + // throw new InvalidArgumentException('The value to inputFormat has invalid length. Value: ' . $value); + } if (mb_strlen($value) > 64) { throw new InvalidArgumentException('The value to inputFormat is too long.'); diff --git a/src/Contracts/Types/DynamicBytes.php b/src/Contracts/Types/DynamicBytes.php index e6461f8..7ca3d73 100644 --- a/src/Contracts/Types/DynamicBytes.php +++ b/src/Contracts/Types/DynamicBytes.php @@ -63,9 +63,10 @@ class DynamicBytes extends SolidityType implements IType } $value = Utils::stripZero($value); - // if (mb_strlen($value) % 2 !== 0) { - // throw new InvalidArgumentException('The value to inputFormat has invalid length.'); - // } + if (mb_strlen($value) % 2 !== 0) { + $value = "0" . $value; + // throw new InvalidArgumentException('The value to inputFormat has invalid length.'); + } $bn = Utils::toBn(floor(mb_strlen($value) / 2)); $bnHex = $bn->toHex(true); $padded = mb_substr($bnHex, 0, 1); @@ -98,4 +99,4 @@ class DynamicBytes extends SolidityType implements IType return '0x' . mb_substr($value, 64, $length); } -} +} \ No newline at end of file From cd1bcf3a1794bcb07b52dd2ca8f11fab071350fa Mon Sep 17 00:00:00 2001 From: sc0Vu Date: Mon, 11 Feb 2019 00:19:13 +0800 Subject: [PATCH 2/3] Add test contracts --- test/contracts/issue125.sol | 15 +++++++++++++++ test/contracts/issue134.sol | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 test/contracts/issue125.sol create mode 100644 test/contracts/issue134.sol diff --git a/test/contracts/issue125.sol b/test/contracts/issue125.sol new file mode 100644 index 0000000..eb3fa8a --- /dev/null +++ b/test/contracts/issue125.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.5.1; + +contract FIX125 { + + bytes public data; + + event SetData( + bytes indexed _data + ); + + function setData(bytes memory _data) public { + data = _data; + emit SetData(_data); + } +} \ No newline at end of file diff --git a/test/contracts/issue134.sol b/test/contracts/issue134.sol new file mode 100644 index 0000000..6f004a0 --- /dev/null +++ b/test/contracts/issue134.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.5.1; + +contract FIX134 { + bytes public data; + uint256 public number; + + event Say( + uint256 indexed _number + ); + + event Say( + uint256 indexed _number, + bytes indexed _data + ); + + function say(uint256 _number) public { + number = _number; + emit Say(_number); + } + + function say(uint256 _number, bytes memory _data) public { + data = _data; + number = _number; + emit Say(_number, _data); + } +} \ No newline at end of file From 2b5295533f3c76ec25d91bc22fd54b066cb601bb Mon Sep 17 00:00:00 2001 From: sc0Vu Date: Mon, 11 Feb 2019 22:11:29 +0800 Subject: [PATCH 3/3] Add support for same function name but with different parameters See issue #134 --- src/Contract.php | 260 +++++++++++++++++++++++++++++------- src/Contracts/Ethabi.php | 1 + test/unit/ContractTest.php | 261 +++++++++++++++++++++++++++++++++++-- 3 files changed, 463 insertions(+), 59 deletions(-) diff --git a/src/Contract.php b/src/Contract.php index 002b068..69c5c9b 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -129,7 +129,7 @@ class Contract foreach ($abiArray as $item) { if (isset($item['type'])) { if ($item['type'] === 'function') { - $this->functions[$item['name']] = $item; + $this->functions[] = $item; } elseif ($item['type'] === 'constructor') { $this->constructor = $item; } elseif ($item['type'] === 'event') { @@ -382,7 +382,7 @@ class Contract foreach ($abiArray as $item) { if (isset($item['type'])) { if ($item['type'] === 'function') { - $this->functions[$item['name']] = $item; + $this->functions[] = $item; } elseif ($item['type'] === 'constructor') { $this->constructor = $item; } elseif ($item['type'] === 'event') { @@ -451,26 +451,75 @@ class Contract $method = array_splice($arguments, 0, 1)[0]; $callback = array_pop($arguments); - if (!is_string($method) || !isset($this->functions[$method])) { - throw new InvalidArgumentException('Please make sure the method exists.'); + if (!is_string($method)) { + throw new InvalidArgumentException('Please make sure the method is string.'); } - $function = $this->functions[$method]; - if (count($arguments) < count($function['inputs'])) { - throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + $functions = []; + foreach ($this->functions as $function) { + if ($function["name"] === $method) { + $functions[] = $function; + } + }; + if (count($functions) < 1) { + throw new InvalidArgumentException('Please make sure the method exists.'); } if (is_callable($callback) !== true) { throw new \InvalidArgumentException('The last param must be callback function.'); } - $params = array_splice($arguments, 0, count($function['inputs'])); - $data = $this->ethabi->encodeParameters($function, $params); - $functionName = Utils::jsonMethodToString($function); - $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); - $transaction = []; - if (count($arguments) > 0) { - $transaction = $arguments[0]; + // check the last one in arguments is transaction object + $argsLen = count($arguments); + $transaction = []; + $hasTransaction = false; + + if ($argsLen > 0) { + $transaction = $arguments[$argsLen - 1]; } + if ( + isset($transaction["from"]) || + isset($transaction["to"]) || + isset($transaction["gas"]) || + isset($transaction["gasPrice"]) || + isset($transaction["value"]) || + isset($transaction["data"]) || + isset($transaction["nonce"]) + ) { + $hasTransaction = true; + } else { + $transaction = []; + } + + $params = []; + $data = ""; + $functionName = ""; + foreach ($functions as $function) { + if ($hasTransaction) { + if ($argsLen - 1 !== count($function['inputs'])) { + continue; + } else { + $paramsLen = $argsLen - 1; + } + } else { + if ($argsLen !== count($function['inputs'])) { + continue; + } else { + $paramsLen = $argsLen - 1; + } + } + try { + $params = array_splice($arguments, 0, $paramsLen); + $data = $this->ethabi->encodeParameters($function, $params); + $functionName = Utils::jsonMethodToString($function); + } catch (InvalidArgumentException $e) { + continue; + } + break; + } + if (empty($data) || empty($functionName)) { + throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + } + $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $transaction['to'] = $this->toAddress; $transaction['data'] = $functionSignature . Utils::stripZero($data); @@ -497,26 +546,75 @@ class Contract $method = array_splice($arguments, 0, 1)[0]; $callback = array_pop($arguments); - if (!is_string($method) || !isset($this->functions[$method])) { - throw new InvalidArgumentException('Please make sure the method exists.'); + if (!is_string($method)) { + throw new InvalidArgumentException('Please make sure the method is string.'); } - $function = $this->functions[$method]; - if (count($arguments) < count($function['inputs'])) { - throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + $functions = []; + foreach ($this->functions as $function) { + if ($function["name"] === $method) { + $functions[] = $function; + } + }; + if (count($functions) < 1) { + throw new InvalidArgumentException('Please make sure the method exists.'); } if (is_callable($callback) !== true) { throw new \InvalidArgumentException('The last param must be callback function.'); } - $params = array_splice($arguments, 0, count($function['inputs'])); - $data = $this->ethabi->encodeParameters($function, $params); - $functionName = Utils::jsonMethodToString($function); - $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); - $transaction = []; - if (count($arguments) > 0) { - $transaction = $arguments[0]; + // check the last one in arguments is transaction object + $argsLen = count($arguments); + $transaction = []; + $hasTransaction = false; + + if ($argsLen > 0) { + $transaction = $arguments[$argsLen - 1]; } + if ( + isset($transaction["from"]) || + isset($transaction["to"]) || + isset($transaction["gas"]) || + isset($transaction["gasPrice"]) || + isset($transaction["value"]) || + isset($transaction["data"]) || + isset($transaction["nonce"]) + ) { + $hasTransaction = true; + } else { + $transaction = []; + } + + $params = []; + $data = ""; + $functionName = ""; + foreach ($functions as $function) { + if ($hasTransaction) { + if ($argsLen - 1 !== count($function['inputs'])) { + continue; + } else { + $paramsLen = $argsLen - 1; + } + } else { + if ($argsLen !== count($function['inputs'])) { + continue; + } else { + $paramsLen = $argsLen - 1; + } + } + try { + $params = array_splice($arguments, 0, $paramsLen); + $data = $this->ethabi->encodeParameters($function, $params); + $functionName = Utils::jsonMethodToString($function); + } catch (InvalidArgumentException $e) { + continue; + } + break; + } + if (empty($data) || empty($functionName)) { + throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + } + $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $transaction['to'] = $this->toAddress; $transaction['data'] = $functionSignature . Utils::stripZero($data); @@ -567,26 +665,75 @@ class Contract } else { $method = array_splice($arguments, 0, 1)[0]; - if (!is_string($method) && !isset($this->functions[$method])) { - throw new InvalidArgumentException('Please make sure the method is existed.'); + if (!is_string($method)) { + throw new InvalidArgumentException('Please make sure the method is string.'); } - $function = $this->functions[$method]; - - if (count($arguments) < count($function['inputs'])) { - throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + + $functions = []; + foreach ($this->functions as $function) { + if ($function["name"] === $method) { + $functions[] = $function; + } + }; + if (count($functions) < 1) { + throw new InvalidArgumentException('Please make sure the method exists.'); } if (is_callable($callback) !== true) { throw new \InvalidArgumentException('The last param must be callback function.'); } - $params = array_splice($arguments, 0, count($function['inputs'])); - $data = $this->ethabi->encodeParameters($function, $params); - $functionName = Utils::jsonMethodToString($function); - $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); + + // check the last one in arguments is transaction object + $argsLen = count($arguments); $transaction = []; + $hasTransaction = false; - if (count($arguments) > 0) { - $transaction = $arguments[0]; + if ($argsLen > 0) { + $transaction = $arguments[$argsLen - 1]; } + if ( + isset($transaction["from"]) || + isset($transaction["to"]) || + isset($transaction["gas"]) || + isset($transaction["gasPrice"]) || + isset($transaction["value"]) || + isset($transaction["data"]) || + isset($transaction["nonce"]) + ) { + $hasTransaction = true; + } else { + $transaction = []; + } + + $params = []; + $data = ""; + $functionName = ""; + foreach ($functions as $function) { + if ($hasTransaction) { + if ($argsLen - 1 !== count($function['inputs'])) { + continue; + } else { + $paramsLen = $argsLen - 1; + } + } else { + if ($argsLen !== count($function['inputs'])) { + continue; + } else { + $paramsLen = $argsLen - 1; + } + } + try { + $params = array_splice($arguments, 0, $paramsLen); + $data = $this->ethabi->encodeParameters($function, $params); + $functionName = Utils::jsonMethodToString($function); + } catch (InvalidArgumentException $e) { + continue; + } + break; + } + if (empty($data) || empty($functionName)) { + throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); + } + $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $transaction['to'] = $this->toAddress; $transaction['data'] = $functionSignature . Utils::stripZero($data); } @@ -632,17 +779,38 @@ class Contract } else { $method = array_splice($arguments, 0, 1)[0]; - if (!is_string($method) && !isset($this->functions[$method])) { - throw new InvalidArgumentException('Please make sure the method is existed.'); + if (!is_string($method)) { + throw new InvalidArgumentException('Please make sure the method is string.'); } - $function = $this->functions[$method]; - - if (count($arguments) < count($function['inputs'])) { + + $functions = []; + foreach ($this->functions as $function) { + if ($function["name"] === $method) { + $functions[] = $function; + } + }; + if (count($functions) < 1) { + throw new InvalidArgumentException('Please make sure the method exists.'); + } + + $params = $arguments; + $data = ""; + $functionName = ""; + foreach ($functions as $function) { + if (count($arguments) !== count($function['inputs'])) { + continue; + } + try { + $data = $this->ethabi->encodeParameters($function, $params); + $functionName = Utils::jsonMethodToString($function); + } catch (InvalidArgumentException $e) { + continue; + } + break; + } + if (empty($data) || empty($functionName)) { throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); } - $params = array_splice($arguments, 0, count($function['inputs'])); - $data = $this->ethabi->encodeParameters($function, $params); - $functionName = Utils::jsonMethodToString($function); $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $functionData = Utils::stripZero($functionSignature) . Utils::stripZero($data); } diff --git a/src/Contracts/Ethabi.php b/src/Contracts/Ethabi.php index 5b8878d..694a563 100644 --- a/src/Contracts/Ethabi.php +++ b/src/Contracts/Ethabi.php @@ -100,6 +100,7 @@ class Ethabi /** * encodeEventSignature + * TODO: Fix same event name with different params * * @param string|stdClass|array $functionName * @return string diff --git a/test/unit/ContractTest.php b/test/unit/ContractTest.php index e29fbf2..66e7be7 100644 --- a/test/unit/ContractTest.php +++ b/test/unit/ContractTest.php @@ -491,7 +491,7 @@ class ContractTest extends TestCase } if ($transaction) { $this->contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); @@ -535,7 +535,7 @@ class ContractTest extends TestCase } if ($transaction) { $this->contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); @@ -562,7 +562,7 @@ class ContractTest extends TestCase } if ($transaction) { $topics = $transaction->logs[0]->topics; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; // validate topics $this->assertEquals($contract->ethabi->encodeEventSignature($this->contract->events['Transfer']), $topics[0]); @@ -611,7 +611,7 @@ class ContractTest extends TestCase } if ($transaction) { $this->contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); @@ -671,7 +671,7 @@ class ContractTest extends TestCase } if ($transaction) { $this->contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); @@ -744,7 +744,7 @@ class ContractTest extends TestCase } if ($transaction) { $this->contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); @@ -802,7 +802,7 @@ class ContractTest extends TestCase } if ($transaction) { $this->contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); @@ -831,7 +831,7 @@ class ContractTest extends TestCase } if ($transaction) { $topics = $transaction->logs[0]->topics; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; // validate topics $this->assertEquals($contract->ethabi->encodeEventSignature($this->contract->events['AddUser']), $topics[0]); @@ -1116,14 +1116,14 @@ class ContractTest extends TestCase } if ($transaction) { $contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); $contract->at($contractAddress); foreach ($functions as $function) { - $contract->call($function['name'], [], function ($err, $res) use ($function) { + $contract->call($function['name'], function ($err, $res) use ($function) { if ($err !== null) { echo 'Error when call ' . $function['name'] . '. Message: ' . $err->getMessage() . "\n"; return; @@ -1215,13 +1215,13 @@ class ContractTest extends TestCase } if ($transaction) { $contractAddress = $transaction->contractAddress; - echo "\nTransaction has mind:) block number: " . $transaction->blockNumber . "\n"; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; } }); }); $contract->at($contractAddress); - $contract->call('data', [], function ($err, $res) { + $contract->call('data', function ($err, $res) { if ($err !== null) { echo 'Error when call ' . $function['name'] . '. Message: ' . $err->getMessage() . "\n"; return; @@ -1251,7 +1251,7 @@ class ContractTest extends TestCase }); }); - $contract->call('data', [], function ($err, $res) { + $contract->call('data', function ($err, $res) { if ($err !== null) { echo 'Error when call ' . $function['name'] . '. Message: ' . $err->getMessage() . "\n"; return; @@ -1259,4 +1259,239 @@ class ContractTest extends TestCase $this->assertEquals('0x44aec9b900000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000000b', $res[0]); }); } + + /** + * testIssue134 + * + * @return void + */ + public function testIssue134() + { + $bytecode = '0x608060405234801561001057600080fd5b50610487806100206000396000f3fe60806040526004361061005c576000357c01000000000000000000000000000000000000000000000000000000009004806373d4a13a146100615780638381f58a146100f15780639a73c4091461011c578063c3fad957146101ee575b600080fd5b34801561006d57600080fd5b50610076610229565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b657808201518184015260208101905061009b565b50505050905090810190601f1680156100e35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100fd57600080fd5b506101066102c7565b6040518082815260200191505060405180910390f35b34801561012857600080fd5b506101ec6004803603604081101561013f57600080fd5b81019080803590602001909291908035906020019064010000000081111561016657600080fd5b82018360208201111561017857600080fd5b8035906020019184600183028401116401000000008311171561019a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506102cd565b005b3480156101fa57600080fd5b506102276004803603602081101561021157600080fd5b810190808035906020019092919050505061037f565b005b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102bf5780601f10610294576101008083540402835291602001916102bf565b820191906000526020600020905b8154815290600101906020018083116102a257829003601f168201915b505050505081565b60015481565b80600090805190602001906102e39291906103b6565b5081600181905550806040518082805190602001908083835b60208310151561032157805182526020820191506020810190506020830392506102fc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020827f95d23dc1ab7ea37e46bd697006b873f80ffecb4cb4ae085c7cc60a977ba7f2f560405160405180910390a35050565b80600181905550807f9d9c58c068c2bc9cb27d4e5c437f624ccbf4910ba8893ffd03ee311830becab160405160405180910390a250565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103f757805160ff1916838001178555610425565b82800160010185558215610425579182015b82811115610424578251825591602001919060010190610409565b5b5090506104329190610436565b5090565b61045891905b8082111561045457600081600090555060010161043c565b5090565b9056fea165627a7a72305820830ee43c52ac89b1fbc68647e1893aea17aa8ca132532723176508a6bc4586c90029'; + $abi = '[ + { + "constant": true, + "inputs": [], + "name": "data", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "number", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_number", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "say", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_number", + "type": "uint256" + } + ], + "name": "say", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_number", + "type": "uint256" + } + ], + "name": "Say", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_number", + "type": "uint256" + }, + { + "indexed": true, + "name": "_data", + "type": "bytes" + } + ], + "name": "Say", + "type": "event" + } + ]'; + $testNumber = 16; + $testData = "0x01234"; + $contractAddress = ""; + $contract = new Contract($this->web3->provider, $abi); + + if (!isset($this->accounts[0])) { + $account = '0x407d73d8a49eeb85d32cf465507dd71d507100c1'; + } else { + $account = $this->accounts[0]; + } + $contract = new Contract($this->web3->provider, $abi); + $contract->bytecode($bytecode)->new([ + 'from' => $account, + 'gas' => '0x200b20' + ], function ($err, $result) use ($contract, &$contractAddress) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if ($result) { + echo "\nTransaction has made:) id: " . $result . "\n"; + } + $transactionId = $result; + $this->assertTrue((preg_match('/^0x[a-f0-9]{64}$/', $transactionId) === 1)); + + $contract->eth->getTransactionReceipt($transactionId, function ($err, $transaction) use (&$contractAddress) { + if ($err !== null) { + return $this->fail($err); + } + if ($transaction) { + $contractAddress = $transaction->contractAddress; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; + } + }); + }); + $contract->at($contractAddress); + + // test for send transaction and get data + $contract->send('say', $testNumber, $testData, [ + 'from' => $account, + 'gas' => '0x200b20' + ], function ($err, $result) use ($contract, $testNumber, $testData) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if ($result) { + echo "\nTransaction has made:) id: " . $result . "\n"; + } + $transactionId = $result; + $this->assertTrue((preg_match('/^0x[a-f0-9]{64}$/', $transactionId) === 1)); + + $contract->eth->getTransactionReceipt($transactionId, function ($err, $transaction) use ($testNumber, $testData, $contract) { + if ($err !== null) { + return $this->fail($err); + } + if ($transaction) { + $topics = $transaction->logs[0]->topics; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; + + // validate topics + $this->assertEquals($contract->ethabi->encodeEventSignature($contract->events['Say']), $topics[0]); + $this->assertEquals('0x' . IntegerFormatter::format($testNumber), $topics[1], $topics[2]); + } + $contract->call('number', function ($err, $res) use ($testNumber) { + if ($err !== null) { + echo 'Error when call ' . $function['name'] . '. Message: ' . $err->getMessage() . "\n"; + return; + } + $this->assertEquals((string) $testNumber, $res[0]->toString()); + }); + }); + }); + $testNumber++; + + $contract->send('say', $testNumber, [ + 'from' => $account, + 'gas' => '0x200b20' + ], function ($err, $result) use ($contract, $testNumber) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if ($result) { + echo "\nTransaction has made:) id: " . $result . "\n"; + } + $transactionId = $result; + $this->assertTrue((preg_match('/^0x[a-f0-9]{64}$/', $transactionId) === 1)); + + $contract->eth->getTransactionReceipt($transactionId, function ($err, $transaction) use ($testNumber, $contract) { + if ($err !== null) { + return $this->fail($err); + } + if ($transaction) { + $topics = $transaction->logs[0]->topics; + echo "\nTransaction has mined:) block number: " . $transaction->blockNumber . "\n"; + + // validate topics + // $this->assertEquals($contract->ethabi->encodeEventSignature($contract->events['Say']), $topics[0]); + $this->assertEquals('0x' . IntegerFormatter::format($testNumber), $topics[1]); + } + $contract->call('number', function ($err, $res) use ($testNumber) { + if ($err !== null) { + echo 'Error when call ' . $function['name'] . '. Message: ' . $err->getMessage() . "\n"; + return; + } + $this->assertEquals((string) $testNumber, $res[0]->toString()); + }); + }); + }); + + // test for estimate gas + $contract->estimateGas("say", $testNumber, $testData, [ + 'from' => $account, + 'gas' => '0x200b20' + ], function ($err, $result) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if (isset($result)) { + echo "\nEstimate gas: " . $result->toString() . "\n"; + $this->assertTrue($result !== null); + } + }); + + $contract->estimateGas("say", $testNumber, [ + 'from' => $account, + 'gas' => '0x200b20' + ], function ($err, $result) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + if (isset($result)) { + echo "\nEstimate gas: " . $result->toString() . "\n"; + $this->assertTrue($result !== null); + } + }); + } } \ No newline at end of file