Merge remote-tracking branch 'origin/master' into fix-str

This commit is contained in:
Pavel Rubin 2018-09-01 11:29:22 -04:00
commit 15b11032f1
23 changed files with 460 additions and 51 deletions

View File

@ -233,7 +233,7 @@ git clone https://github.com/sc0Vu/web3.php.git
2. Copy web3.php to web3.php/docker/app directory and start container. 2. Copy web3.php to web3.php/docker/app directory and start container.
``` ```
cp files docker/app && docker-compose up -d php cp files docker/app && docker-compose up -d php ganache
``` ```
3. Enter php container and install packages. 3. Enter php container and install packages.
@ -241,11 +241,44 @@ cp files docker/app && docker-compose up -d php
docker-compose exec php ash docker-compose exec php ash
``` ```
4. Run test script 4. Change testHost in `TestCase.php`
```
/**
* testHost
*
* @var string
*/
protected $testHost = 'http://ganache:8545';
```
5. Run test script
``` ```
vendor/bin/phpunit vendor/bin/phpunit
``` ```
###### Install packages
Enter container first
```
docker-compose exec php ash
```
1. gmp
```
apk add gmp-dev
docker-php-ext-install gmp
```
2. bcmath
```
docker-php-ext-install bcmath
```
###### Remove extension
Move the extension config from `/usr/local/etc/php/conf.d/`
```
mv /usr/local/etc/php/conf.d/extension-config-name to/directory
```
# API # API
Todo. Todo.

View File

@ -13,7 +13,7 @@
"guzzlehttp/guzzle": "~6.0", "guzzlehttp/guzzle": "~6.0",
"PHP": "^7.1", "PHP": "^7.1",
"kornrunner/keccak": "~1.0", "kornrunner/keccak": "~1.0",
"phpseclib/phpseclib": "~2.0" "phpseclib/phpseclib": "~2.0.11"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~6.0" "phpunit/phpunit": "~6.0"

View File

@ -9,3 +9,10 @@ services:
volumes: volumes:
- ./app:/app - ./app:/app
tty: true tty: true
ganache:
build:
context: ./ganache
dockerfile: Dockerfile
ports:
- "8545"

View File

@ -0,0 +1,9 @@
FROM node:9.11.1-alpine
MAINTAINER Peter Lai <alk03073135@gmail.com>
RUN npm install -g ganache-cli
EXPOSE 8545
CMD ganache-cli -g 0 -l 6000000 --hostname=0.0.0.0

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
ganache-cli -g 0 -l 0 > /dev/null & ganache-cli -g 0 -l 6000000 > /dev/null &
ganachecli_pid=$! ganachecli_pid=$!
echo "Start ganache-cli pid: $ganachecli_pid and sleep 3 seconds" echo "Start ganache-cli pid: $ganachecli_pid and sleep 3 seconds"

View File

@ -22,6 +22,7 @@ use Web3\Contracts\Ethabi;
use Web3\Contracts\Types\Address; use Web3\Contracts\Types\Address;
use Web3\Contracts\Types\Boolean; use Web3\Contracts\Types\Boolean;
use Web3\Contracts\Types\Bytes; use Web3\Contracts\Types\Bytes;
use Web3\Contracts\Types\DynamicBytes;
use Web3\Contracts\Types\Integer; use Web3\Contracts\Types\Integer;
use Web3\Contracts\Types\Str; use Web3\Contracts\Types\Str;
use Web3\Contracts\Types\Uinteger; use Web3\Contracts\Types\Uinteger;
@ -133,6 +134,7 @@ class Contract
'address' => new Address, 'address' => new Address,
'bool' => new Boolean, 'bool' => new Boolean,
'bytes' => new Bytes, 'bytes' => new Bytes,
'dynamicBytes' => new DynamicBytes,
'int' => new Integer, 'int' => new Integer,
'string' => new Str, 'string' => new Str,
'uint' => new Uinteger, 'uint' => new Uinteger,
@ -233,6 +235,14 @@ class Contract
return $this->events; return $this->events;
} }
/**
* @return string
*/
public function getToAddress()
{
return $this->toAddress;
}
/** /**
* getConstructor * getConstructor
* *
@ -397,7 +407,6 @@ class Contract
if (count($arguments) > 0) { if (count($arguments) > 0) {
$transaction = $arguments[0]; $transaction = $arguments[0];
} }
$transaction['to'] = '';
$transaction['data'] = '0x' . $this->bytecode . Utils::stripZero($data); $transaction['data'] = '0x' . $this->bytecode . Utils::stripZero($data);
$this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){ $this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){
@ -423,8 +432,8 @@ class Contract
$method = array_splice($arguments, 0, 1)[0]; $method = array_splice($arguments, 0, 1)[0];
$callback = array_pop($arguments); $callback = array_pop($arguments);
if (!is_string($method) && !isset($this->functions[$method])) { if (!is_string($method) || !isset($this->functions[$method])) {
throw new InvalidArgumentException('Please make sure the method is existed.'); throw new InvalidArgumentException('Please make sure the method exists.');
} }
$function = $this->functions[$method]; $function = $this->functions[$method];
@ -469,8 +478,8 @@ class Contract
$method = array_splice($arguments, 0, 1)[0]; $method = array_splice($arguments, 0, 1)[0];
$callback = array_pop($arguments); $callback = array_pop($arguments);
if (!is_string($method) && !isset($this->functions[$method])) { if (!is_string($method) || !isset($this->functions[$method])) {
throw new InvalidArgumentException('Please make sure the method is existed.'); throw new InvalidArgumentException('Please make sure the method exists.');
} }
$function = $this->functions[$method]; $function = $this->functions[$method];
@ -622,4 +631,4 @@ class Contract
return $functionData; return $functionData;
} }
} }
} }

View File

@ -249,7 +249,7 @@ class Ethabi
$param = mb_strtolower(Utils::stripZero($param)); $param = mb_strtolower(Utils::stripZero($param));
for ($i=0; $i<$typesLength; $i++) { for ($i=0; $i<$typesLength; $i++) {
if (isset($outputTypes['outputs'][$i]['name'])) { if (isset($outputTypes['outputs'][$i]['name']) && empty($outputTypes['outputs'][$i]['name']) === false) {
$result[$outputTypes['outputs'][$i]['name']] = $solidityTypes[$i]->decode($param, $offsets[$i], $types[$i]); $result[$outputTypes['outputs'][$i]['name']] = $solidityTypes[$i]->decode($param, $offsets[$i], $types[$i]);
} else { } else {
$result[$i] = $solidityTypes[$i]->decode($param, $offsets[$i], $types[$i]); $result[$i] = $solidityTypes[$i]->decode($param, $offsets[$i], $types[$i]);
@ -280,7 +280,12 @@ class Ethabi
$className = $this->types[$match[0]]; $className = $this->types[$match[0]];
if (call_user_func([$this->types[$match[0]], 'isType'], $type) === false) { if (call_user_func([$this->types[$match[0]], 'isType'], $type) === false) {
throw new InvalidArgumentException('Unsupport solidity parameter type: ' . $type); // check dynamic bytes
if ($match[0] === 'bytes') {
$className = $this->types['dynamicBytes'];
} else {
throw new InvalidArgumentException('Unsupport solidity parameter type: ' . $type);
}
} }
$solidityTypes[$key] = $className; $solidityTypes[$key] = $className;
} }

View File

@ -37,7 +37,7 @@ class Address extends SolidityType implements IType
*/ */
public function isType($name) public function isType($name)
{ {
return (preg_match('/address(\[([0-9]*)\])*/', $name) === 1); return (preg_match('/^address(\[([0-9]*)\])*$/', $name) === 1);
} }
/** /**

View File

@ -35,7 +35,7 @@ class Boolean extends SolidityType implements IType
*/ */
public function isType($name) public function isType($name)
{ {
return (preg_match('/bool(\[([0-9]*)\])*/', $name) === 1); return (preg_match('/^bool(\[([0-9]*)\])*$/', $name) === 1);
} }
/** /**

View File

@ -36,7 +36,7 @@ class Bytes extends SolidityType implements IType
*/ */
public function isType($name) public function isType($name)
{ {
return (preg_match('/bytes([0-9]{1,})?(\[([0-9]*)\])*/', $name) === 1); return (preg_match('/^bytes([0-9]{1,})(\[([0-9]*)\])*$/', $name) === 1);
} }
/** /**
@ -90,6 +90,11 @@ class Bytes extends SolidityType implements IType
if (empty($checkZero)) { if (empty($checkZero)) {
return '0'; return '0';
} }
if (preg_match('/^bytes([0-9]*)/', $name, $match) === 1) {
$size = intval($match[1]);
$length = 2 * $size;
$value = mb_substr($value, 0, $length);
}
return '0x' . $value; return '0x' . $value;
} }
} }

View File

@ -36,7 +36,7 @@ class DynamicBytes extends SolidityType implements IType
*/ */
public function isType($name) public function isType($name)
{ {
return (preg_match('/bytes(\[([0-9]*)\])*/', $name) === 1); return (preg_match('/^bytes(\[([0-9]*)\])*$/', $name) === 1);
} }
/** /**
@ -89,6 +89,9 @@ class DynamicBytes extends SolidityType implements IType
if (empty($checkZero)) { if (empty($checkZero)) {
return '0'; return '0';
} }
return '0x' . $value; $size = intval(Utils::toBn(mb_substr($value, 0, 64))->toString());
$length = 2 * $size;
return '0x' . mb_substr($value, 64, $length);
} }
} }

View File

@ -37,7 +37,7 @@ class Integer extends SolidityType implements IType
*/ */
public function isType($name) public function isType($name)
{ {
return (preg_match('/int([0-9]{1,})?(\[([0-9]*)\])*/', $name) === 1); return (preg_match('/^int([0-9]{1,})?(\[([0-9]*)\])*$/', $name) === 1);
} }
/** /**

View File

@ -37,7 +37,7 @@ class Str extends SolidityType implements IType
*/ */
public function isType($name) public function isType($name)
{ {
return (preg_match('/string(\[([0-9]*)\])*/', $name) === 1); return (preg_match('/^string(\[([0-9]*)\])*$/', $name) === 1);
} }
/** /**
@ -77,7 +77,7 @@ class Str extends SolidityType implements IType
public function outputFormat($value, $name) public function outputFormat($value, $name)
{ {
$strLen = mb_substr($value, 0, 64); $strLen = mb_substr($value, 0, 64);
$strValue = mb_substr($value, 64, 64); $strValue = mb_substr($value, 64);
$match = []; $match = [];
if (preg_match('/^[0]+([a-f0-9]+)$/', $strLen, $match) === 1) { if (preg_match('/^[0]+([a-f0-9]+)$/', $strLen, $match) === 1) {
@ -87,4 +87,4 @@ class Str extends SolidityType implements IType
return Utils::hexToBin($strValue); return Utils::hexToBin($strValue);
} }
} }

View File

@ -37,7 +37,7 @@ class Uinteger extends SolidityType implements IType
*/ */
public function isType($name) public function isType($name)
{ {
return (preg_match('/uint([0-9]{1,})?(\[([0-9]*)\])*/', $name) === 1); return (preg_match('/^uint([0-9]{1,})?(\[([0-9]*)\])*$/', $name) === 1);
} }
/** /**

View File

@ -284,9 +284,7 @@ class Utils
if (is_array($bn)) { if (is_array($bn)) {
// fraction number // fraction number
list($whole, $fraction, $negative1) = $bn; list($whole, $fraction, $fractionLength, $negative1) = $bn;
$fractionLength = strlen($fraction->toString());
if ($fractionLength > strlen(self::UNITS[$unit])) { if ($fractionLength > strlen(self::UNITS[$unit])) {
throw new InvalidArgumentException('toWei fraction part is out of limit.'); throw new InvalidArgumentException('toWei fraction part is out of limit.');
@ -504,6 +502,7 @@ class Utils
return [ return [
new BigNumber($whole), new BigNumber($whole),
new BigNumber($fraction), new BigNumber($fraction),
strlen($comps[1]),
isset($negative1) ? $negative1 : false isset($negative1) ? $negative1 : false
]; ];
} else { } else {

View File

@ -16,22 +16,22 @@ class BytesTypeTest extends TestCase
protected $testTypes = [ protected $testTypes = [
[ [
'value' => 'bytes', 'value' => 'bytes',
'result' => true 'result' => false
], [ ], [
'value' => 'bytes[]', 'value' => 'bytes[]',
'result' => true 'result' => false
], [ ], [
'value' => 'bytes[4]', 'value' => 'bytes[4]',
'result' => true 'result' => false
], [ ], [
'value' => 'bytes[][]', 'value' => 'bytes[][]',
'result' => true 'result' => false
], [ ], [
'value' => 'bytes[3][]', 'value' => 'bytes[3][]',
'result' => true 'result' => false
], [ ], [
'value' => 'bytes[][6][]', 'value' => 'bytes[][6][]',
'result' => true 'result' => false
], [ ], [
'value' => 'bytes32', 'value' => 'bytes32',
'result' => true 'result' => true

File diff suppressed because one or more lines are too long

View File

@ -34,10 +34,10 @@ class DynamicBytesTypeTest extends TestCase
'result' => true 'result' => true
], [ ], [
'value' => 'bytes32', 'value' => 'bytes32',
'result' => true 'result' => false
], [ ], [
'value' => 'bytes8[4]', 'value' => 'bytes8[4]',
'result' => true 'result' => false
], ],
]; ];

View File

@ -9,6 +9,7 @@ use Web3\Contracts\Ethabi;
use Web3\Contracts\Types\Address; use Web3\Contracts\Types\Address;
use Web3\Contracts\Types\Boolean; use Web3\Contracts\Types\Boolean;
use Web3\Contracts\Types\Bytes; use Web3\Contracts\Types\Bytes;
use Web3\Contracts\Types\DynamicBytes;
use Web3\Contracts\Types\Integer; use Web3\Contracts\Types\Integer;
use Web3\Contracts\Types\Str; use Web3\Contracts\Types\Str;
use Web3\Contracts\Types\Uinteger; use Web3\Contracts\Types\Uinteger;
@ -179,9 +180,10 @@ class EthabiTest extends TestCase
'address' => new Address, 'address' => new Address,
'bool' => new Boolean, 'bool' => new Boolean,
'bytes' => new Bytes, 'bytes' => new Bytes,
'dynamicBytes' => new DynamicBytes,
'int' => new Integer, 'int' => new Integer,
'string' => new Str, 'string' => new Str,
'uint' => new Uinteger, 'uint' => new Uinteger
]); ]);
} }
@ -293,4 +295,23 @@ class EthabiTest extends TestCase
} }
} }
} }
/**
* testIssue71
* test 33 bytes and 128 bytes string, see: https://github.com/sc0Vu/web3.php/issues/71
* string generated from: https://www.lipsum.com/
*
* @return void
*/
public function testIssue71()
{
$abi = $this->abi;
$specialString = 'Lorem ipsum dolor sit amet metus.';
$encodedString = $abi->encodeParameter('string', $specialString);
$this->assertEquals($specialString, $abi->decodeParameter('string', $encodedString));
$specialString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce pulvinar quam felis, suscipit posuere neque aliquam in cras amet.';
$encodedString = $abi->encodeParameter('string', $specialString);
$this->assertEquals($specialString, $abi->decodeParameter('string', $encodedString));
}
} }

View File

@ -17,7 +17,7 @@ class HttpProviderTest extends TestCase
*/ */
public function testSend() public function testSend()
{ {
$requestManager = new HttpRequestManager('http://localhost:8545'); $requestManager = new HttpRequestManager($this->testHost);
$provider = new HttpProvider($requestManager); $provider = new HttpProvider($requestManager);
$method = new ClientVersion('web3_clientVersion', []); $method = new ClientVersion('web3_clientVersion', []);
@ -36,7 +36,7 @@ class HttpProviderTest extends TestCase
*/ */
public function testBatch() public function testBatch()
{ {
$requestManager = new HttpRequestManager('http://localhost:8545'); $requestManager = new HttpRequestManager($this->testHost);
$provider = new HttpProvider($requestManager); $provider = new HttpProvider($requestManager);
$method = new ClientVersion('web3_clientVersion', []); $method = new ClientVersion('web3_clientVersion', []);
$callback = function ($err, $data) { $callback = function ($err, $data) {

View File

@ -45,5 +45,11 @@ class IntegerFormatterTest extends TestCase
$hex = $formatter->format('1', 20); $hex = $formatter->format('1', 20);
$this->assertEquals($hex, implode('', array_fill(0, 19, '0')) . '1'); $this->assertEquals($hex, implode('', array_fill(0, 19, '0')) . '1');
$hex = $formatter->format(48);
$this->assertEquals($hex, implode('', array_fill(0, 62, '0')) . '30');
$hex = $formatter->format('48');
$this->assertEquals($hex, implode('', array_fill(0, 62, '0')) . '30');
} }
} }

View File

@ -63,7 +63,7 @@ class PersonalApiTest extends TestCase
$personal->newAccount('123456', function ($err, $account) { $personal->newAccount('123456', function ($err, $account) {
if ($err !== null) { if ($err !== null) {
return $this->fail($e->getMessage()); return $this->fail($err->getMessage());
} }
$this->assertTrue(is_string($account)); $this->assertTrue(is_string($account));
}); });
@ -81,7 +81,7 @@ class PersonalApiTest extends TestCase
// create account // create account
$personal->newAccount('123456', function ($err, $account) { $personal->newAccount('123456', function ($err, $account) {
if ($err !== null) { if ($err !== null) {
return $this->fail($e->getMessage()); return $this->fail($err->getMessage());
} }
$this->newAccount = $account; $this->newAccount = $account;
$this->assertTrue(is_string($account)); $this->assertTrue(is_string($account));
@ -107,7 +107,7 @@ class PersonalApiTest extends TestCase
// create account // create account
$personal->newAccount('123456', function ($err, $account) { $personal->newAccount('123456', function ($err, $account) {
if ($err !== null) { if ($err !== null) {
return $this->fail($e->getMessage()); return $this->fail($err->getMessage());
} }
$this->newAccount = $account; $this->newAccount = $account;
$this->assertTrue(is_string($account)); $this->assertTrue(is_string($account));
@ -133,7 +133,7 @@ class PersonalApiTest extends TestCase
// create account // create account
$personal->newAccount('123456', function ($err, $account) { $personal->newAccount('123456', function ($err, $account) {
if ($err !== null) { if ($err !== null) {
return $this->fail($e->getMessage()); return $this->fail($err->getMessage());
} }
$this->newAccount = $account; $this->newAccount = $account;
$this->assertTrue(is_string($account)); $this->assertTrue(is_string($account));

View File

@ -87,6 +87,16 @@ class UtilsTest extends TestCase
$this->assertEquals('0x', Utils::toHex(0, true)); $this->assertEquals('0x', Utils::toHex(0, true));
$this->assertEquals('0x', Utils::toHex(new BigNumber(0), true)); $this->assertEquals('0x', Utils::toHex(new BigNumber(0), true));
$this->assertEquals('0x30', Utils::toHex(48, true));
$this->assertEquals('0x30', Utils::toHex('48', true));
$this->assertEquals('30', Utils::toHex(48));
$this->assertEquals('30', Utils::toHex('48'));
$this->assertEquals('0x30', Utils::toHex(new BigNumber(48), true));
$this->assertEquals('0x30', Utils::toHex(new BigNumber('48'), true));
$this->assertEquals('30', Utils::toHex(new BigNumber(48)));
$this->assertEquals('30', Utils::toHex(new BigNumber('48')));
$this->expectException(InvalidArgumentException::class); $this->expectException(InvalidArgumentException::class);
$hex = Utils::toHex(new stdClass); $hex = Utils::toHex(new stdClass);
} }
@ -265,12 +275,24 @@ class UtilsTest extends TestCase
$bn = Utils::toWei('1.69', 'ether'); $bn = Utils::toWei('1.69', 'ether');
$this->assertEquals($bn->toString(), '1690000000000000000'); $this->assertEquals($bn->toString(), '1690000000000000000');
$bn = Utils::toWei('0.01', 'ether');
$this->assertEquals($bn->toString(), '10000000000000000');
$bn = Utils::toWei('0.002', 'ether');
$this->assertEquals($bn->toString(), '2000000000000000');
$bn = Utils::toWei(0.1, 'ether'); $bn = Utils::toWei(0.1, 'ether');
$this->assertEquals($bn->toString(), '100000000000000000'); $this->assertEquals($bn->toString(), '100000000000000000');
$bn = Utils::toWei(1.69, 'ether'); $bn = Utils::toWei(1.69, 'ether');
$this->assertEquals($bn->toString(), '1690000000000000000'); $this->assertEquals($bn->toString(), '1690000000000000000');
$bn = Utils::toWei(0.01, 'ether');
$this->assertEquals($bn->toString(), '10000000000000000');
$bn = Utils::toWei(0.002, 'ether');
$this->assertEquals($bn->toString(), '2000000000000000');
$bn = Utils::toWei('-0.1', 'ether'); $bn = Utils::toWei('-0.1', 'ether');
$this->assertEquals($bn->toString(), '-100000000000000000'); $this->assertEquals($bn->toString(), '-100000000000000000');
@ -531,39 +553,45 @@ class UtilsTest extends TestCase
$this->assertEquals($bn->toString(), '-1'); $this->assertEquals($bn->toString(), '-1');
$bn = Utils::toBn('-0.1'); $bn = Utils::toBn('-0.1');
$this->assertEquals(count($bn), 3); $this->assertEquals(count($bn), 4);
$this->assertEquals($bn[0]->toString(), '0'); $this->assertEquals($bn[0]->toString(), '0');
$this->assertEquals($bn[1]->toString(), '1'); $this->assertEquals($bn[1]->toString(), '1');
$this->assertEquals($bn[2]->toString(), '-1'); $this->assertEquals($bn[2], 1);
$this->assertEquals($bn[3]->toString(), '-1');
$bn = Utils::toBn(-0.1); $bn = Utils::toBn(-0.1);
$this->assertEquals(count($bn), 3); $this->assertEquals(count($bn), 4);
$this->assertEquals($bn[0]->toString(), '0'); $this->assertEquals($bn[0]->toString(), '0');
$this->assertEquals($bn[1]->toString(), '1'); $this->assertEquals($bn[1]->toString(), '1');
$this->assertEquals($bn[2]->toString(), '-1'); $this->assertEquals($bn[2], 1);
$this->assertEquals($bn[3]->toString(), '-1');
$bn = Utils::toBn('0.1'); $bn = Utils::toBn('0.1');
$this->assertEquals(count($bn), 3); $this->assertEquals(count($bn), 4);
$this->assertEquals($bn[0]->toString(), '0'); $this->assertEquals($bn[0]->toString(), '0');
$this->assertEquals($bn[1]->toString(), '1'); $this->assertEquals($bn[1]->toString(), '1');
$this->assertEquals($bn[2], false); $this->assertEquals($bn[2], 1);
$this->assertEquals($bn[3], false);
$bn = Utils::toBn('-1.69'); $bn = Utils::toBn('-1.69');
$this->assertEquals(count($bn), 3); $this->assertEquals(count($bn), 4);
$this->assertEquals($bn[0]->toString(), '1'); $this->assertEquals($bn[0]->toString(), '1');
$this->assertEquals($bn[1]->toString(), '69'); $this->assertEquals($bn[1]->toString(), '69');
$this->assertEquals($bn[2]->toString(), '-1'); $this->assertEquals($bn[2], 2);
$this->assertEquals($bn[3]->toString(), '-1');
$bn = Utils::toBn(-1.69); $bn = Utils::toBn(-1.69);
$this->assertEquals($bn[0]->toString(), '1'); $this->assertEquals($bn[0]->toString(), '1');
$this->assertEquals($bn[1]->toString(), '69'); $this->assertEquals($bn[1]->toString(), '69');
$this->assertEquals($bn[2]->toString(), '-1'); $this->assertEquals($bn[2], 2);
$this->assertEquals($bn[3]->toString(), '-1');
$bn = Utils::toBn('1.69'); $bn = Utils::toBn('1.69');
$this->assertEquals(count($bn), 3); $this->assertEquals(count($bn), 4);
$this->assertEquals($bn[0]->toString(), '1'); $this->assertEquals($bn[0]->toString(), '1');
$this->assertEquals($bn[1]->toString(), '69'); $this->assertEquals($bn[1]->toString(), '69');
$this->assertEquals($bn[2], false); $this->assertEquals($bn[2], 2);
$this->assertEquals($bn[3], false);
$bn = Utils::toBn(new BigNumber(1)); $bn = Utils::toBn(new BigNumber(1));
$this->assertEquals($bn->toString(), '1'); $this->assertEquals($bn->toString(), '1');