├── .github └── workflows │ └── php.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── src └── Util.php └── test ├── TestCase.php └── unit └── UtilTest.php /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP 2 | 3 | on: ["push", "pull_request"] 4 | 5 | jobs: 6 | build_and_test: 7 | name: Build and test ethereum-util with ${{ matrix.php-version }} 8 | strategy: 9 | matrix: 10 | php-version: ["7.3", "7.4", "8.0"] 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Setup PHP 16 | uses: shivammathur/setup-php@v2 17 | with: 18 | php-version: ${{ matrix.php-version }} 19 | 20 | - name: PHP version 21 | run: | 22 | php --version 23 | 24 | - uses: actions/checkout@v2 25 | 26 | - name: Validate composer.json and composer.lock 27 | run: composer validate 28 | 29 | - name: Cache Composer packages 30 | id: composer-cache 31 | uses: actions/cache@v2 32 | with: 33 | path: vendor 34 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 35 | restore-keys: | 36 | ${{ runner.os }}-php- 37 | 38 | - name: Install dependencies 39 | if: steps.composer-cache.outputs.cache-hit != 'true' 40 | run: composer install --prefer-dist --no-progress --no-suggest 41 | 42 | - name: Run test suite 43 | run: vendor/bin/phpunit --coverage-clover=coverage.xml 44 | 45 | - uses: codecov/codecov-action@v1 46 | with: 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | /vendor/ 3 | composer.lock 4 | .DS_Store 5 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file 6 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 7 | # composer.lock 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 web3p 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethereum-util 2 | 3 | [![PHP](https://github.com/web3p/ethereum-util/actions/workflows/php.yml/badge.svg)](https://github.com/web3p/ethereum-util/actions/workflows/php.yml) 4 | [![codecov](https://codecov.io/gh/web3p/ethereum-util/branch/master/graph/badge.svg)](https://codecov.io/gh/web3p/ethereum-util) 5 | [![Licensed under the MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/web3p/ethereum-util/blob/master/LICENSE) 6 | 7 | A collection of utility functions for Ethereum written in php. 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web3p/ethereum-util", 3 | "description": "A collection of utility functions for Ethereum written in PHP.", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "sc0Vu", 9 | "email": "alk03073135@gmail.com" 10 | } 11 | ], 12 | "require-dev": { 13 | "phpunit/phpunit": "~7 | ~8.0" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "Web3p\\EthereumUtil\\": "src/" 18 | } 19 | }, 20 | "autoload-dev": { 21 | "psr-4": { 22 | "Test\\": "test/" 23 | } 24 | }, 25 | "require": { 26 | "PHP": "^7.1 | ^8.0", 27 | "kornrunner/keccak": "~1", 28 | "simplito/elliptic-php": "~1.0.6", 29 | "phpseclib/phpseclib": "~2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./test/unit 15 | 16 | 17 | 18 | 19 | ./src 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Util.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * @author Peter Lai 9 | * @license MIT 10 | */ 11 | 12 | namespace Web3p\EthereumUtil; 13 | 14 | use InvalidArgumentException; 15 | use RuntimeException; 16 | use kornrunner\Keccak; 17 | use phpseclib\Math\BigInteger as BigNumber; 18 | use Elliptic\EC; 19 | use Elliptic\EC\KeyPair; 20 | use Elliptic\EC\Signature; 21 | 22 | class Util 23 | { 24 | /** 25 | * SHA3_NULL_HASH 26 | * 27 | * @const string 28 | */ 29 | const SHA3_NULL_HASH = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; 30 | 31 | /** 32 | * secp256k1 33 | * 34 | * @var \Elliptic\EC 35 | */ 36 | protected $secp256k1; 37 | 38 | /** 39 | * construct 40 | * 41 | * @return void 42 | */ 43 | public function __construct() 44 | { 45 | $this->secp256k1 = new EC('secp256k1'); 46 | } 47 | 48 | /** 49 | * get 50 | * 51 | * @param string $name 52 | * @return mixed 53 | */ 54 | public function __get($name) 55 | { 56 | $method = 'get' . ucfirst($name); 57 | 58 | if (method_exists($this, $method)) { 59 | return call_user_func_array([$this, $method], []); 60 | } 61 | return false; 62 | } 63 | 64 | /** 65 | * set 66 | * 67 | * @param string $name 68 | * @param mixed $value 69 | * @return void 70 | */ 71 | public function __set($name, $value) 72 | { 73 | $method = 'set' . ucfirst($name); 74 | 75 | if (method_exists($this, $method)) { 76 | call_user_func_array([$this, $method], [$value]); 77 | } 78 | } 79 | 80 | /** 81 | * sha3 82 | * keccak256 83 | * 84 | * @param string $value 85 | * @return null|string 86 | */ 87 | public function sha3(string $value) 88 | { 89 | $hash = Keccak::hash($value, 256); 90 | 91 | if ($hash === $this::SHA3_NULL_HASH) { 92 | return null; 93 | } 94 | return $hash; 95 | } 96 | 97 | /** 98 | * isZeroPrefixed 99 | * 100 | * @param string $value 101 | * @return bool 102 | */ 103 | public function isZeroPrefixed(string $value) 104 | { 105 | return (strpos($value, '0x') === 0); 106 | } 107 | 108 | /** 109 | * stripZero 110 | * 111 | * @param string $value 112 | * @return string 113 | */ 114 | public function stripZero(string $value) 115 | { 116 | if ($this->isZeroPrefixed($value)) { 117 | $count = 1; 118 | return str_replace('0x', '', $value, $count); 119 | } 120 | return $value; 121 | } 122 | 123 | /** 124 | * isHex 125 | * 126 | * @param string $value 127 | * @return bool 128 | */ 129 | public function isHex(string $value) 130 | { 131 | return (is_string($value) && preg_match('/^(0x)?[a-fA-F0-9]+$/', $value) === 1); 132 | } 133 | 134 | /** 135 | * publicKeyToAddress 136 | * 137 | * @param string $publicKey 138 | * @throws InvalidArgumentException 139 | * @return string 140 | */ 141 | public function publicKeyToAddress(string $publicKey) 142 | { 143 | if ($this->isHex($publicKey) === false) { 144 | throw new InvalidArgumentException('Invalid public key format.'); 145 | } 146 | $publicKey = $this->stripZero($publicKey); 147 | 148 | if (strlen($publicKey) !== 130) { 149 | throw new InvalidArgumentException('Invalid public key length.'); 150 | } 151 | return '0x' . substr($this->sha3(substr(hex2bin($publicKey), 1)), 24); 152 | } 153 | 154 | /** 155 | * privateKeyToPublicKey 156 | * 157 | * @param string $privateKey 158 | * @throws InvalidArgumentException 159 | * @return string 160 | */ 161 | public function privateKeyToPublicKey(string $privateKey) 162 | { 163 | if ($this->isHex($privateKey) === false) { 164 | throw new InvalidArgumentException('Invalid private key format.'); 165 | } 166 | $privateKey = $this->stripZero($privateKey); 167 | 168 | if (strlen($privateKey) !== 64) { 169 | throw new InvalidArgumentException('Invalid private key length.'); 170 | } 171 | $privateKey = $this->secp256k1->keyFromPrivate($privateKey, 'hex'); 172 | $publicKey = $privateKey->getPublic(false, 'hex'); 173 | 174 | return '0x' . $publicKey; 175 | } 176 | 177 | /** 178 | * recoverPublicKey 179 | * 180 | * @param string $hash 181 | * @param string $r 182 | * @param string $s 183 | * @param int $v 184 | * @throws InvalidArgumentException 185 | * @return string 186 | */ 187 | public function recoverPublicKey(string $hash, string $r, string $s, int $v) 188 | { 189 | if ($this->isHex($hash) === false) { 190 | throw new InvalidArgumentException('Invalid hash format.'); 191 | } 192 | $hash = $this->stripZero($hash); 193 | 194 | if ($this->isHex($r) === false || $this->isHex($s) === false) { 195 | throw new InvalidArgumentException('Invalid signature format.'); 196 | } 197 | $r = $this->stripZero($r); 198 | $s = $this->stripZero($s); 199 | 200 | if (strlen($r) !== 64 || strlen($s) !== 64) { 201 | throw new InvalidArgumentException('Invalid signature length.'); 202 | } 203 | $publicKey = $this->secp256k1->recoverPubKey($hash, [ 204 | 'r' => $r, 205 | 's' => $s 206 | ], $v); 207 | $publicKey = $publicKey->encode('hex'); 208 | 209 | return '0x' . $publicKey; 210 | } 211 | 212 | /** 213 | * ecsign 214 | * 215 | * @param string $privateKey 216 | * @param string $message 217 | * @throws InvalidArgumentException 218 | * @return \Elliptic\EC\Signature 219 | */ 220 | public function ecsign(string $privateKey, string $message) 221 | { 222 | if ($this->isHex($privateKey) === false) { 223 | throw new InvalidArgumentException('Invalid private key format.'); 224 | } 225 | $privateKeyLength = strlen($this->stripZero($privateKey)); 226 | 227 | if ($privateKeyLength % 2 !== 0 && $privateKeyLength !== 64) { 228 | throw new InvalidArgumentException('Private key length was wrong.'); 229 | } 230 | $secp256k1 = new EC('secp256k1'); 231 | $privateKey = $secp256k1->keyFromPrivate($privateKey, 'hex'); 232 | $signature = $privateKey->sign($message, [ 233 | 'canonical' => true 234 | ]); 235 | // Ethereum v is recovery param + 35 236 | // Or recovery param + 35 + (chain id * 2) 237 | $signature->recoveryParam += 35; 238 | 239 | return $signature; 240 | } 241 | 242 | /** 243 | * hasPersonalMessage 244 | * 245 | * @param string $message 246 | * @return string 247 | */ 248 | public function hashPersonalMessage(string $message) 249 | { 250 | $prefix = sprintf("\x19Ethereum Signed Message:\n%d", mb_strlen($message)); 251 | return $this->sha3($prefix . $message); 252 | } 253 | 254 | /** 255 | * isNegative 256 | * 257 | * @param string 258 | * @throws InvalidArgumentException 259 | * @return bool 260 | */ 261 | public function isNegative(string $value) 262 | { 263 | if (!is_string($value)) { 264 | throw new InvalidArgumentException('The value to isNegative function must be string.'); 265 | } 266 | return (strpos($value, '-') === 0); 267 | } 268 | 269 | /** 270 | * toBn 271 | * Change number or number string to bignumber. 272 | * 273 | * @param BigNumber|string|int $number 274 | * @throws InvalidArgumentException 275 | * @return array|\phpseclib\Math\BigInteger 276 | */ 277 | public function toBn($number) 278 | { 279 | if ($number instanceof BigNumber){ 280 | $bn = $number; 281 | } elseif (is_int($number)) { 282 | $bn = new BigNumber($number); 283 | } elseif (is_numeric($number)) { 284 | $number = (string) $number; 285 | 286 | if ($this->isNegative($number)) { 287 | $count = 1; 288 | $number = str_replace('-', '', $number, $count); 289 | $negative1 = new BigNumber(-1); 290 | } 291 | if (strpos($number, '.') > 0) { 292 | $comps = explode('.', $number); 293 | 294 | if (count($comps) > 2) { 295 | throw new InvalidArgumentException('toBn number must be a valid number.'); 296 | } 297 | $whole = $comps[0]; 298 | $fraction = $comps[1]; 299 | 300 | return [ 301 | new BigNumber($whole), 302 | new BigNumber($fraction), 303 | strlen($comps[1]), 304 | isset($negative1) ? $negative1 : false 305 | ]; 306 | } else { 307 | $bn = new BigNumber($number); 308 | } 309 | if (isset($negative1)) { 310 | $bn = $bn->multiply($negative1); 311 | } 312 | } elseif (is_string($number)) { 313 | $number = mb_strtolower($number); 314 | 315 | if ($this->isNegative($number)) { 316 | $count = 1; 317 | $number = str_replace('-', '', $number, $count); 318 | $negative1 = new BigNumber(-1); 319 | } 320 | if (empty($number)) { 321 | $bn = new BigNumber(0); 322 | } else if ($this->isZeroPrefixed($number) || $this->isHex($number)) { 323 | $number = $this->stripZero($number); 324 | $bn = new BigNumber($number, 16); 325 | } else { 326 | throw new InvalidArgumentException('toBn number must be valid hex string.'); 327 | } 328 | if (isset($negative1)) { 329 | $bn = $bn->multiply($negative1); 330 | } 331 | } else { 332 | throw new InvalidArgumentException('toBn number must be BigNumber, string or int.'); 333 | } 334 | return $bn; 335 | } 336 | } -------------------------------------------------------------------------------- /test/TestCase.php: -------------------------------------------------------------------------------- 1 | assertNull($util->sha3('')); 23 | $this->assertEquals('47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad', $util->sha3('hello world')); 24 | } 25 | 26 | /** 27 | * testIsZeroPrefixed 28 | * 29 | * @return void 30 | */ 31 | public function testIsZeroPrefixed() 32 | { 33 | $util = new Util; 34 | 35 | $this->assertTrue($util->isZeroPrefixed('0x1234')); 36 | $this->assertFalse($util->isZeroPrefixed('1234')); 37 | } 38 | 39 | /** 40 | * testStripZero 41 | * 42 | * @return void 43 | */ 44 | public function testStripZero() 45 | { 46 | $util = new Util; 47 | 48 | $this->assertEquals('1234', $util->stripZero('0x1234')); 49 | $this->assertEquals('1234', $util->stripZero('1234')); 50 | } 51 | 52 | /** 53 | * testIsHex 54 | * 55 | * @return void 56 | */ 57 | public function testIsHex() 58 | { 59 | $util = new Util; 60 | 61 | $this->assertTrue($util->isHex('1234')); 62 | $this->assertTrue($util->isHex('0x1234')); 63 | $this->assertFalse($util->isHex('hello world')); 64 | } 65 | 66 | /** 67 | * testPublicKeyToAddress 68 | * 69 | * @return void 70 | */ 71 | public function testPublicKeyToAddress() 72 | { 73 | $util = new Util; 74 | 75 | $this->assertEquals('0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f', $util->publicKeyToAddress('044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a')); 76 | $this->assertEquals('0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f', $util->publicKeyToAddress('0x044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a')); 77 | } 78 | 79 | /** 80 | * testPrivateKeyToPublicKey 81 | * 82 | * @return void 83 | */ 84 | public function testPrivateKeyToPublicKey() 85 | { 86 | $util = new Util; 87 | 88 | $this->assertEquals('0x044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a', $util->privateKeyToPublicKey('0x4646464646464646464646464646464646464646464646464646464646464646')); 89 | $this->assertEquals('0x044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a', $util->privateKeyToPublicKey('4646464646464646464646464646464646464646464646464646464646464646')); 90 | } 91 | 92 | /** 93 | * testRecoverPublicKey 94 | * 95 | * @return void 96 | */ 97 | public function testRecoverPublicKey() 98 | { 99 | $util = new Util; 100 | 101 | $this->assertEquals('0x044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a', $util->recoverPublicKey('0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53', '0x28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276', '0x67cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83', 0)); 102 | $this->assertEquals('0x044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a', $util->recoverPublicKey('daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53', '28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276', '67cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83', 0)); 103 | } 104 | 105 | /** 106 | * testEcsign 107 | * 108 | * @return void 109 | */ 110 | public function testEcsign() 111 | { 112 | $util = new Util; 113 | $signature = $util->ecsign('0x4646464646464646464646464646464646464646464646464646464646464646', 'daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53'); 114 | 115 | // EIP155 test data 116 | $this->assertEquals('28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276', $signature->r->toString(16)); 117 | $this->assertEquals('67cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83', $signature->s->toString(16)); 118 | $this->assertEquals(35, $signature->recoveryParam); 119 | } 120 | 121 | /** 122 | * testHashPersonalMessage 123 | * 124 | * @return void 125 | */ 126 | public function testHashPersonalMessage() 127 | { 128 | $util = new Util; 129 | $hashedMessage = $util->hashPersonalMessage('Hello world'); 130 | 131 | $this->assertEquals('8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede', $hashedMessage); 132 | } 133 | 134 | /** 135 | * testIsNegative 136 | * 137 | * @return void 138 | */ 139 | public function testIsNegative() 140 | { 141 | $util = new Util; 142 | $isNegative = $util->isNegative('-1'); 143 | $this->assertTrue($isNegative); 144 | 145 | $isNegative = $util->isNegative('1'); 146 | $this->assertFalse($isNegative); 147 | } 148 | 149 | /** 150 | * testToBn 151 | * 152 | * @return void 153 | */ 154 | public function testToBn() 155 | { 156 | $util = new Util; 157 | $bn = $util->toBn(''); 158 | $this->assertEquals($bn->toString(), '0'); 159 | 160 | $bn = $util->toBn(11); 161 | $this->assertEquals($bn->toString(), '11'); 162 | 163 | $bn = $util->toBn('0x12'); 164 | $this->assertEquals($bn->toString(), '18'); 165 | 166 | $bn = $util->toBn('-0x12'); 167 | $this->assertEquals($bn->toString(), '-18'); 168 | 169 | $bn = $util->toBn(0x12); 170 | $this->assertEquals($bn->toString(), '18'); 171 | 172 | $bn = $util->toBn('ae'); 173 | $this->assertEquals($bn->toString(), '174'); 174 | 175 | $bn = $util->toBn('-ae'); 176 | $this->assertEquals($bn->toString(), '-174'); 177 | 178 | $bn = $util->toBn('-1'); 179 | $this->assertEquals($bn->toString(), '-1'); 180 | 181 | $bn = $util->toBn('-0.1'); 182 | $this->assertEquals(count($bn), 4); 183 | $this->assertEquals($bn[0]->toString(), '0'); 184 | $this->assertEquals($bn[1]->toString(), '1'); 185 | $this->assertEquals($bn[2], 1); 186 | $this->assertEquals($bn[3]->toString(), '-1'); 187 | 188 | $bn = $util->toBn(-0.1); 189 | $this->assertEquals(count($bn), 4); 190 | $this->assertEquals($bn[0]->toString(), '0'); 191 | $this->assertEquals($bn[1]->toString(), '1'); 192 | $this->assertEquals($bn[2], 1); 193 | $this->assertEquals($bn[3]->toString(), '-1'); 194 | 195 | $bn = $util->toBn('0.1'); 196 | $this->assertEquals(count($bn), 4); 197 | $this->assertEquals($bn[0]->toString(), '0'); 198 | $this->assertEquals($bn[1]->toString(), '1'); 199 | $this->assertEquals($bn[2], 1); 200 | $this->assertEquals($bn[3], false); 201 | 202 | $bn = $util->toBn('-1.69'); 203 | $this->assertEquals(count($bn), 4); 204 | $this->assertEquals($bn[0]->toString(), '1'); 205 | $this->assertEquals($bn[1]->toString(), '69'); 206 | $this->assertEquals($bn[2], 2); 207 | $this->assertEquals($bn[3]->toString(), '-1'); 208 | 209 | $bn = $util->toBn(-1.69); 210 | $this->assertEquals($bn[0]->toString(), '1'); 211 | $this->assertEquals($bn[1]->toString(), '69'); 212 | $this->assertEquals($bn[2], 2); 213 | $this->assertEquals($bn[3]->toString(), '-1'); 214 | 215 | $bn = $util->toBn('1.69'); 216 | $this->assertEquals(count($bn), 4); 217 | $this->assertEquals($bn[0]->toString(), '1'); 218 | $this->assertEquals($bn[1]->toString(), '69'); 219 | $this->assertEquals($bn[2], 2); 220 | $this->assertEquals($bn[3], false); 221 | 222 | $bn = $util->toBn(new BigNumber(1)); 223 | $this->assertEquals($bn->toString(), '1'); 224 | $util->toBn(new BigNumber(1)); 225 | 226 | $this->expectException(InvalidArgumentException::class); 227 | $bn = $util->toBn(new stdClass); 228 | } 229 | } --------------------------------------------------------------------------------