├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── _config.yml
├── abi.json
├── composer.json
├── examples
├── account.php
├── amount.php
├── balance.php
├── contract.php
├── contract_balance.php
├── custom-nodes.php
├── find-transaction.php
├── hex.php
├── is-connection.php
├── option-trx.php
├── send-transaction.php
├── transactionBuilder.php
└── universal.php
├── phpunit.xml.dist
├── src
├── Concerns
│ ├── ManagesTronscan.php
│ └── ManagesUniversal.php
├── Exception
│ ├── ErrorException.php
│ ├── NotFoundException.php
│ ├── TRC20Exception.php
│ └── TronException.php
├── Provider
│ ├── HttpProvider.php
│ └── HttpProviderInterface.php
├── Support
│ ├── Base58.php
│ ├── Base58Check.php
│ ├── BigInteger.php
│ ├── Crypto.php
│ ├── Hash.php
│ ├── Keccak.php
│ ├── Secp.php
│ └── Utils.php
├── TRC20Contract.php
├── TransactionBuilder.php
├── Tron.php
├── TronAddress.php
├── TronAwareTrait.php
├── TronInterface.php
├── TronManager.php
└── trc20.json
└── tests
└── TronTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | # User-specific stuff
2 | .idea/**/workspace.xml
3 | .idea/php.xml
4 | .idea/vcs.xml
5 | .idea/**/tasks.xml
6 | .idea/**/usage.statistics.xml
7 | .idea/**/dictionaries
8 |
9 | # Generated files
10 | .idea/**/contentModel.xml
11 |
12 | # Sensitive or high-churn files
13 | .idea/**/dataSources/
14 | .idea/**/dataSources.ids
15 | .idea/**/dataSources.local.xml
16 | .idea/**/sqlDataSources.xml
17 | .idea/**/dynamic.xml
18 | .idea/**/uiDesigner.xml
19 | .idea/**/dbnavigator.xml
20 |
21 | # Gradle
22 | .idea/**/gradle.xml
23 | .idea/**/libraries
24 |
25 | # Gradle and Maven with auto-import
26 | # When using Gradle or Maven with auto-import, you should exclude module files,
27 | # since they will be recreated, and may cause churn. Uncomment if using
28 | # auto-import.
29 | # .idea/modules.xml
30 | # .idea/*.iml
31 | # .idea/modules
32 |
33 | # CMake
34 | cmake-build-*/
35 |
36 | # Mongo Explorer plugin
37 | .idea/**/mongoSettings.xml
38 |
39 | # File-based project format
40 | *.iws
41 |
42 | # IntelliJ
43 | out/
44 |
45 | # mpeltonen/sbt-idea plugin
46 | .idea_modules/
47 |
48 | # JIRA plugin
49 | atlassian-ide-plugin.xml
50 |
51 | # Cursive Clojure plugin
52 | .idea/replstate.xml
53 |
54 | # Crashlytics plugin (for Android Studio and IntelliJ)
55 | com_crashlytics_export_strings.xml
56 | crashlytics.properties
57 | crashlytics-build.properties
58 | fabric.properties
59 |
60 | # Editor-based Rest Client
61 | .idea/httpRequests
62 |
63 | examples/index.php
64 | composer.lock
65 | examples/index2.php
66 | vendor/
67 | .idea/inspectionProfiles/
68 | .idea/misc.xml
69 | .idea/modules.xml
70 | .idea/tron-api.iml
71 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.4
5 |
6 | matrix:
7 | fast_finish: true
8 |
9 | cache:
10 | directories:
11 | - $HOME/.composer/cache
12 |
13 | before_install:
14 | - travis_retry composer self-update
15 |
16 | install:
17 | - travis_retry composer install --no-interaction --prefer-dist
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 iEXBase
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 | # TRON API
2 | A PHP API for interacting with the Tron Protocol
3 |
4 | [](https://packagist.org/packages/iexbase/tron-api)
5 | [](LICENSE)
6 | [](https://travis-ci.com/iexbase/tron-api)
7 | [](https://github.com/iexbase/tron-api/graphs/contributors)
8 | [](https://packagist.org/packages/iexbase/tron-api)
9 |
10 | ## Install
11 |
12 | ```bash
13 | > composer require iexbase/tron-api --ignore-platform-reqs
14 | ```
15 | ## Requirements
16 |
17 | The following versions of PHP are supported by this version.
18 |
19 | * PHP 7.4
20 |
21 | ## Example Usage
22 |
23 | ```php
24 | use IEXBase\TronAPI\Tron;
25 |
26 | $fullNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
27 | $solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
28 | $eventServer = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
29 |
30 | try {
31 | $tron = new \IEXBase\TronAPI\Tron($fullNode, $solidityNode, $eventServer);
32 | } catch (\IEXBase\TronAPI\Exception\TronException $e) {
33 | exit($e->getMessage());
34 | }
35 |
36 |
37 | $this->setAddress('..');
38 | //Balance
39 | $tron->getBalance(null, true);
40 |
41 | // Transfer Trx
42 | var_dump($tron->send('to', 1.5));
43 |
44 | //Generate Address
45 | var_dump($tron->createAccount());
46 |
47 | //Get Last Blocks
48 | var_dump($tron->getLatestBlocks(2));
49 |
50 | //Change account name (only once)
51 | var_dump($tron->changeAccountName('address', 'NewName'));
52 |
53 |
54 | // Contract
55 | $tron->contract('Contract Address');
56 |
57 |
58 |
59 | ```
60 |
61 | ## Testing
62 |
63 | ``` bash
64 | $ vendor/bin/phpunit
65 | ```
66 |
67 | ## Donations
68 | **Tron(TRX)**: TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY
69 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/abi.json:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"number","type":"uint256"}],"name":"fibonacciNotify","outputs":[{"name":"result","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"number","type":"uint256"}],"name":"fibonacci","outputs":[{"name":"result","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"input","type":"uint256"},{"indexed":false,"name":"result","type":"uint256"}],"name":"Notify","type":"event"}]
2 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "iexbase/tron-api",
3 | "description": "A PHP API for interacting with Tron (Trx)",
4 | "license": "MIT",
5 | "type": "library",
6 | "homepage": "https://github.com/iexbase/tron-api",
7 | "authors": [
8 | {
9 | "name": "Shamsudin Serderov",
10 | "email": "steein.shamsudin@gmail.com"
11 | }
12 | ],
13 | "keywords": [
14 | "iexbase",
15 | "tron-lib",
16 | "tron-php",
17 | "tron-api",
18 | "tron-rest-api"
19 | ],
20 |
21 | "require": {
22 | "php": ">=8.0",
23 | "comely-io/data-types": "^1.0",
24 | "guzzlehttp/guzzle": "^7.2",
25 | "iexbase/web3.php": "^2.0.1",
26 | "kornrunner/secp256k1": "^0.2",
27 | "simplito/elliptic-php": "^1.0",
28 | "ext-json": "*",
29 | "ext-bcmath": "*"
30 | },
31 |
32 | "require-dev": {
33 | "phpunit/phpunit": "^6.0"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "IEXBase\\TronAPI\\": "src"
38 | }
39 | },
40 |
41 | "autoload-dev": {
42 | "psr-4": {
43 | "IEXBase\\TronAPI\\Test\\": "tests"
44 | }
45 | },
46 |
47 | "config": {
48 | "sort-packages": true
49 | },
50 |
51 | "minimum-stability": "dev",
52 | "prefer-stable": true
53 | }
54 |
--------------------------------------------------------------------------------
/examples/account.php:
--------------------------------------------------------------------------------
1 | generateAddress(); // or createAddress()
8 | $isValid = $tron->isAddress($generateAddress->getAddress());
9 |
10 |
11 | echo 'Address hex: '. $generateAddress->getAddress();
12 | echo 'Address base58: '. $generateAddress->getAddress(true);
13 | echo 'Private key: '. $generateAddress->getPrivateKey();
14 | echo 'Public key: '. $generateAddress->getPublicKey();
15 | echo 'Is Validate: '. $isValid;
16 |
17 | echo 'Raw data: '.$generateAddress->getRawData();
18 |
19 | } catch (\IEXBase\TronAPI\Exception\TronException $e) {
20 | echo $e->getMessage();
21 | }
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/amount.php:
--------------------------------------------------------------------------------
1 | toTron(1.15); //1150000
13 | $to = $tron->fromTron(11500000); //11.5000000
14 |
--------------------------------------------------------------------------------
/examples/balance.php:
--------------------------------------------------------------------------------
1 | getMessage());
12 | }
13 |
14 |
15 | $tron->setAddress('address');
16 | $balance = $tron->getBalance(null, true);
--------------------------------------------------------------------------------
/examples/contract.php:
--------------------------------------------------------------------------------
1 | getMessage();
14 | }
15 |
16 |
17 | try {
18 | $tron = new Tron($fullNode, $solidityNode, $eventServer, null, true);
19 | $contract = $tron->contract('TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'); // Tether USDT https://tronscan.org/#/token20/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
20 |
21 | // Data
22 | echo $contract->name();
23 | echo $contract->symbol();
24 | echo $contract->balanceOf();
25 | echo $contract->totalSupply();
26 | //echo $contract->transfer('to', 'amount', 'from');
27 |
28 |
29 | } catch (\IEXBase\TronAPI\Exception\TronException $e) {
30 | echo $e->getMessage();
31 | }
--------------------------------------------------------------------------------
/examples/contract_balance.php:
--------------------------------------------------------------------------------
1 | getMessage());
10 | }
11 |
12 |
13 | $balance=$tron->getTransactionBuilder()->contractbalance($tron->getAddress);
14 | foreach($balance as $key =>$item)
15 | {
16 | echo $item["name"]. " (".$item["symbol"].") => " . $item["balance"] . "\n";
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/examples/custom-nodes.php:
--------------------------------------------------------------------------------
1 | getMessage());
17 | }
--------------------------------------------------------------------------------
/examples/find-transaction.php:
--------------------------------------------------------------------------------
1 | getMessage());
12 | }
13 |
14 | $detail = $tron->getTransaction('TxId');
15 | var_dump($detail);
--------------------------------------------------------------------------------
/examples/hex.php:
--------------------------------------------------------------------------------
1 | toHex('TT67rPNwgmpeimvHUMVzFfKsjL9GZ1wGw8');
7 | //result: 41BBC8C05F1B09839E72DB044A6AA57E2A5D414A10
8 |
9 | $tron->fromHex('41BBC8C05F1B09839E72DB044A6AA57E2A5D414A10');
10 | //result: TT67rPNwgmpeimvHUMVzFfKsjL9GZ1wGw8
--------------------------------------------------------------------------------
/examples/is-connection.php:
--------------------------------------------------------------------------------
1 | getMessage());
12 | }
13 |
14 | $tron->isConnected();
15 |
--------------------------------------------------------------------------------
/examples/option-trx.php:
--------------------------------------------------------------------------------
1 | getMessage());
12 | }
13 |
14 | //option 1
15 | $tron->sendTransaction('to',0.1, 'hello');
16 |
17 | //option 2
18 | $tron->send('to',0.1);
19 |
20 | //option 3
21 | $tron->sendTrx('to',0.1);
22 |
--------------------------------------------------------------------------------
/examples/send-transaction.php:
--------------------------------------------------------------------------------
1 | getMessage());
12 | }
13 |
14 | $tron->setAddress('address');
15 | $tron->setPrivateKey('privateKey');
16 |
17 | try {
18 | $transfer = $tron->send( 'ToAddress', 1);
19 | } catch (\IEXBase\TronAPI\Exception\TronException $e) {
20 | die($e->getMessage());
21 | }
22 |
23 | var_dump($transfer);
--------------------------------------------------------------------------------
/examples/transactionBuilder.php:
--------------------------------------------------------------------------------
1 | getMessage());
10 | }
11 |
12 |
13 | try {
14 | $transaction = $tron->getTransactionBuilder()->sendTrx('to', 2,'fromAddress');
15 | $signedTransaction = $tron->signTransaction($transaction);
16 | $response = $tron->sendRawTransaction($signedTransaction);
17 | } catch (\IEXBase\TronAPI\Exception\TronException $e) {
18 | die($e->getMessage());
19 | }
20 |
--------------------------------------------------------------------------------
/examples/universal.php:
--------------------------------------------------------------------------------
1 | setPrivateKey('...');
8 |
9 |
10 | /**
11 | * check multi balances
12 | *
13 | * $address = [
14 | * ['address', 'isFromTron'],
15 | * ['address', 'isFromTron'],
16 | * ]
17 | */
18 |
19 | //address one -> TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY
20 | $addresses = [
21 | ['address one', true],
22 | ['address two', true],
23 | ['address three', false],
24 | ];
25 |
26 | //isValid (tron address) - default false
27 | $check = $tron->balances($addresses);
28 | var_dump($check);
29 |
30 |
31 | /**
32 | * send one to many
33 | *
34 | * $address = [
35 | * ['to address', 'amount float'],
36 | * ['to address', 'amount float'],
37 | * ]
38 | *
39 | * toAddress format: TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY
40 | */
41 |
42 |
43 | $toArray = [
44 | ['TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY', 0.1],
45 | ['TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY', 0.2],
46 | ['other address', 0.001]
47 | ];
48 |
49 | //default: $this->setPrivateKey();
50 | $send = $tron->sendOneToMany('from_address', $toArray, 'private_key alt');
51 | var_dump($send);
52 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | tests
15 |
16 |
17 |
18 |
19 | src/
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Concerns/ManagesTronscan.php:
--------------------------------------------------------------------------------
1 | manager->request('api/transaction', $options);
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Concerns/ManagesUniversal.php:
--------------------------------------------------------------------------------
1 | [],
16 | 'one_to_many' => []
17 | ];
18 |
19 | /**
20 | * Check multiple balances
21 | *
22 | * @param array $accounts
23 | * @param bool $isValid
24 | * @return array
25 | * @throws ErrorException
26 | */
27 | public function balances(array $accounts, $isValid = false): array
28 | {
29 | if(!is_array($accounts)) {
30 | throw new ErrorException('Data must be an array');
31 | }
32 |
33 | if(count($accounts) > 20) {
34 | throw new ErrorException('Once you can check 20 accounts');
35 | }
36 |
37 | foreach ($accounts as $item)
38 | {
39 | if($isValid && $this->validateAddress($item[0])['result'] == false) {
40 | throw new ErrorException($item[0].' invalid address');
41 | }
42 |
43 | array_push($this->attribute['balances'], [
44 | 'address' => $item[0],
45 | 'balance' => $this->getBalance($item[0], $item[1])
46 | ]);
47 | }
48 |
49 | return $this->attribute['balances'];
50 | }
51 |
52 | /**
53 | * We send funds to several addresses at once.
54 | *
55 | * @param string $from
56 | * @param array $to
57 | * @param null $private_key
58 | * @param bool $isValid
59 | * @return array
60 | * @throws ErrorException
61 | */
62 | public function sendOneToMany(array $to, $private_key = null, bool $isValid = false, string $from = null): array
63 | {
64 | if(!is_null($private_key)) {
65 | $this->privateKey = $private_key;
66 | }
67 |
68 | if(count($to) > 10) {
69 | throw new ErrorException('Allowed to send to "10" accounts');
70 | }
71 |
72 | foreach ($to as $item)
73 | {
74 | if($isValid && $this->validateAddress($item[0])['result'] == false) {
75 | throw new ErrorException($item[0].' invalid address');
76 | }
77 |
78 | array_push($this->attribute['one_to_many'],
79 | $this->send($item[0], $item[1], $from)
80 | );
81 | }
82 |
83 | return $this->attribute['one_to_many'];
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Exception/ErrorException.php:
--------------------------------------------------------------------------------
1 | host = $host;
75 | $this->timeout = $timeout;
76 | $this->statusPage = $statusPage;
77 | $this->headers = $headers;
78 |
79 | $this->httpClient = new Client([
80 | 'base_uri' => $host,
81 | 'timeout' => $timeout,
82 | 'auth' => $user && [$user, $password]
83 | ]);
84 | }
85 |
86 | /**
87 | * Enter a new page
88 | *
89 | * @param string $page
90 | */
91 | public function setStatusPage(string $page = '/'): void
92 | {
93 | $this->statusPage = $page;
94 | }
95 |
96 | /**
97 | * Check connection
98 | *
99 | * @return bool
100 | * @throws TronException
101 | */
102 | public function isConnected() : bool
103 | {
104 | $response = $this->request($this->statusPage);
105 |
106 | if(array_key_exists('blockID', $response)) {
107 | return true;
108 | } elseif(array_key_exists('status', $response)) {
109 | return true;
110 | }
111 | return false;
112 | }
113 |
114 | /**
115 | * Getting a host
116 | *
117 | * @return string
118 | */
119 | public function getHost(): string
120 | {
121 | return $this->host;
122 | }
123 |
124 | /**
125 | * Getting timeout
126 | *
127 | * @return int
128 | */
129 | public function getTimeout(): int
130 | {
131 | return $this->timeout;
132 | }
133 |
134 | /**
135 | * We send requests to the server
136 | *
137 | * @param $url
138 | * @param array $payload
139 | * @param string $method
140 | * @return array|mixed
141 | * @throws TronException
142 | */
143 | public function request($url, array $payload = [], string $method = 'get'): array
144 | {
145 | $method = strtoupper($method);
146 |
147 | if(!in_array($method, ['GET', 'POST'])) {
148 | throw new TronException('The method is not defined');
149 | }
150 |
151 | $options = [
152 | 'headers' => $this->headers,
153 | 'body' => json_encode($payload)
154 | ];
155 |
156 | $request = new Request($method, $url, $options['headers'], $options['body']);
157 | $rawResponse = $this->httpClient->send($request, $options);
158 |
159 | return $this->decodeBody(
160 | $rawResponse->getBody(),
161 | $rawResponse->getStatusCode()
162 | );
163 | }
164 |
165 | /**
166 | * Convert the original answer to an array
167 | *
168 | * @param StreamInterface $stream
169 | * @param int $status
170 | * @return array|mixed
171 | */
172 | protected function decodeBody(StreamInterface $stream, int $status): array
173 | {
174 | $decodedBody = json_decode($stream->getContents(),true);
175 |
176 | if((string)$stream == 'OK') {
177 | $decodedBody = [
178 | 'status' => 1
179 | ];
180 | }elseif ($decodedBody == null or !is_array($decodedBody)) {
181 | $decodedBody = [];
182 | }
183 |
184 | if($status == 404) {
185 | throw new NotFoundException('Page not found');
186 | }
187 |
188 | return $decodedBody;
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/Provider/HttpProviderInterface.php:
--------------------------------------------------------------------------------
1 | value = $this->initValue($value);
33 | $this->mutable = $mutable;
34 | }
35 |
36 | /**
37 | * Gets the value of the big integer.
38 | *
39 | * @return string
40 | */
41 | public function getValue(): string
42 | {
43 | return gmp_strval($this->value);
44 | }
45 |
46 | /**
47 | * Sets the value.
48 | *
49 | * @param string $value The value to set.
50 | * @return BigInteger
51 | */
52 | public function setValue(string $value): BigInteger
53 | {
54 | if (!$this->isMutable()) {
55 | throw new RuntimeException('Cannot set the value since the number is immutable.');
56 | }
57 |
58 | $this->value = $this->initValue($value);
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * Converts the value to an absolute number.
65 | *
66 | * @return BigInteger
67 | */
68 | public function abs(): BigInteger
69 | {
70 | $value = gmp_abs($this->value);
71 |
72 | return $this->assignValue($value);
73 | }
74 |
75 | /**
76 | * Adds the given value to this value.
77 | *
78 | * @param string $value The value to add.
79 | * @return BigInteger
80 | */
81 | public function add(string $value): BigInteger
82 | {
83 | $gmp = $this->initValue($value);
84 |
85 | $calculatedValue = gmp_add($this->value, $gmp);
86 |
87 | return $this->assignValue($calculatedValue);
88 | }
89 |
90 | /**
91 | * Compares this number and the given number.
92 | *
93 | * @param string $value The value to compare.
94 | * @return int Returns -1 is the number is less than this number. 0 if equal and 1 when greater.
95 | */
96 | public function cmp($value): int
97 | {
98 | $value = $this->initValue($value);
99 |
100 | $result = gmp_cmp($this->value, $value);
101 |
102 | // It could happen that gmp_cmp returns a value greater than one (e.g. gmp_cmp('123', '-123')). That's why
103 | // we do an additional check to make sure to return the correct value.
104 |
105 | if ($result > 0) {
106 | return 1;
107 | } elseif ($result < 0) {
108 | return -1;
109 | }
110 |
111 | return 0;
112 | }
113 |
114 | /**
115 | * Divides this value by the given value.
116 | *
117 | * @param string $value The value to divide by.
118 | * @return BigInteger
119 | */
120 | public function divide(string $value): BigInteger
121 | {
122 | $gmp = $this->initValue($value);
123 |
124 | $calculatedValue = gmp_div_q($this->value, $gmp, GMP_ROUND_ZERO);
125 |
126 | return $this->assignValue($calculatedValue);
127 | }
128 |
129 | /**
130 | * Calculates factorial of this value.
131 | *
132 | * @return BigInteger
133 | */
134 | public function factorial(): BigInteger
135 | {
136 | $calculatedValue = gmp_fact($this->getValue());
137 |
138 | return $this->assignValue($calculatedValue);
139 | }
140 |
141 | /**
142 | * Performs a modulo operation with the given number.
143 | *
144 | * @param string $value The value to perform a modulo operation with.
145 | * @return BigInteger
146 | */
147 | public function mod(string $value): BigInteger
148 | {
149 | $gmp = $this->initValue($value);
150 |
151 | $calculatedValue = gmp_mod($this->value, $gmp);
152 |
153 | return $this->assignValue($calculatedValue);
154 | }
155 |
156 | /**
157 | * Multiplies the given value with this value.
158 | *
159 | * @param string $value The value to multiply with.
160 | * @return BigInteger
161 | */
162 | public function multiply(string $value): BigInteger
163 | {
164 | $gmp = $this->initValue($value);
165 |
166 | $calculatedValue = gmp_mul($this->value, $gmp);
167 |
168 | return $this->assignValue($calculatedValue);
169 | }
170 |
171 | /**
172 | * Negates the value.
173 | *
174 | * @return BigInteger
175 | */
176 | public function negate(): BigInteger
177 | {
178 | $calculatedValue = gmp_neg($this->value);
179 |
180 | return $this->assignValue($calculatedValue);
181 | }
182 |
183 | /**
184 | * Performs a power operation with the given number.
185 | *
186 | * @param int $value The value to perform a power operation with.
187 | * @return BigInteger
188 | */
189 | public function pow(int $value): BigInteger
190 | {
191 | $calculatedValue = gmp_pow($this->value, $value);
192 |
193 | return $this->assignValue($calculatedValue);
194 | }
195 |
196 | /**
197 | * Subtracts the given value from this value.
198 | *
199 | * @param string $value The value to subtract.
200 | * @return BigInteger
201 | */
202 | public function subtract(string $value): BigInteger
203 | {
204 | $gmp = $this->initValue($value);
205 |
206 | $calculatedValue = gmp_sub($this->value, $gmp);
207 |
208 | return $this->assignValue($calculatedValue);
209 | }
210 |
211 | /**
212 | * Checks if the big integr is the prime number.
213 | *
214 | * @param float $probabilityFactor A normalized factor between 0 and 1 used for checking the probability.
215 | * @return bool Returns true if the number is a prime number false if not.
216 | */
217 | public function isPrimeNumber(float $probabilityFactor = 1.0): bool
218 | {
219 | $reps = (int)floor(($probabilityFactor * 5.0) + 5.0);
220 |
221 | if ($reps < 5 || $reps > 10) {
222 | throw new InvalidArgumentException('The provided probability number should be 5 to 10.');
223 | }
224 |
225 | return gmp_prob_prime($this->value, $reps) !== 0;
226 | }
227 |
228 | /**
229 | * Checks if this object is mutable.
230 | *
231 | * @return bool
232 | */
233 | public function isMutable(): bool
234 | {
235 | return $this->mutable;
236 | }
237 |
238 | /**
239 | * Converts this class to a string.
240 | *
241 | * @return string
242 | */
243 | public function toString(): string
244 | {
245 | return $this->getValue();
246 | }
247 |
248 | /**
249 | * Converts this class to a string.
250 | *
251 | * @return string
252 | */
253 | public function __toString(): string
254 | {
255 | return $this->toString();
256 | }
257 |
258 | /**
259 | * A helper method to assign the given value.
260 | *
261 | * @param GMP $value The value to assign.
262 | * @return BigInteger
263 | */
264 | private function assignValue(GMP $value): BigInteger
265 | {
266 | $rawValue = gmp_strval($value);
267 |
268 | if ($this->isMutable()) {
269 | $this->value = gmp_init($rawValue);
270 |
271 | return $this;
272 | }
273 |
274 | return new BigInteger($rawValue, false);
275 | }
276 |
277 | /**
278 | * Creates a new GMP object.
279 | *
280 | * @param string $value The value to initialize with.
281 | * @return GMP
282 | * @throws InvalidArgumentException Thrown when the value is invalid.
283 | */
284 | private function initValue(string $value): GMP
285 | {
286 | $result = @gmp_init($value);
287 |
288 | if ($result === false) {
289 | throw new InvalidArgumentException('The provided number is invalid.');
290 | }
291 |
292 | return $result;
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/src/Support/Crypto.php:
--------------------------------------------------------------------------------
1 | 256) {
15 | die("Invalid Base: " . $base);
16 | }
17 | bcscale(0);
18 | $value = "";
19 | if (!$digits) {
20 | $digits = self::digits($base);
21 | }
22 | while ($dec > $base - 1) {
23 | $rest = bcmod($dec, $base);
24 | $dec = bcdiv($dec, $base);
25 | $value = $digits[$rest] . $value;
26 | }
27 | $value = $digits[intval($dec)] . $value;
28 | return (string)$value;
29 | } else {
30 | die('Please install BCMATH');
31 | }
32 | }
33 |
34 | public static function base2dec($value, $base, $digits = false)
35 | {
36 | if (extension_loaded('bcmath')) {
37 | if ($base < 2 || $base > 256) {
38 | die("Invalid Base: " . $base);
39 | }
40 | bcscale(0);
41 | if ($base < 37) {
42 | $value = strtolower($value);
43 | }
44 | if (!$digits) {
45 | $digits = self::digits($base);
46 | }
47 | $size = strlen($value);
48 | $dec = "0";
49 | for ($loop = 0; $loop < $size; $loop++) {
50 | $element = strpos($digits, $value[$loop]);
51 | $power = bcpow($base, $size - $loop - 1);
52 | $dec = bcadd($dec, bcmul($element, $power));
53 | }
54 | return (string)$dec;
55 | } else {
56 | die('Please install BCMATH');
57 | }
58 | }
59 |
60 | public static function digits($base)
61 | {
62 | if ($base > 64) {
63 | $digits = "";
64 | for ($loop = 0; $loop < 256; $loop++) {
65 | $digits .= chr($loop);
66 | }
67 | } else {
68 | $digits = "0123456789abcdefghijklmnopqrstuvwxyz";
69 | $digits .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
70 | }
71 | $digits = substr($digits, 0, $base);
72 | return (string)$digits;
73 | }
74 |
75 | public static function bin2bc($num)
76 | {
77 | return self::base2dec($num, 256);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Support/Hash.php:
--------------------------------------------------------------------------------
1 | > 31)) & (0xFFFFFFFF),
39 | $bc[($i + 4) % 5][1] ^ (($bc[($i + 1) % 5][1] << 1) | ($bc[($i + 1) % 5][0] >> 31)) & (0xFFFFFFFF)
40 | ];
41 |
42 | for ($j = 0; $j < 25; $j += 5) {
43 | $st[$j + $i] = [
44 | $st[$j + $i][0] ^ $t[0],
45 | $st[$j + $i][1] ^ $t[1]
46 | ];
47 | }
48 | }
49 |
50 | // Rho Pi
51 | $t = $st[1];
52 | for ($i = 0; $i < 24; $i++) {
53 | $j = self::$keccakf_piln[$i];
54 |
55 | $bc[0] = $st[$j];
56 |
57 | $n = self::$keccakf_rotc[$i];
58 | $hi = $t[0];
59 | $lo = $t[1];
60 | if ($n >= 32) {
61 | $n -= 32;
62 | $hi = $t[1];
63 | $lo = $t[0];
64 | }
65 |
66 | $st[$j] =[
67 | (($hi << $n) | ($lo >> (32 - $n))) & (0xFFFFFFFF),
68 | (($lo << $n) | ($hi >> (32 - $n))) & (0xFFFFFFFF)
69 | ];
70 |
71 | $t = $bc[0];
72 | }
73 |
74 | // Chi
75 | for ($j = 0; $j < 25; $j += 5) {
76 | for ($i = 0; $i < 5; $i++) {
77 | $bc[$i] = $st[$j + $i];
78 | }
79 | for ($i = 0; $i < 5; $i++) {
80 | $st[$j + $i] = [
81 | $st[$j + $i][0] ^ ~$bc[($i + 1) % 5][0] & $bc[($i + 2) % 5][0],
82 | $st[$j + $i][1] ^ ~$bc[($i + 1) % 5][1] & $bc[($i + 2) % 5][1]
83 | ];
84 | }
85 | }
86 |
87 | // Iota
88 | $st[0] = [
89 | $st[0][0] ^ $keccakf_rndc[$round][0],
90 | $st[0][1] ^ $keccakf_rndc[$round][1]
91 | ];
92 | }
93 | }
94 |
95 | private static function keccak64($in_raw, int $capacity, int $outputlength, $suffix, bool $raw_output): string {
96 | $capacity /= 8;
97 |
98 | $inlen = mb_strlen($in_raw, self::ENCODING);
99 |
100 | $rsiz = 200 - 2 * $capacity;
101 | $rsizw = $rsiz / 8;
102 |
103 | $st = [];
104 | for ($i = 0; $i < 25; $i++) {
105 | $st[] = [0, 0];
106 | }
107 |
108 | for ($in_t = 0; $inlen >= $rsiz; $inlen -= $rsiz, $in_t += $rsiz) {
109 | for ($i = 0; $i < $rsizw; $i++) {
110 | $t = unpack('V*', mb_substr($in_raw, $i * 8 + $in_t, 8, self::ENCODING));
111 |
112 | $st[$i] = [
113 | $st[$i][0] ^ $t[2],
114 | $st[$i][1] ^ $t[1]
115 | ];
116 | }
117 |
118 | self::keccakf64($st, self::KECCAK_ROUNDS);
119 | }
120 |
121 | $temp = mb_substr($in_raw, $in_t, $inlen, self::ENCODING);
122 | $temp = str_pad($temp, $rsiz, "\x0", STR_PAD_RIGHT);
123 |
124 | $temp[$inlen] = chr($suffix);
125 | $temp[$rsiz - 1] = chr(ord($temp[$rsiz - 1]) | 0x80);
126 |
127 | for ($i = 0; $i < $rsizw; $i++) {
128 | $t = unpack('V*', mb_substr($temp, $i * 8, 8, self::ENCODING));
129 |
130 | $st[$i] = [
131 | $st[$i][0] ^ $t[2],
132 | $st[$i][1] ^ $t[1]
133 | ];
134 | }
135 |
136 | self::keccakf64($st, self::KECCAK_ROUNDS);
137 |
138 | $out = '';
139 | for ($i = 0; $i < 25; $i++) {
140 | $out .= $t = pack('V*', $st[$i][1], $st[$i][0]);
141 | }
142 | $r = mb_substr($out, 0, $outputlength / 8, self::ENCODING);
143 |
144 | return $raw_output ? $r : bin2hex($r);
145 | }
146 |
147 | private static function keccakf32(&$st, $rounds): void {
148 | $keccakf_rndc = [
149 | [0x0000, 0x0000, 0x0000, 0x0001], [0x0000, 0x0000, 0x0000, 0x8082], [0x8000, 0x0000, 0x0000, 0x0808a], [0x8000, 0x0000, 0x8000, 0x8000],
150 | [0x0000, 0x0000, 0x0000, 0x808b], [0x0000, 0x0000, 0x8000, 0x0001], [0x8000, 0x0000, 0x8000, 0x08081], [0x8000, 0x0000, 0x0000, 0x8009],
151 | [0x0000, 0x0000, 0x0000, 0x008a], [0x0000, 0x0000, 0x0000, 0x0088], [0x0000, 0x0000, 0x8000, 0x08009], [0x0000, 0x0000, 0x8000, 0x000a],
152 | [0x0000, 0x0000, 0x8000, 0x808b], [0x8000, 0x0000, 0x0000, 0x008b], [0x8000, 0x0000, 0x0000, 0x08089], [0x8000, 0x0000, 0x0000, 0x8003],
153 | [0x8000, 0x0000, 0x0000, 0x8002], [0x8000, 0x0000, 0x0000, 0x0080], [0x0000, 0x0000, 0x0000, 0x0800a], [0x8000, 0x0000, 0x8000, 0x000a],
154 | [0x8000, 0x0000, 0x8000, 0x8081], [0x8000, 0x0000, 0x0000, 0x8080], [0x0000, 0x0000, 0x8000, 0x00001], [0x8000, 0x0000, 0x8000, 0x8008]
155 | ];
156 |
157 | $bc = [];
158 | for ($round = 0; $round < $rounds; $round++) {
159 |
160 | // Theta
161 | for ($i = 0; $i < 5; $i++) {
162 | $bc[$i] = [
163 | $st[$i][0] ^ $st[$i + 5][0] ^ $st[$i + 10][0] ^ $st[$i + 15][0] ^ $st[$i + 20][0],
164 | $st[$i][1] ^ $st[$i + 5][1] ^ $st[$i + 10][1] ^ $st[$i + 15][1] ^ $st[$i + 20][1],
165 | $st[$i][2] ^ $st[$i + 5][2] ^ $st[$i + 10][2] ^ $st[$i + 15][2] ^ $st[$i + 20][2],
166 | $st[$i][3] ^ $st[$i + 5][3] ^ $st[$i + 10][3] ^ $st[$i + 15][3] ^ $st[$i + 20][3]
167 | ];
168 | }
169 |
170 | for ($i = 0; $i < 5; $i++) {
171 | $t = [
172 | $bc[($i + 4) % 5][0] ^ ((($bc[($i + 1) % 5][0] << 1) | ($bc[($i + 1) % 5][1] >> 15)) & (0xFFFF)),
173 | $bc[($i + 4) % 5][1] ^ ((($bc[($i + 1) % 5][1] << 1) | ($bc[($i + 1) % 5][2] >> 15)) & (0xFFFF)),
174 | $bc[($i + 4) % 5][2] ^ ((($bc[($i + 1) % 5][2] << 1) | ($bc[($i + 1) % 5][3] >> 15)) & (0xFFFF)),
175 | $bc[($i + 4) % 5][3] ^ ((($bc[($i + 1) % 5][3] << 1) | ($bc[($i + 1) % 5][0] >> 15)) & (0xFFFF))
176 | ];
177 |
178 | for ($j = 0; $j < 25; $j += 5) {
179 | $st[$j + $i] = [
180 | $st[$j + $i][0] ^ $t[0],
181 | $st[$j + $i][1] ^ $t[1],
182 | $st[$j + $i][2] ^ $t[2],
183 | $st[$j + $i][3] ^ $t[3]
184 | ];
185 | }
186 | }
187 |
188 | // Rho Pi
189 | $t = $st[1];
190 | for ($i = 0; $i < 24; $i++) {
191 | $j = self::$keccakf_piln[$i];
192 | $bc[0] = $st[$j];
193 |
194 |
195 | $n = self::$keccakf_rotc[$i] >> 4;
196 | $m = self::$keccakf_rotc[$i] % 16;
197 |
198 | $st[$j] = [
199 | ((($t[(0+$n) %4] << $m) | ($t[(1+$n) %4] >> (16-$m))) & (0xFFFF)),
200 | ((($t[(1+$n) %4] << $m) | ($t[(2+$n) %4] >> (16-$m))) & (0xFFFF)),
201 | ((($t[(2+$n) %4] << $m) | ($t[(3+$n) %4] >> (16-$m))) & (0xFFFF)),
202 | ((($t[(3+$n) %4] << $m) | ($t[(0+$n) %4] >> (16-$m))) & (0xFFFF))
203 | ];
204 |
205 | $t = $bc[0];
206 | }
207 |
208 | // Chi
209 | for ($j = 0; $j < 25; $j += 5) {
210 | for ($i = 0; $i < 5; $i++) {
211 | $bc[$i] = $st[$j + $i];
212 | }
213 | for ($i = 0; $i < 5; $i++) {
214 | $st[$j + $i] = [
215 | $st[$j + $i][0] ^ ~$bc[($i + 1) % 5][0] & $bc[($i + 2) % 5][0],
216 | $st[$j + $i][1] ^ ~$bc[($i + 1) % 5][1] & $bc[($i + 2) % 5][1],
217 | $st[$j + $i][2] ^ ~$bc[($i + 1) % 5][2] & $bc[($i + 2) % 5][2],
218 | $st[$j + $i][3] ^ ~$bc[($i + 1) % 5][3] & $bc[($i + 2) % 5][3]
219 | ];
220 | }
221 | }
222 |
223 | // Iota
224 | $st[0] = [
225 | $st[0][0] ^ $keccakf_rndc[$round][0],
226 | $st[0][1] ^ $keccakf_rndc[$round][1],
227 | $st[0][2] ^ $keccakf_rndc[$round][2],
228 | $st[0][3] ^ $keccakf_rndc[$round][3]
229 | ];
230 | }
231 | }
232 |
233 | private static function keccak32($in_raw, int $capacity, int $outputlength, $suffix, bool $raw_output): string {
234 | $capacity /= 8;
235 |
236 | $inlen = mb_strlen($in_raw, self::ENCODING);
237 |
238 | $rsiz = 200 - 2 * $capacity;
239 | $rsizw = $rsiz / 8;
240 |
241 | $st = [];
242 | for ($i = 0; $i < 25; $i++) {
243 | $st[] = [0, 0, 0, 0];
244 | }
245 |
246 | for ($in_t = 0; $inlen >= $rsiz; $inlen -= $rsiz, $in_t += $rsiz) {
247 | for ($i = 0; $i < $rsizw; $i++) {
248 | $t = unpack('v*', mb_substr($in_raw, $i * 8 + $in_t, 8, self::ENCODING));
249 |
250 | $st[$i] = [
251 | $st[$i][0] ^ $t[4],
252 | $st[$i][1] ^ $t[3],
253 | $st[$i][2] ^ $t[2],
254 | $st[$i][3] ^ $t[1]
255 | ];
256 | }
257 |
258 | self::keccakf32($st, self::KECCAK_ROUNDS);
259 | }
260 |
261 | $temp = mb_substr($in_raw, $in_t, $inlen, self::ENCODING);
262 | $temp = str_pad($temp, $rsiz, "\x0", STR_PAD_RIGHT);
263 |
264 | $temp[$inlen] = chr($suffix);
265 | $temp[$rsiz - 1] = chr((int) $temp[$rsiz - 1] | 0x80);
266 |
267 | for ($i = 0; $i < $rsizw; $i++) {
268 | $t = unpack('v*', mb_substr($temp, $i * 8, 8, self::ENCODING));
269 |
270 | $st[$i] = [
271 | $st[$i][0] ^ $t[4],
272 | $st[$i][1] ^ $t[3],
273 | $st[$i][2] ^ $t[2],
274 | $st[$i][3] ^ $t[1]
275 | ];
276 | }
277 |
278 | self::keccakf32($st, self::KECCAK_ROUNDS);
279 |
280 | $out = '';
281 | for ($i = 0; $i < 25; $i++) {
282 | $out .= $t = pack('v*', $st[$i][3],$st[$i][2], $st[$i][1], $st[$i][0]);
283 | }
284 | $r = mb_substr($out, 0, $outputlength / 8, self::ENCODING);
285 |
286 | return $raw_output ? $r: bin2hex($r);
287 | }
288 |
289 | private static function keccak($in_raw, int $capacity, int $outputlength, $suffix, bool $raw_output): string {
290 | return self::$x64
291 | ? self::keccak64($in_raw, $capacity, $outputlength, $suffix, $raw_output)
292 | : self::keccak32($in_raw, $capacity, $outputlength, $suffix, $raw_output);
293 | }
294 |
295 | public static function hash($in, int $mdlen, bool $raw_output = false): string {
296 | if (!in_array($mdlen, [224, 256, 384, 512], true)) {
297 | throw new Exception('Unsupported Keccak Hash output size.');
298 | }
299 |
300 | return self::keccak($in, $mdlen, $mdlen, self::LFSR, $raw_output);
301 | }
302 |
303 | public static function shake($in, int $security_level, int $outlen, bool $raw_output = false): string {
304 | if (!in_array($security_level, [128, 256], true)) {
305 | throw new Exception('Unsupported Keccak Shake security level.');
306 | }
307 |
308 | return self::keccak($in, $security_level, $outlen, 0x1f, $raw_output);
309 | }
310 |
311 | }
--------------------------------------------------------------------------------
/src/Support/Secp.php:
--------------------------------------------------------------------------------
1 | sign($message, $privateKey, ['canonical' => false]);
18 |
19 | return $sign->toHex() . bin2hex(implode('', array_map('chr', [$sign->getRecoveryParam()])));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Support/Utils.php:
--------------------------------------------------------------------------------
1 |
7 | * @license https://github.com/iexbase/tron-api/blob/master/LICENSE (MIT License)
8 | * @version 1.3.4
9 | * @link https://github.com/iexbase/tron-api
10 | *
11 | * For the full copyright and license information, please view the LICENSE
12 | * file that was distributed with this source code.
13 | */
14 |
15 | declare(strict_types=1);
16 |
17 | namespace IEXBase\TronAPI;
18 |
19 | use Comely\DataTypes\BcNumber;
20 | use IEXBase\TronAPI\Exception\TRC20Exception;
21 | use IEXBase\TronAPI\Exception\TronException;
22 |
23 | /**
24 | * Class TRC20Contract
25 | * @package TronAPI
26 | */
27 | class TRC20Contract
28 | {
29 | const TRX_TO_SUN = 1000000;
30 |
31 | /***
32 | * Maximum decimal supported by the Token
33 | *
34 | * @var integer|null
35 | */
36 | private ?int $_decimals = null;
37 |
38 | /***
39 | * Token Name
40 | *
41 | * @var string|null
42 | */
43 | private ?string $_name = null;
44 |
45 | /***
46 | * Token Symbol
47 | *
48 | * @var string|null
49 | */
50 | private ?string $_symbol = null;
51 |
52 | /**
53 | * The smart contract which issued TRC20 Token
54 | *
55 | * @var string
56 | */
57 | private string $contractAddress;
58 |
59 | /**
60 | * ABI Data
61 | *
62 | * @var string|null
63 | */
64 | private $abiData;
65 |
66 | /**
67 | * Fee Limit
68 | *
69 | * @var integer
70 | */
71 | private int $feeLimit = 10;
72 |
73 | /**
74 | * Base Tron object
75 | *
76 | * @var Tron
77 | */
78 | protected Tron $_tron;
79 |
80 | /**
81 | * Total Supply
82 | *
83 | * @var string|null
84 | */
85 | private ?string $_totalSupply = null;
86 |
87 | /**
88 | * Create Trc20 Contract
89 | *
90 | * @param Tron $tron
91 | * @param string $contractAddress
92 | * @param string|null $abi
93 | */
94 | public function __construct(Tron $tron, string $contractAddress, string $abi = null)
95 | {
96 | $this->_tron = $tron;
97 |
98 | // If abi is absent, then it takes by default
99 | if(is_null($abi)) {
100 | $abi = file_get_contents(__DIR__.'/trc20.json');
101 | }
102 |
103 | $this->abiData = json_decode($abi, true);
104 | $this->contractAddress = $contractAddress;
105 | }
106 |
107 | /**
108 | * Debug Info
109 | *
110 | * @return array
111 | * @throws TronException
112 | */
113 | public function __debugInfo(): array
114 | {
115 | return $this->array();
116 | }
117 |
118 | /**
119 | * Clears cached values
120 | *
121 | * @return void
122 | */
123 | public function clearCached(): void
124 | {
125 | $this->_name = null;
126 | $this->_symbol = null;
127 | $this->_decimals = null;
128 | $this->_totalSupply = null;
129 | }
130 |
131 | /**
132 | * All data
133 | *
134 | * @throws TronException
135 | */
136 | public function array(): array
137 | {
138 | return [
139 | 'name' => $this->name(),
140 | 'symbol' => $this->symbol(),
141 | 'decimals' => $this->decimals(),
142 | 'totalSupply' => $this->totalSupply(true)
143 | ];
144 | }
145 |
146 | /**
147 | * Get token name
148 | *
149 | * @return string
150 | * @throws TronException
151 | */
152 | public function name(): string
153 | {
154 | if ($this->_name) {
155 | return $this->_name;
156 | }
157 |
158 | $result = $this->trigger('name', null, []);
159 | $name = $result[0] ?? null;
160 |
161 | if (!is_string($name)) {
162 | throw new TRC20Exception('Failed to retrieve TRC20 token name');
163 | }
164 |
165 | $this->_name = $this->cleanStr($name);
166 | return $this->_name;
167 | }
168 |
169 | /**
170 | * Get symbol name
171 | *
172 | * @return string
173 | * @throws TronException
174 | */
175 | public function symbol(): string
176 | {
177 | if ($this->_symbol) {
178 | return $this->_symbol;
179 | }
180 | $result = $this->trigger('symbol', null, []);
181 | $code = $result[0] ?? null;
182 |
183 | if (!is_string($code)) {
184 | throw new TRC20Exception('Failed to retrieve TRRC20 token symbol');
185 | }
186 |
187 | $this->_symbol = $this->cleanStr($code);
188 | return $this->_symbol;
189 | }
190 |
191 | /**
192 | * The total number of tokens issued on the main network
193 | *
194 | * @param bool $scaled
195 | * @return string
196 | * @throws Exception\TronException
197 | * @throws TRC20Exception
198 | */
199 | public function totalSupply(bool $scaled = true): string
200 | {
201 | if (!$this->_totalSupply) {
202 |
203 | $result = $this->trigger('totalSupply', null, []);
204 | $totalSupply = $result[0]->toString() ?? null;
205 |
206 | if (!is_string($totalSupply) || !preg_match('/^[0-9]+$/', $totalSupply)) {
207 | throw new TRC20Exception('Failed to retrieve TRC20 token totalSupply');
208 | }
209 |
210 | $this->_totalSupply = $totalSupply;
211 | }
212 |
213 | return $scaled ? $this->decimalValue($this->_totalSupply, $this->decimals()) : $this->_totalSupply;
214 | }
215 |
216 | /**
217 | * Maximum decimal supported by the Token
218 | *
219 | * @throws TRC20Exception
220 | * @throws TronException
221 | */
222 | public function decimals(): int
223 | {
224 | if ($this->_decimals) {
225 | return $this->_decimals;
226 | }
227 |
228 | $result = $this->trigger('decimals', null, []);
229 | $scale = intval($result[0]->toString() ?? null);
230 |
231 | if (is_null($scale)) {
232 | throw new TRC20Exception('Failed to retrieve TRC20 token decimals/scale value');
233 | }
234 |
235 | $this->_decimals = $scale;
236 | return $this->_decimals;
237 | }
238 |
239 | /**
240 | * Balance TRC20 contract
241 | *
242 | * @param string|null $address
243 | * @param bool $scaled
244 | * @return string
245 | * @throws TRC20Exception
246 | * @throws TronException
247 | */
248 | public function balanceOf(string $address = null, bool $scaled = true): string
249 | {
250 | if(is_null($address))
251 | $address = $this->_tron->address['base58'];
252 |
253 | $addr = str_pad($this->_tron->address2HexString($address), 64, "0", STR_PAD_LEFT);
254 | $result = $this->trigger('balanceOf', $address, [$addr]);
255 | $balance = $result[0]->toString();
256 |
257 | if (!is_string($balance) || !preg_match('/^[0-9]+$/', $balance)) {
258 | throw new TRC20Exception(
259 | sprintf('Failed to retrieve TRC20 token balance of address "%s"', $addr)
260 | );
261 | }
262 |
263 | return $scaled ? $this->decimalValue($balance, $this->decimals()) : $balance;
264 | }
265 |
266 | /**
267 | * Send TRC20 contract
268 | *
269 | * @param string $to
270 | * @param string $amount
271 | * @param string|null $from
272 | * @return array
273 | * @throws TRC20Exception
274 | * @throws TronException
275 | */
276 | public function transfer(string $to, string $amount, string $from = null): array
277 | {
278 | if($from == null) {
279 | $from = $this->_tron->address['base58'];
280 | }
281 |
282 | $feeLimitInSun = bcmul((string)$this->feeLimit, (string)self::TRX_TO_SUN);
283 |
284 | if (!is_numeric($this->feeLimit) OR $this->feeLimit <= 0) {
285 | throw new TRC20Exception('fee_limit is required.');
286 | } else if($this->feeLimit > 1000) {
287 | throw new TRC20Exception('fee_limit must not be greater than 1000 TRX.');
288 | }
289 |
290 | $tokenAmount = bcmul($amount, bcpow("10", (string)$this->decimals(), 0), 0);
291 |
292 | $transfer = $this->_tron->getTransactionBuilder()
293 | ->triggerSmartContract(
294 | $this->abiData,
295 | $this->_tron->address2HexString($this->contractAddress),
296 | 'transfer',
297 | [$this->_tron->address2HexString($to), $tokenAmount],
298 | $feeLimitInSun,
299 | $this->_tron->address2HexString($from)
300 | );
301 |
302 | $signedTransaction = $this->_tron->signTransaction($transfer);
303 | $response = $this->_tron->sendRawTransaction($signedTransaction);
304 |
305 | return array_merge($response, $signedTransaction);
306 | }
307 |
308 | /**
309 | * TRC20 All transactions
310 | *
311 | * @param string $address
312 | * @param int $limit
313 | * @return array
314 | *
315 | * @throws TronException
316 | */
317 | public function getTransactions(string $address, int $limit = 100): array
318 | {
319 | return $this->_tron->getManager()
320 | ->request("v1/accounts/{$address}/transactions/trc20?limit={$limit}&contract_address={$this->contractAddress}", [], 'get');
321 | }
322 |
323 | /**
324 | * Get transaction info by contract address
325 | *
326 | * @throws TronException
327 | */
328 | public function getTransactionInfoByContract(array $options = []): array
329 | {
330 | return $this->_tron->getManager()
331 | ->request("v1/contracts/{$this->contractAddress}/transactions?".http_build_query($options), [],'get');
332 | }
333 |
334 | /**
335 | * Get TRC20 token holder balances
336 | *
337 | * @throws TronException
338 | */
339 | public function getTRC20TokenHolderBalance(array $options = []): array
340 | {
341 | return $this->_tron->getManager()
342 | ->request("v1/contracts/{$this->contractAddress}/tokens?".http_build_query($options), [],'get');
343 | }
344 |
345 | /**
346 | * Find transaction
347 | *
348 | * @param string $transaction_id
349 | * @return array
350 | * @throws TronException
351 | */
352 | public function getTransaction(string $transaction_id): array
353 | {
354 | return $this->_tron->getManager()
355 | ->request('/wallet/gettransactioninfobyid', ['value' => $transaction_id], 'post');
356 | }
357 |
358 | /**
359 | * Config trigger
360 | *
361 | * @param $function
362 | * @param null $address
363 | * @param array $params
364 | * @return mixed
365 | * @throws TronException
366 | */
367 | private function trigger($function, $address = null, array $params = [])
368 | {
369 | $owner_address = is_null($address) ? '410000000000000000000000000000000000000000' : $this->_tron->address2HexString($address);
370 |
371 | return $this->_tron->getTransactionBuilder()
372 | ->triggerConstantContract($this->abiData, $this->_tron->address2HexString($this->contractAddress), $function, $params, $owner_address);
373 | }
374 |
375 | /**
376 | * @param string $int
377 | * @param int $scale
378 | * @return string
379 | */
380 | protected function decimalValue(string $int, int $scale = 18): string
381 | {
382 | return (new BcNumber($int))->divide(pow(10, $scale), $scale)->value();
383 | }
384 |
385 | /**
386 | * @param string $str
387 | * @return string
388 | */
389 | public function cleanStr(string $str): string
390 | {
391 | return preg_replace('/[^\w.-]/', '', trim($str));
392 | }
393 |
394 | /**
395 | * Set fee limit
396 | *
397 | * @param int $fee_limit
398 | * @return TRC20Contract
399 | */
400 | public function setFeeLimit(int $fee_limit) : TRC20Contract
401 | {
402 | $this->feeLimit = $fee_limit;
403 | return $this;
404 | }
405 | }
406 |
--------------------------------------------------------------------------------
/src/TransactionBuilder.php:
--------------------------------------------------------------------------------
1 | tron = $tron;
27 | }
28 |
29 | /**
30 | * Creates a transaction of transfer.
31 | * If the recipient address does not exist, a corresponding account will be created on the blockchain.
32 | *
33 | * @param string $to
34 | * @param float $amount
35 | * @param string|null $from
36 | * @param string|null $message
37 | * @return array
38 | * @throws TronException
39 | */
40 | public function sendTrx(string $to, float $amount, string $from = null, string $message = null)
41 | {
42 | if ($amount < 0) {
43 | throw new TronException('Invalid amount provided');
44 | }
45 |
46 | if(is_null($from)) {
47 | $from = $this->tron->address['hex'];
48 | }
49 |
50 | $to = $this->tron->address2HexString($to);
51 | $from = $this->tron->address2HexString($from);
52 |
53 | if ($from === $to) {
54 | throw new TronException('Cannot transfer TRX to the same account');
55 | }
56 |
57 | $options = [
58 | 'to_address' => $to,
59 | 'owner_address' => $from,
60 | 'amount' => $this->tron->toTron($amount),
61 | ];
62 |
63 | if(!is_null($message)) {
64 | $params['extra_data'] = $this->tron->stringUtf8toHex($message);
65 | }
66 |
67 | return $this->tron->getManager()->request('wallet/createtransaction', $options);
68 | }
69 |
70 | /**
71 | * Transfer Token
72 | *
73 | * @param string $to
74 | * @param int $amount
75 | * @param string $tokenID
76 | * @param string|null $from
77 | * @return array
78 | * @throws TronException
79 | */
80 | public function sendToken(string $to, int $amount, string $tokenID, string $from)
81 | {
82 | if (!is_integer($amount) or $amount <= 0) {
83 | throw new TronException('Invalid amount provided');
84 | }
85 |
86 | if (!is_string($tokenID)) {
87 | throw new TronException('Invalid token ID provided');
88 | }
89 |
90 | if ($to === $from) {
91 | throw new TronException('Cannot transfer tokens to the same account');
92 | }
93 |
94 | $transfer = $this->tron->getManager()->request('wallet/transferasset', [
95 | 'owner_address' => $this->tron->address2HexString($from),
96 | 'to_address' => $this->tron->address2HexString($to),
97 | 'asset_name' => $this->tron->stringUtf8toHex($tokenID),
98 | 'amount' => intval($amount)
99 | ]);
100 |
101 | if (array_key_exists('Error', $transfer)) {
102 | throw new TronException($transfer['Error']);
103 | }
104 | return $transfer;
105 | }
106 |
107 | /**
108 | * Purchase a Token
109 | *
110 | * @param $issuerAddress
111 | * @param $tokenID
112 | * @param $amount
113 | * @param $buyer
114 | * @return array
115 | * @throws TronException
116 | */
117 | public function purchaseToken($issuerAddress, $tokenID, $amount, $buyer)
118 | {
119 | if (!is_string($tokenID)) {
120 | throw new TronException('Invalid token ID provided');
121 | }
122 |
123 | if (!is_integer($amount) and $amount <= 0) {
124 | throw new TronException('Invalid amount provided');
125 | }
126 |
127 | $purchase = $this->tron->getManager()->request('wallet/participateassetissue', [
128 | 'to_address' => $this->tron->address2HexString($issuerAddress),
129 | 'owner_address' => $this->tron->address2HexString($buyer),
130 | 'asset_name' => $this->tron->stringUtf8toHex($tokenID),
131 | 'amount' => $this->tron->toTron($amount)
132 | ]);
133 |
134 | if (array_key_exists('Error', $purchase)) {
135 | throw new TronException($purchase['Error']);
136 | }
137 | return $purchase;
138 | }
139 |
140 | /**
141 | * createToken
142 | *
143 | * @param array $options
144 | * @param null $issuerAddress
145 | * @return array
146 | * @throws TronException
147 | */
148 | public function createToken($options = [], $issuerAddress = null)
149 | {
150 | $startDate = new \DateTime();
151 | $startTimeStamp = $startDate->getTimestamp() * 1000;
152 |
153 | // Create default parameters in case of their absence
154 | if(!$options['totalSupply']) $options['totalSupply'] = 0;
155 | if(!$options['trxRatio']) $options['trxRatio'] = 1;
156 | if(!$options['tokenRatio']) $options['tokenRatio'] = 1;
157 | if(!$options['freeBandwidth']) $options['freeBandwidth'] = 0;
158 | if(!$options['freeBandwidthLimit']) $options['freeBandwidthLimit'] = 0;
159 | if(!$options['frozenAmount']) $options['frozenAmount'] = 0;
160 | if(!$options['frozenDuration']) $options['frozenDuration'] = 0;
161 |
162 | if (is_null($issuerAddress)) {
163 | $issuerAddress = $this->tron->address['hex'];
164 | }
165 |
166 | if(!$options['name'] or !is_string($options['name'])) {
167 | throw new TronException('Invalid token name provided');
168 | }
169 |
170 | if(!$options['abbreviation'] or !is_string($options['abbreviation'])) {
171 | throw new TronException('Invalid token abbreviation provided');
172 | }
173 |
174 | if(!is_integer($options['totalSupply']) or $options['totalSupply'] <= 0) {
175 | throw new TronException('Invalid supply amount provided');
176 | }
177 |
178 | if(!is_integer($options['trxRatio']) or $options['trxRatio'] <= 0) {
179 | throw new TronException('TRX ratio must be a positive integer');
180 | }
181 |
182 | if(!is_integer($options['saleStart']) or $options['saleStart'] <= $startTimeStamp) {
183 | throw new TronException('Invalid sale start timestamp provided');
184 | }
185 |
186 | if(!is_integer($options['saleEnd']) or $options['saleEnd'] <= $options['saleStart']) {
187 | throw new TronException('Invalid sale end timestamp provided');
188 | }
189 |
190 | if(!$options['description'] or !is_string($options['description'])) {
191 | throw new TronException('Invalid token description provided');
192 | }
193 |
194 | if(!is_string($options['url']) || !filter_var($options['url'], FILTER_VALIDATE_URL)) {
195 | throw new TronException('Invalid token url provided');
196 | }
197 |
198 | if(!is_integer($options['freeBandwidth']) || $options['freeBandwidth'] < 0) {
199 | throw new TronException('Invalid free bandwidth amount provided');
200 | }
201 |
202 | if(!is_integer($options['freeBandwidthLimit']) || $options['freeBandwidthLimit '] < 0 ||
203 | ($options['freeBandwidth'] && !$options['freeBandwidthLimit'])
204 | ) {
205 | throw new TronException('Invalid free bandwidth limit provided');
206 | }
207 |
208 | if(!is_integer($options['frozenAmount']) || $options['frozenAmount '] < 0 ||
209 | (!$options['frozenDuration'] && $options['frozenAmount'])
210 | ) {
211 | throw new TronException('Invalid frozen supply provided');
212 | }
213 |
214 | if(!is_integer($options['frozenDuration']) || $options['frozenDuration '] < 0 ||
215 | ($options['frozenDuration'] && !$options['frozenAmount'])
216 | ) {
217 | throw new TronException('Invalid frozen duration provided');
218 | }
219 |
220 | $data = [
221 | 'owner_address' => $this->tron->address2HexString($issuerAddress),
222 | 'name' => $this->tron->stringUtf8toHex($options['name']),
223 | 'abbr' => $this->tron->stringUtf8toHex($options['abbreviation']),
224 | 'description' => $this->tron->stringUtf8toHex($options['description']),
225 | 'url' => $this->tron->stringUtf8toHex($options['url']),
226 | 'total_supply' => intval($options['totalSupply']),
227 | 'trx_num' => intval($options['trxRatio']),
228 | 'num' => intval($options['tokenRatio']),
229 | 'start_time' => intval($options['saleStart']),
230 | 'end_time' => intval($options['saleEnd']),
231 | 'free_asset_net_limit' => intval($options['freeBandwidth']),
232 | 'public_free_asset_net_limit' => intval($options['freeBandwidthLimit']),
233 | 'frozen_supply' => [
234 | 'frozen_amount' => intval($options['frozenAmount']),
235 | 'frozen_days' => intval($options['frozenDuration']),
236 | ]
237 | ];
238 |
239 | if($options['precision'] && !is_nan(intval($options['precision']))) {
240 | $data['precision'] = intval($options['precision']);
241 | }
242 |
243 | if($options['voteScore'] && !is_nan(intval($options['voteScore']))) {
244 | $data['vote_score'] = intval($options['voteScore']);
245 | }
246 |
247 | return $this->tron->getManager()->request('wallet/createassetissue', $data);
248 | }
249 |
250 | /**
251 | * Freezes an amount of TRX.
252 | * Will give bandwidth OR Energy and TRON Power(voting rights) to the owner of the frozen tokens.
253 | *
254 | * @param float $amount
255 | * @param int $duration
256 | * @param string $resource
257 | * @param string|null $address
258 | * @return array
259 | * @throws TronException
260 | */
261 | public function freezeBalance(float $amount = 0, int $duration = 3, string $resource = 'BANDWIDTH', string $address = null)
262 | {
263 | if(empty($address))
264 | throw new TronException('Address not specified');
265 |
266 | if (!in_array($resource, ['BANDWIDTH', 'ENERGY'])) {
267 | throw new TronException('Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"');
268 | }
269 |
270 | if (!is_float($amount)) {
271 | throw new TronException('Invalid amount provided');
272 | }
273 |
274 | if(!is_integer($duration) and $duration < 3) {
275 | throw new TronException('Invalid duration provided, minimum of 3 days');
276 | }
277 |
278 | return $this->tron->getManager()->request('wallet/freezebalance', [
279 | 'owner_address' => $this->tron->address2HexString($address),
280 | 'frozen_balance' => $this->tron->toTron($amount),
281 | 'frozen_duration' => $duration,
282 | 'resource' => $resource
283 | ]);
284 | }
285 |
286 | /**
287 | * Unfreeze TRX that has passed the minimum freeze duration.
288 | * Unfreezing will remove bandwidth and TRON Power.
289 | *
290 | * @param string $resource
291 | * @param string $owner_address
292 | * @return array
293 | * @throws TronException
294 | */
295 | public function unfreezeBalance(string $resource = 'BANDWIDTH', string $owner_address = null)
296 | {
297 | if(is_null($owner_address)) {
298 | throw new TronException('Owner Address not specified');
299 | }
300 |
301 | if (!in_array($resource, ['BANDWIDTH', 'ENERGY'])) {
302 | throw new TronException('Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"');
303 | }
304 |
305 | return $this->tron->getManager()->request('wallet/unfreezebalance', [
306 | 'owner_address' => $this->tron->address2HexString($owner_address),
307 | 'resource' => $resource
308 | ]);
309 | }
310 |
311 | /**
312 | * Withdraw Super Representative rewards, useable every 24 hours.
313 | *
314 | * @param string $owner_address
315 | * @return array
316 | * @throws TronException
317 | */
318 | public function withdrawBlockRewards($owner_address = null)
319 | {
320 | $withdraw = $this->tron->getManager()->request('wallet/withdrawbalance', [
321 | 'owner_address' => $this->tron->address2HexString($owner_address)
322 | ]);
323 |
324 | if (array_key_exists('Error', $withdraw)) {
325 | throw new TronException($withdraw['Error']);
326 | }
327 | return $withdraw;
328 | }
329 |
330 | /**
331 | * Update a Token's information
332 | *
333 | * @param string $description
334 | * @param string $url
335 | * @param int $freeBandwidth
336 | * @param int $freeBandwidthLimit
337 | * @param $address
338 | * @return array
339 | * @throws TronException
340 | */
341 | public function updateToken(string $description, string $url, int $freeBandwidth = 0, int $freeBandwidthLimit = 0, $address = null)
342 | {
343 | if(is_null($address)) {
344 | throw new TronException('Owner Address not specified');
345 | }
346 |
347 | if (!is_integer($freeBandwidth) || $freeBandwidth < 0) {
348 | throw new TronException('Invalid free bandwidth amount provided');
349 | }
350 |
351 | if (!is_integer($freeBandwidthLimit) || $freeBandwidthLimit < 0 && ($freeBandwidth && !$freeBandwidthLimit)) {
352 | throw new TronException('Invalid free bandwidth limit provided');
353 | }
354 |
355 | return $this->tron->getManager()->request('wallet/updateasset', [
356 | 'owner_address' => $this->tron->address2HexString($address),
357 | 'description' => $this->tron->stringUtf8toHex($description),
358 | 'url' => $this->tron->stringUtf8toHex($url),
359 | 'new_limit' => intval($freeBandwidth),
360 | 'new_public_limit' => intval($freeBandwidthLimit)
361 | ]);
362 | }
363 |
364 | /**
365 | * updateEnergyLimit
366 | *
367 | * @param string $contractAddress
368 | * @param int $originEnergyLimit
369 | * @param string $ownerAddress
370 | * @return array
371 | * @throws TronException
372 | */
373 | public function updateEnergyLimit(string $contractAddress, int $originEnergyLimit, string $ownerAddress)
374 | {
375 | $contractAddress = $this->tron->address2HexString($contractAddress);
376 | $ownerAddress = $this->tron->address2HexString($ownerAddress);
377 |
378 | if($originEnergyLimit < 0 || $originEnergyLimit > 10000000) {
379 | throw new TronException('Invalid originEnergyLimit provided');
380 | }
381 |
382 | return $this->tron->getManager()->request('wallet/updateenergylimit', [
383 | 'owner_address' => $this->tron->address2HexString($ownerAddress),
384 | 'contract_address' => $this->tron->address2HexString($contractAddress),
385 | 'origin_energy_limit' => $originEnergyLimit
386 | ]);
387 | }
388 |
389 | /**
390 | * updateSetting
391 | *
392 | * @param string $contractAddress
393 | * @param int $userFeePercentage
394 | * @param string $ownerAddress
395 | * @return array
396 | * @throws TronException
397 | */
398 | public function updateSetting(string $contractAddress, int $userFeePercentage, string $ownerAddress)
399 | {
400 | $contractAddress = $this->tron->address2HexString($contractAddress);
401 | $ownerAddress = $this->tron->address2HexString($ownerAddress);
402 |
403 | if($userFeePercentage < 0 || $userFeePercentage > 1000) {
404 | throw new TronException('Invalid userFeePercentage provided');
405 | }
406 |
407 | return $this->tron->getManager()->request('wallet/updatesetting', [
408 | 'owner_address' => $this->tron->address2HexString($ownerAddress),
409 | 'contract_address' => $this->tron->address2HexString($contractAddress),
410 | 'consume_user_resource_percent' => $userFeePercentage
411 | ]);
412 | }
413 | /**
414 | * Contract Balance
415 | * @param string $address $tron->toHex('Txxxxx');
416 | *
417 | * @return array
418 | */
419 | public function contractbalance($adres)
420 | {
421 | $trc20=array();
422 | $abi=json_decode('{"entrys": [{"constant": true,"name": "name","outputs": [{"type": "string"}],"type": "Function","stateMutability": "View"},{"name": "approve","inputs": [{"name": "_spender","type": "address"},{"name": "_value","type": "uint256"}],"outputs": [{"type": "bool"}],"type": "Function","stateMutability": "Nonpayable"},{"name": "setCanApproveCall","inputs": [{"name": "_val","type": "bool"}],"type": "Function","stateMutability": "Nonpayable"},{"constant": true,"name": "totalSupply","outputs": [{"type": "uint256"}],"type": "Function","stateMutability": "View"},{"name": "transferFrom","inputs": [{"name": "_from","type": "address"},{"name": "_to","type": "address"},{"name": "_value","type": "uint256"}],"outputs": [{"type": "bool"}],"type": "Function","stateMutability": "Nonpayable"},{"constant": true,"name": "decimals","outputs": [{"type": "uint8"}],"type": "Function","stateMutability": "View"},{"name": "setCanBurn","inputs": [{"name": "_val","type": "bool"}],"type": "Function","stateMutability": "Nonpayable"},{"name": "burn","inputs": [{"name": "_value","type": "uint256"}],"outputs": [{"name": "success","type": "bool"}],"type": "Function","stateMutability": "Nonpayable"},{"constant": true,"name": "balanceOf","inputs": [{"name": "_owner","type": "address"}],"outputs": [{"type": "uint256"}],"type": "Function","stateMutability": "View"},{"constant": true,"name": "symbol","outputs": [{"type": "string"}],"type": "Function","stateMutability": "View"},{"name": "transfer","inputs": [{"name": "_to","type": "address"},{"name": "_value","type": "uint256"}],"outputs": [{"type": "bool"}],"type": "Function","stateMutability": "Nonpayable"},{"constant": true,"name": "canBurn","outputs": [{"type": "bool"}],"type": "Function","stateMutability": "View"},{"name": "approveAndCall","inputs": [{"name": "_spender","type": "address"},{"name": "_value","type": "uint256"},{"name": "_extraData","type": "bytes"}],"outputs": [{"name": "success","type": "bool"}],"type": "Function","stateMutability": "Nonpayable"},{"constant": true,"name": "allowance","inputs": [{"name": "_owner","type": "address"},{"name": "_spender","type": "address"}],"outputs": [{"type": "uint256"}],"type": "Function","stateMutability": "View"},{"name": "transferOwnership","inputs": [{"name": "_newOwner","type": "address"}],"type": "Function","stateMutability": "Nonpayable"},{"constant": true,"name": "canApproveCall","outputs": [{"type": "bool"}],"type": "Function","stateMutability": "View"},{"type": "Constructor","stateMutability": "Nonpayable"},{"name": "Transfer","inputs": [{"indexed": true,"name": "_from","type": "address"},{"indexed": true,"name": "_to","type": "address"},{"name": "_value","type": "uint256"}],"type": "Event"},{"name": "Approval","inputs": [{"indexed": true,"name": "_owner","type": "address"},{"indexed": true,"name": "_spender","type": "address"},{"name": "_value","type": "uint256"}],"type": "Event"},{"name": "Burn","inputs": [{"indexed": true,"name": "_from","type": "address"},{"name": "_value","type": "uint256"}],"type": "Event"}]}',true);
423 | $feeLimit=1000000;
424 | $func="balanceOf";
425 | $jsonData = json_decode(file_get_contents("https://apilist.tronscan.org/api/token_trc20?sort=issue_time&limit=100&start=0"),true);
426 | foreach($jsonData["trc20_tokens"] as $key =>$item)
427 | {
428 | $owner=$item["contract_address"];
429 | $params=array("0"=>$this->tron->toHex($adres));
430 | $result = $this->tron->getTransactionBuilder()->triggerSmartContract(
431 | $abi['entrys'],
432 | $this->tron->toHex($owner),
433 | $func,
434 | $params,
435 | $feeLimit,
436 | $this->tron->toHex($adres),
437 | 0,
438 | 0);
439 | $balance_hex=$result["0"];
440 | $balance=0+(float)number_format($balance_hex->value/pow(10,$item["decimals"]),$item["decimals"],".","");
441 | if($balance>0)
442 | {
443 | $trc20[]=array(
444 | "name"=>$item["name"],
445 | "symbol"=>$item["symbol"],
446 | "balance"=>$balance,
447 | "value"=>$balance_hex->value,
448 | "decimals"=>$item["decimals"],
449 | );
450 | }
451 | }
452 | return $trc20;
453 | }
454 |
455 | /**
456 | * Triggers smart contract
457 | *
458 | * @param mixed $abi
459 | * @param string $contract $tron->toHex('Txxxxx');
460 | * @param string $function
461 | * @param array $params array("0"=>$value);
462 | * @param integer $feeLimit
463 | * @param string $address $tron->toHex('Txxxxx');
464 | * @param int $callValue
465 | * @param int $bandwidthLimit
466 | *
467 | * @return mixed
468 | * @throws TronException
469 | */
470 | public function triggerSmartContract($abi,
471 | $contract,
472 | $function,
473 | $params,
474 | $feeLimit,
475 | $address,
476 | $callValue = 0,
477 | $bandwidthLimit = 0)
478 | {
479 | $func_abi = [];
480 | foreach($abi as $key =>$item) {
481 | if(isset($item['name']) && $item['name'] === $function) {
482 | $func_abi = $item;
483 | break;
484 | }
485 | }
486 |
487 | if(count($func_abi) === 0)
488 | throw new TronException("Function $function not defined in ABI");
489 |
490 | if(!is_array($params))
491 | throw new TronException("Function params must be an array");
492 |
493 | if(count($func_abi['inputs']) !== count($params))
494 | throw new TronException("Count of params and abi inputs must be identical");
495 |
496 | if($feeLimit > 1000000000)
497 | throw new TronException('fee_limit must not be greater than 1000000000');
498 |
499 |
500 | $inputs = array_map(function($item){ return $item['type']; },$func_abi['inputs']);
501 | $signature = $func_abi['name'].'(';
502 | if(count($inputs) > 0)
503 | $signature .= implode(',',$inputs);
504 | $signature .= ')';
505 |
506 | $eth_abi = new Ethabi([
507 | 'address' => new Address,
508 | 'bool' => new Boolean,
509 | 'bytes' => new Bytes,
510 | 'dynamicBytes' => new DynamicBytes,
511 | 'int' => new Integer,
512 | 'string' => new Str,
513 | 'uint' => new Uinteger,
514 | ]);
515 | $parameters = substr($eth_abi->encodeParameters($func_abi, $params),2);
516 |
517 | $result = $this->tron->getManager()->request('wallet/triggersmartcontract', [
518 | 'contract_address' => $contract,
519 | 'function_selector' => $signature,
520 | 'parameter' => $parameters,
521 | 'owner_address' => $address,
522 | 'fee_limit' => $feeLimit,
523 | 'call_value' => $callValue,
524 | 'consume_user_resource_percent' => $bandwidthLimit,
525 | ]);
526 |
527 | if(!isset($result['result'])){
528 | throw new TronException('No result field in response. Raw response:'.print_r($result,true));
529 | }
530 | if(isset($result['result']['result'])) {
531 | if(count($func_abi['outputs']) >= 0 && isset($result['constant_result'])) {
532 | return $eth_abi->decodeParameters($func_abi, $result['constant_result'][0]);
533 | }
534 | return $result['transaction'];
535 | }
536 | $message = isset($result['result']['message']) ?
537 | $this->tron->hexString2Utf8($result['result']['message']) : '';
538 |
539 | throw new TronException('Failed to execute. Error:'.$message);
540 | }
541 |
542 | /**
543 | * Triggers constant contract
544 | *
545 | * @param mixed $abi
546 | * @param string $contract $tron->toHex('Txxxxx');
547 | * @param string $function
548 | * @param array $params array("0"=>$value);
549 | * @param string $address $tron->toHex('Txxxxx');
550 | *
551 | * @return mixed
552 | * @throws TronException
553 | */
554 | public function triggerConstantContract($abi,
555 | $contract,
556 | $function,
557 | $params = [],
558 | $address = '410000000000000000000000000000000000000000')
559 | {
560 | $func_abi = [];
561 | foreach($abi as $key =>$item) {
562 | if(isset($item['name']) && $item['name'] === $function) {
563 | $func_abi = $item + ['inputs' => []];
564 | break;
565 | }
566 | }
567 |
568 | if(count($func_abi) === 0)
569 | throw new TronException("Function $function not defined in ABI");
570 |
571 | if(!is_array($params))
572 | throw new TronException("Function params must be an array");
573 |
574 | if(count($func_abi['inputs']) !== count($params))
575 | throw new TronException("Count of params and abi inputs must be identical");
576 |
577 |
578 | $inputs = array_map(function($item){ return $item['type']; },$func_abi['inputs']);
579 | $signature = $func_abi['name'].'(';
580 | if(count($inputs) > 0)
581 | $signature .= implode(',',$inputs);
582 | $signature .= ')';
583 |
584 | $eth_abi = new Ethabi([
585 | 'address' => new Address,
586 | 'bool' => new Boolean,
587 | 'bytes' => new Bytes,
588 | 'dynamicBytes' => new DynamicBytes,
589 | 'int' => new Integer,
590 | 'string' => new Str,
591 | 'uint' => new Uinteger,
592 | ]);
593 | $parameters = substr($eth_abi->encodeParameters($func_abi, $params),2);
594 |
595 | $result = $this->tron->getManager()->request('wallet/triggerconstantcontract', [
596 | 'contract_address' => $contract,
597 | 'function_selector' => $signature,
598 | 'parameter' => $parameters,
599 | 'owner_address' => $address,
600 | ]);
601 |
602 | if(!isset($result['result'])){
603 | throw new TronException('No result field in response. Raw response:'.print_r($result,true));
604 | }
605 | if(isset($result['result']['result'])) {
606 | if(count($func_abi['outputs']) >= 0 && isset($result['constant_result'])) {
607 | return $eth_abi->decodeParameters($func_abi, $result['constant_result'][0]);
608 | }
609 | return $result['transaction'];
610 | }
611 | $message = isset($result['result']['message']) ?
612 | $this->tron->hexString2Utf8($result['result']['message']) : '';
613 |
614 | throw new TronException('Failed to execute. Error:'.$message);
615 | }
616 | }
617 |
--------------------------------------------------------------------------------
/src/Tron.php:
--------------------------------------------------------------------------------
1 |
7 | * @license https://github.com/iexbase/tron-api/blob/master/LICENSE (MIT License)
8 | * @version 1.3.4
9 | * @link https://github.com/iexbase/tron-api
10 | *
11 | * For the full copyright and license information, please view the LICENSE
12 | * file that was distributed with this source code.
13 | */
14 |
15 | declare(strict_types=1);
16 |
17 | namespace IEXBase\TronAPI;
18 |
19 | use Elliptic\EC;
20 | use IEXBase\TronAPI\Exception\TRC20Exception;
21 | use IEXBase\TronAPI\Support\Base58;
22 | use IEXBase\TronAPI\Support\Base58Check;
23 | use IEXBase\TronAPI\Support\Crypto;
24 | use IEXBase\TronAPI\Support\Hash;
25 | use IEXBase\TronAPI\Support\Keccak;
26 | use IEXBase\TronAPI\Support\Utils;
27 | use IEXBase\TronAPI\Provider\HttpProviderInterface;
28 | use IEXBase\TronAPI\Exception\TronException;
29 |
30 | /**
31 | * A PHP API for interacting with the Tron (TRX)
32 | *
33 | * @package TronAPI
34 | * @author Shamsudin Serderov
35 | * @since 1.0.0
36 | */
37 | class Tron implements TronInterface
38 | {
39 | use TronAwareTrait,
40 | Concerns\ManagesUniversal,
41 | Concerns\ManagesTronscan;
42 |
43 | const ADDRESS_SIZE = 34;
44 | const ADDRESS_PREFIX = "41";
45 | const ADDRESS_PREFIX_BYTE = 0x41;
46 |
47 | /**
48 | * Default Address:
49 | * Example:
50 | * - base58: T****
51 | * - hex: 41****
52 | *
53 | * @var array
54 | */
55 | public $address = [
56 | 'base58' => null,
57 | 'hex' => null
58 | ];
59 |
60 | /**
61 | * Private key
62 | *
63 | * @var string
64 | */
65 | protected $privateKey;
66 |
67 | /**
68 | * Default block
69 | *
70 | * @var string|integer|bool
71 | */
72 | protected $defaultBlock = 'latest';
73 |
74 | /**
75 | * Transaction Builder
76 | *
77 | * @var TransactionBuilder
78 | */
79 | protected $transactionBuilder;
80 |
81 | /**
82 | * Transaction Builder
83 | *
84 | * @var TransactionBuilder
85 | */
86 | protected $trc20Contract;
87 |
88 | /**
89 | * Provider manager
90 | *
91 | * @var TronManager
92 | */
93 | protected $manager;
94 |
95 | /**
96 | * Object Result
97 | *
98 | * @var bool
99 | */
100 | protected $isObject = false;
101 |
102 | /**
103 | * Create a new Tron object
104 | *
105 | * @param HttpProviderInterface $fullNode
106 | * @param HttpProviderInterface $solidityNode
107 | * @param HttpProviderInterface|null $eventServer
108 | * @param HttpProviderInterface|null $signServer
109 | * @param HttpProviderInterface|null $explorer
110 | * @param string $privateKey
111 |
112 | * @throws TronException
113 | */
114 | public function __construct(?HttpProviderInterface $fullNode = null,
115 | ?HttpProviderInterface $solidityNode = null,
116 | ?HttpProviderInterface $eventServer = null,
117 | ?HttpProviderInterface $signServer = null,
118 | ?HttpProviderInterface $explorer = null,
119 | ?string $privateKey = null)
120 | {
121 | if(!is_null($privateKey)) {
122 | $this->setPrivateKey($privateKey);
123 | }
124 |
125 | $this->setManager(new TronManager($this, [
126 | 'fullNode' => $fullNode,
127 | 'solidityNode' => $solidityNode,
128 | 'eventServer' => $eventServer,
129 | 'signServer' => $signServer,
130 | ]));
131 |
132 | $this->transactionBuilder = new TransactionBuilder($this);
133 | }
134 |
135 | /**
136 | * Create a new tron instance if the value isn't one already.
137 | *
138 | * @param HttpProviderInterface|null $fullNode
139 | * @param HttpProviderInterface|null $solidityNode
140 | * @param HttpProviderInterface|null $eventServer
141 | * @param HttpProviderInterface|null $signServer
142 | * @param string|null $privateKey
143 | * @return static
144 | * @throws TronException
145 | */
146 | public static function make(?HttpProviderInterface $fullNode = null,
147 | ?HttpProviderInterface $solidityNode = null,
148 | ?HttpProviderInterface $eventServer = null,
149 | ?HttpProviderInterface $signServer = null,
150 | string $privateKey = null) {
151 | return new static($fullNode, $solidityNode, $eventServer, $signServer, $privateKey);
152 | }
153 |
154 | /**
155 | * Фасад для Laravel
156 | *
157 | * @return Tron
158 | */
159 | public function getFacade(): Tron {
160 | return $this;
161 | }
162 |
163 | /**
164 | * Enter the link to the manager nodes
165 | *
166 | * @param $providers
167 | */
168 | public function setManager($providers) {
169 | $this->manager = $providers;
170 | }
171 |
172 | /**
173 | * Get provider manager
174 | *
175 | * @return TronManager
176 | */
177 | public function getManager(): TronManager {
178 | return $this->manager;
179 | }
180 |
181 |
182 | /**
183 | * Contract module
184 | *
185 | * @param string $contractAddress
186 | * @param string|null $abi
187 | * @return TRC20Contract
188 | */
189 | public function contract(string $contractAddress, string $abi = null)
190 | {
191 | return new TRC20Contract($this, $contractAddress, $abi);
192 | }
193 |
194 | /**
195 | * Set is object
196 | *
197 | * @param bool $value
198 | * @return Tron
199 | */
200 | public function setIsObject(bool $value)
201 | {
202 | $this->isObject = boolval($value);
203 | return $this;
204 | }
205 |
206 | /**
207 | * Get Transaction Builder
208 | *
209 | * @return TransactionBuilder
210 | */
211 | public function getTransactionBuilder(): TransactionBuilder
212 | {
213 | return $this->transactionBuilder;
214 | }
215 |
216 | /**
217 | * Check connected provider
218 | *
219 | * @param $provider
220 | * @return bool
221 | */
222 | public function isValidProvider($provider): bool
223 | {
224 | return ($provider instanceof HttpProviderInterface);
225 | }
226 |
227 | /**
228 | * Enter the default block
229 | *
230 | * @param bool $blockID
231 | * @return void
232 | * @throws TronException
233 | */
234 | public function setDefaultBlock($blockID = false): void
235 | {
236 | if($blockID === false || $blockID == 'latest' || $blockID == 'earliest' || $blockID === 0) {
237 | $this->defaultBlock = $blockID;
238 | return;
239 | }
240 |
241 | if(!is_integer($blockID)) {
242 | throw new TronException('Invalid block ID provided');
243 | }
244 |
245 | $this->defaultBlock = abs($blockID);
246 | }
247 |
248 | /**
249 | * Get default block
250 | *
251 | * @return string|integer|bool
252 | */
253 | public function getDefaultBlock()
254 | {
255 | return $this->defaultBlock;
256 | }
257 |
258 | /**
259 | * Enter your private account key
260 | *
261 | * @param string $privateKey
262 | */
263 | public function setPrivateKey(string $privateKey): void
264 | {
265 | $this->privateKey = $privateKey;
266 | }
267 |
268 | /**
269 | * Enter your account address
270 | *
271 | * @param string $address
272 | */
273 | public function setAddress(string $address): void
274 | {
275 | $_toHex = $this->address2HexString($address);
276 | $_fromHex = $this->hexString2Address($address);
277 |
278 | $this->address = [
279 | 'hex' => $_toHex,
280 | 'base58' => $_fromHex
281 | ];
282 | }
283 |
284 | /**
285 | * Get account address
286 | *
287 | * @return array
288 | */
289 | public function getAddress(): array
290 | {
291 | return $this->address;
292 | }
293 |
294 | /**
295 | * Get customized provider data
296 | *
297 | * @return array
298 | */
299 | public function providers(): array
300 | {
301 | return $this->manager->getProviders();
302 | }
303 |
304 | /**
305 | * Check Connection Providers
306 | *
307 | * @return array
308 | */
309 | public function isConnected(): array
310 | {
311 | return $this->manager->isConnected();
312 | }
313 |
314 | /**
315 | * Last block number
316 | *
317 | * @return array
318 | * @throws TronException
319 | */
320 | public function getCurrentBlock(): array
321 | {
322 | return $this->manager->request('wallet/getnowblock');
323 | }
324 |
325 | /**
326 | * Will return all events matching the filters.
327 | *
328 | * @param $contractAddress
329 | * @param int $sinceTimestamp
330 | * @param string|null $eventName
331 | * @param int $blockNumber
332 | * @return array
333 | * @throws TronException
334 | */
335 | public function getEventResult($contractAddress, int $sinceTimestamp = 0, string $eventName = null, int $blockNumber = 0)
336 | {
337 | if (!$this->isValidProvider($this->manager->eventServer())) {
338 | throw new TronException('No event server configured');
339 | }
340 |
341 | $routeParams = [];
342 | if($eventName && !$contractAddress) {
343 | throw new TronException('Usage of event name filtering requires a contract address');
344 | }
345 |
346 | if($blockNumber && !$eventName)
347 | throw new TronException('Usage of block number filtering requires an event name');
348 |
349 | if($contractAddress) {
350 | array_push($routeParams, $contractAddress);
351 | }
352 | if($eventName) {
353 | array_push($routeParams, $eventName);
354 | }
355 | if($blockNumber) {
356 | array_push($routeParams, $blockNumber);
357 | }
358 |
359 | $routeParams = implode('/', $routeParams);
360 | return $this->manager->request("event/contract/{$routeParams}?since={$sinceTimestamp}");
361 | }
362 |
363 |
364 | /**
365 | * Will return all events within a transactionID.
366 | *
367 | * @param string $transactionID
368 | * @return array
369 | * @throws TronException
370 | */
371 | public function getEventByTransactionID(string $transactionID)
372 | {
373 | if (!$this->isValidProvider($this->manager->eventServer())) {
374 | throw new TronException('No event server configured');
375 | }
376 | return $this->manager->request("event/transaction/{$transactionID}");
377 | }
378 |
379 | /**
380 | * Get block details using HashString or blockNumber
381 | *
382 | * @param null $block
383 | * @return array
384 | * @throws TronException
385 | */
386 | public function getBlock($block = null): array
387 | {
388 | $block = (is_null($block) ? $this->defaultBlock : $block);
389 |
390 | if($block === false) {
391 | throw new TronException('No block identifier provided');
392 | }
393 |
394 | if($block == 'earliest') {
395 | $block = 0;
396 | }
397 |
398 | if($block == 'latest') {
399 | return $this->getCurrentBlock();
400 | }
401 |
402 | if(Utils::isHex($block)) {
403 | return $this->getBlockByHash($block);
404 | }
405 | return $this->getBlockByNumber($block);
406 | }
407 |
408 | /**
409 | * Query block by ID
410 | *
411 | * @param $hashBlock
412 | * @return array
413 | * @throws TronException
414 | */
415 | public function getBlockByHash(string $hashBlock): array
416 | {
417 | return $this->manager->request('wallet/getblockbyid', [
418 | 'value' => $hashBlock
419 | ]);
420 | }
421 |
422 | /**
423 | * Query block by height
424 | *
425 | * @param $blockID
426 | * @return array
427 | * @throws TronException
428 | */
429 | public function getBlockByNumber(int $blockID): array
430 | {
431 | if(!is_integer($blockID) || $blockID < 0) {
432 | throw new TronException('Invalid block number provided');
433 | }
434 |
435 | $response = $this->manager->request('wallet/getblockbynum', [
436 | 'num' => intval($blockID)
437 | ]);
438 |
439 | if (empty($response)) {
440 | throw new TronException('Block not found');
441 | }
442 | return $response;
443 | }
444 |
445 | /**
446 | * Total number of transactions in a block
447 | *
448 | * @param $block
449 | * @return int
450 | * @throws TronException
451 | */
452 | public function getBlockTransactionCount($block): int
453 | {
454 | $transaction = $this->getBlock($block)['transactions'];
455 | if(!$transaction) {
456 | return 0;
457 | }
458 |
459 | return count($transaction);
460 | }
461 |
462 | /**
463 | * Get transaction details from Block
464 | *
465 | * @param null $block
466 | * @param int $index
467 | * @return array | string
468 | * @throws TronException
469 | */
470 | public function getTransactionFromBlock($block = null, $index = 0)
471 | {
472 | if(!is_integer($index) || $index < 0) {
473 | throw new TronException('Invalid transaction index provided');
474 | }
475 |
476 | $transactions = $this->getBlock($block)['transactions'];
477 | if(!$transactions || count($transactions) < $index) {
478 | throw new TronException('Transaction not found in block');
479 | }
480 |
481 | return $transactions[$index];
482 | }
483 |
484 | /**
485 | * Query transaction based on id
486 | *
487 | * @param $transactionID
488 | * @return array
489 | * @throws TronException
490 | */
491 | public function getTransaction(string $transactionID): array
492 | {
493 | $response = $this->manager->request('wallet/gettransactionbyid', [
494 | 'value' => $transactionID
495 | ]);
496 |
497 | if(!$response) {
498 | throw new TronException('Transaction not found');
499 | }
500 |
501 | return $response;
502 | }
503 |
504 | /**
505 | * Query transaction fee based on id
506 | *
507 | * @param $transactionID
508 | * @return array
509 | * @throws TronException
510 | */
511 | public function getTransactionInfo(string $transactionID): array
512 | {
513 | return $this->manager->request('walletsolidity/gettransactioninfobyid', [
514 | 'value' => $transactionID
515 | ]);
516 | }
517 |
518 | /**
519 | * Query the list of transactions received by an address
520 | *
521 | * @param string $address
522 | * @param int $limit
523 | * @param int $offset
524 | * @return array
525 | * @throws TronException
526 | */
527 | public function getTransactionsToAddress(string $address, int $limit = 30, int $offset = 0)
528 | {
529 | return $this->getTransactionsRelated($address,'to', $limit, $offset);
530 | }
531 |
532 | /**
533 | * Query the list of transactions sent by an address
534 | *
535 | * @param string $address
536 | * @param int $limit
537 | * @param int $offset
538 | * @return array
539 | * @throws TronException
540 | */
541 | public function getTransactionsFromAddress(string $address, int $limit = 30, int $offset = 0)
542 | {
543 | return $this->getTransactionsRelated($address,'from', $limit, $offset);
544 | }
545 |
546 | /**
547 | * Query information about an account
548 | *
549 | * @param $address
550 | * @return array
551 | * @throws TronException
552 | */
553 | public function getAccount(string $address = null): array
554 | {
555 | $address = (!is_null($address) ? $this->toHex($address) : $this->address['hex']);
556 |
557 | return $this->manager->request('walletsolidity/getaccount', [
558 | 'address' => $address
559 | ]);
560 | }
561 |
562 | /**
563 | * Getting a balance
564 | *
565 | * @param string $address
566 | * @param bool $fromTron
567 | * @return float
568 | * @throws TronException
569 | */
570 | public function getBalance(string $address = null, bool $fromTron = false): float
571 | {
572 | $account = $this->getAccount($address);
573 |
574 | if(!array_key_exists('balance', $account)) {
575 | return 0;
576 | }
577 |
578 | return ($fromTron == true ?
579 | $this->fromTron($account['balance']) :
580 | $account['balance']);
581 | }
582 |
583 |
584 | /**
585 | * Get token balance
586 | *
587 | * @param string $address
588 | * @param int $tokenId
589 | * @param bool $fromTron
590 | * @return array|int
591 | * @throws TronException
592 | */
593 | public function getTokenBalance(int $tokenId, string $address, bool $fromTron = false)
594 | {
595 | $account = $this->getAccount($address);
596 |
597 | if(isset($account['assetV2']) and !empty($account['assetV2']) )
598 | {
599 | $value = array_filter($account['assetV2'], function($item) use ($tokenId) {
600 | return $item['key'] == $tokenId;
601 | });
602 |
603 | if(empty($value)) {
604 | throw new TronException('Token id not found');
605 | }
606 |
607 | $first = array_shift($value);
608 | return ($fromTron == true ? $this->fromTron($first['value']) : $first['value']);
609 | }
610 |
611 | return 0;
612 | }
613 |
614 | /**
615 | * Query bandwidth information.
616 | *
617 | * @param $address
618 | * @return array
619 | * @throws TronException
620 | */
621 | public function getBandwidth(string $address = null)
622 | {
623 | $address = (!is_null($address) ? $this->toHex($address) : $this->address['hex']);
624 | return $this->manager->request('wallet/getaccountnet', [
625 | 'address' => $address
626 | ]);
627 | }
628 |
629 | /**
630 | * Getting data in the "from","to" directions
631 | *
632 | * @param string $address
633 | * @param string $direction
634 | * @param int $limit
635 | * @param int $offset
636 | * @return array
637 | * @throws TronException
638 | */
639 | public function getTransactionsRelated(string $address, string $direction = 'to', int $limit = 30, int $offset = 0)
640 | {
641 | if(!in_array($direction, ['to', 'from'])) {
642 | throw new TronException('Invalid direction provided: Expected "to", "from"');
643 | }
644 |
645 | if(!is_integer($limit) || $limit < 0 || ($offset && $limit < 1)) {
646 | throw new TronException('Invalid limit provided');
647 | }
648 |
649 | if(!is_integer($offset) || $offset < 0) {
650 | throw new TronException('Invalid offset provided');
651 | }
652 |
653 | $response = $this->manager->request(sprintf('walletextension/gettransactions%sthis', $direction), [
654 | 'account' => ['address' => $this->toHex($address)],
655 | 'limit' => $limit,
656 | 'offset' => $offset
657 | ]);
658 |
659 | return array_merge($response, ['direction' => $direction]);
660 | }
661 |
662 | /**
663 | * Count all transactions on the network
664 | *
665 | * @return integer
666 | * @throws TronException
667 | */
668 | public function getTransactionCount(): int
669 | {
670 | $response = $this->manager->request('wallet/totaltransaction');
671 | return $response['num'];
672 | }
673 |
674 | /**
675 | * Send transaction to Blockchain
676 | *
677 | * @param string $to
678 | * @param float $amount
679 | * @param string|null $message
680 | * @param string|null $from
681 | *
682 | * @return array
683 | * @throws TronException
684 | */
685 | public function sendTransaction(string $to, float $amount, string $from = null, string $message = null): array
686 | {
687 | if (is_null($from)) {
688 | $from = $this->address['hex'];
689 | }
690 |
691 | $transaction = $this->transactionBuilder->sendTrx($to, $amount, $from, $message);
692 | $signedTransaction = $this->signTransaction($transaction);
693 |
694 |
695 | $response = $this->sendRawTransaction($signedTransaction);
696 | return array_merge($response, $signedTransaction);
697 | }
698 |
699 | /**
700 | * Send token transaction to Blockchain
701 | *
702 | * @param string $to
703 | * @param float $amount
704 | * @param int $tokenID
705 | * @param string $from
706 | *
707 | * @return array
708 | * @throws TronException
709 | */
710 | public function sendTokenTransaction(string $to, float $amount, int $tokenID = null, string $from = null): array
711 | {
712 | if (is_null($from)) {
713 | $from = $this->address['hex'];
714 | }
715 |
716 | $transaction = $this->transactionBuilder->sendToken($to, $this->toTron($amount), (string)$tokenID, $from);
717 | $signedTransaction = $this->signTransaction($transaction);
718 |
719 | $response = $this->sendRawTransaction($signedTransaction);
720 |
721 | return array_merge($response, $signedTransaction);
722 | }
723 |
724 | /**
725 | * Sign the transaction, the api has the risk of leaking the private key,
726 | * please make sure to call the api in a secure environment
727 | *
728 | * @param $transaction
729 | * @param string|null $message
730 | * @return array
731 | * @throws TronException
732 | */
733 | public function signTransaction($transaction, string $message = null): array
734 | {
735 | if(!$this->privateKey) {
736 | throw new TronException('Missing private key');
737 | }
738 |
739 | if(!is_array($transaction)) {
740 | throw new TronException('Invalid transaction provided');
741 | }
742 |
743 | if(isset($transaction['Error']))
744 | throw new TronException($transaction['Error']);
745 |
746 |
747 | if(isset($transaction['signature'])) {
748 | throw new TronException('Transaction is already signed');
749 | }
750 |
751 | if(!is_null($message)) {
752 | $transaction['raw_data']['data'] = $this->stringUtf8toHex($message);
753 | }
754 |
755 |
756 | $signature = Support\Secp::sign($transaction['txID'], $this->privateKey);
757 | $transaction['signature'] = [$signature];
758 |
759 | return $transaction;
760 | }
761 |
762 | /**
763 | * Broadcast the signed transaction
764 | *
765 | * @param $signedTransaction
766 | * @return array
767 | * @throws TronException
768 | */
769 | public function sendRawTransaction($signedTransaction): array
770 | {
771 | if(!is_array($signedTransaction)) {
772 | throw new TronException('Invalid transaction provided');
773 | }
774 |
775 | if(!array_key_exists('signature', $signedTransaction) || !is_array($signedTransaction['signature'])) {
776 | throw new TronException('Transaction is not signed');
777 | }
778 |
779 | return $this->manager->request('wallet/broadcasttransaction',
780 | $signedTransaction);
781 | }
782 |
783 | /**
784 | * Modify account name
785 | * Note: Username is allowed to edit only once.
786 | *
787 | * @param $address
788 | * @param $account_name
789 | * @return array
790 | * @throws TronException
791 | */
792 | public function changeAccountName(string $address = null, string $account_name)
793 | {
794 | $address = (!is_null($address) ? $address : $this->address['hex']);
795 |
796 | $transaction = $this->manager->request('wallet/updateaccount', [
797 | 'account_name' => $this->stringUtf8toHex($account_name),
798 | 'owner_address' => $this->toHex($address)
799 | ]);
800 |
801 | $signedTransaction = $this->signTransaction($transaction);
802 | $response = $this->sendRawTransaction($signedTransaction);
803 |
804 | return $response;
805 | }
806 |
807 | /**
808 | * Send funds to the Tron account (option 2)
809 | *
810 | * @param array $args
811 | * @return array
812 | * @throws TronException
813 | */
814 | public function send(...$args): array {
815 | return $this->sendTransaction(...$args);
816 | }
817 |
818 | /**
819 | * Send funds to the Tron account (option 3)
820 | *
821 | * @param array $args
822 | * @return array
823 | * @throws TronException
824 | */
825 | public function sendTrx(...$args): array {
826 | return $this->sendTransaction(...$args);
827 | }
828 |
829 | /**
830 | * Creating a new token based on Tron
831 | *
832 | * @param array token {
833 | * "owner_address": "41e552f6487585c2b58bc2c9bb4492bc1f17132cd0",
834 | * "name": "0x6173736574497373756531353330383934333132313538",
835 | * "abbr": "0x6162627231353330383934333132313538",
836 | * "total_supply": 4321,
837 | * "trx_num": 1,
838 | * "num": 1,
839 | * "start_time": 1530894315158,
840 | * "end_time": 1533894312158,
841 | * "description": "007570646174654e616d6531353330363038383733343633",
842 | * "url": "007570646174654e616d6531353330363038383733343633",
843 | * "free_asset_net_limit": 10000,
844 | * "public_free_asset_net_limit": 10000,
845 | * "frozen_supply": { "frozen_amount": 1, "frozen_days": 2 }
846 | *
847 | * @return array
848 | * @throws TronException
849 | */
850 | public function createToken($token = [])
851 | {
852 | return $this->manager->request('wallet/createassetissue', [
853 | 'owner_address' => $this->toHex($token['owner_address']),
854 | 'name' => $this->stringUtf8toHex($token['name']),
855 | 'abbr' => $this->stringUtf8toHex($token['abbr']),
856 | 'description' => $this->stringUtf8toHex($token['description']),
857 | 'url' => $this->stringUtf8toHex($token['url']),
858 | 'total_supply' => $token['total_supply'],
859 | 'trx_num' => $token['trx_num'],
860 | 'num' => $token['num'],
861 | 'start_time' => $token['start_time'],
862 | 'end_time' => $token['end_time'],
863 | 'free_asset_net_limit' => $token['free_asset_net_limit'],
864 | 'public_free_asset_net_limit' => $token['public_free_asset_net_limit'],
865 | 'frozen_supply' => $token['frozen_supply']
866 | ]);
867 | }
868 |
869 | /**
870 | * Create an account.
871 | * Uses an already activated account to create a new account
872 | *
873 | * @param $address
874 | * @param $newAccountAddress
875 | * @return array
876 | * @throws TronException
877 | */
878 | public function registerAccount(string $address, string $newAccountAddress): array
879 | {
880 | return $this->manager->request('wallet/createaccount', [
881 | 'owner_address' => $this->toHex($address),
882 | 'account_address' => $this->toHex($newAccountAddress)
883 | ]);
884 | }
885 |
886 | /**
887 | * Apply to become a super representative
888 | *
889 | * @param $address
890 | * @param $url
891 | * @return array
892 | * @throws TronException
893 | */
894 | public function applyForSuperRepresentative(string $address, string $url)
895 | {
896 | return $this->manager->request('wallet/createwitness', [
897 | 'owner_address' => $this->toHex($address),
898 | 'url' => $this->stringUtf8toHex($url)
899 | ]);
900 | }
901 |
902 | /**
903 | * Transfer Token
904 | *
905 | * @param $to
906 | * @param $amount
907 | * @param $tokenID
908 | * @param $from
909 | * @return array
910 | * @throws TronException
911 | */
912 | public function sendToken(string $to, int $amount, string $tokenID, string $from = null)
913 | {
914 | if($from == null) {
915 | $from = $this->address['hex'];
916 | }
917 |
918 | $transfer = $this->transactionBuilder->sendToken($to, $amount, $tokenID, $from);
919 | $signedTransaction = $this->signTransaction($transfer);
920 | $response = $this->sendRawTransaction($signedTransaction);
921 |
922 | return array_merge($response, $signedTransaction);
923 | }
924 |
925 | /**
926 | * Purchase a Token
927 | * @param $issuerAddress
928 | * @param $tokenID
929 | * @param $amount
930 | * @param null $buyer
931 | * @return array
932 | * @throws TronException
933 | */
934 | public function purchaseToken($issuerAddress, $tokenID, $amount, $buyer = null)
935 | {
936 | if($buyer == null) {
937 | $buyer = $this->address['hex'];
938 | }
939 |
940 | $purchase = $this->transactionBuilder->purchaseToken($issuerAddress, $tokenID, $amount, $buyer);
941 | $signedTransaction = $this->signTransaction($purchase);
942 | $response = $this->sendRawTransaction($signedTransaction);
943 |
944 | return array_merge($response, $signedTransaction);
945 | }
946 |
947 | /**
948 | * Freezes an amount of TRX.
949 | * Will give bandwidth OR Energy and TRON Power(voting rights) to the owner of the frozen tokens.
950 | *
951 | * @param float $amount
952 | * @param int $duration
953 | * @param string $resource
954 | * @param string $owner_address
955 | * @return array
956 | * @throws TronException
957 | */
958 | public function freezeBalance(float $amount = 0, int $duration = 3, string $resource = 'BANDWIDTH', string $owner_address = null)
959 | {
960 | if($owner_address == null) {
961 | $owner_address = $this->address['hex'];
962 | }
963 |
964 | $freeze = $this->transactionBuilder->freezeBalance($amount, $duration, $resource, $owner_address);
965 | $signedTransaction = $this->signTransaction($freeze);
966 | $response = $this->sendRawTransaction($signedTransaction);
967 |
968 | return array_merge($response, $signedTransaction);
969 | }
970 |
971 | /**
972 | * Unfreeze TRX that has passed the minimum freeze duration.
973 | * Unfreezing will remove bandwidth and TRON Power.
974 | *
975 | * @param string $resource
976 | * @param string $owner_address
977 | * @return array
978 | * @throws TronException
979 | */
980 | public function unfreezeBalance(string $resource = 'BANDWIDTH', string $owner_address = null)
981 | {
982 | if($owner_address == null) {
983 | $owner_address = $this->address['hex'];
984 | }
985 |
986 | $unfreeze = $this->transactionBuilder->unfreezeBalance($resource, $owner_address);
987 | $signedTransaction = $this->signTransaction($unfreeze);
988 | $response = $this->sendRawTransaction($signedTransaction);
989 |
990 | return array_merge($response, $signedTransaction);
991 | }
992 |
993 | /**
994 | * Withdraw Super Representative rewards, useable every 24 hours.
995 | *
996 | * @param string $owner_address
997 | * @return array
998 | * @throws TronException
999 | */
1000 | public function withdrawBlockRewards(string $owner_address = null)
1001 | {
1002 | if($owner_address == null) {
1003 | $owner_address = $this->address['hex'];
1004 | }
1005 |
1006 | $withdraw = $this->transactionBuilder->withdrawBlockRewards($owner_address);
1007 | $signedTransaction = $this->signTransaction($withdraw);
1008 | $response = $this->sendRawTransaction($signedTransaction);
1009 |
1010 | return array_merge($response, $signedTransaction);
1011 | }
1012 |
1013 | /**
1014 | * Update a Token's information
1015 | *
1016 | * @param string $description
1017 | * @param string $url
1018 | * @param int $freeBandwidth
1019 | * @param int $freeBandwidthLimit
1020 | * @param $owner_address
1021 | * @return array
1022 | * @throws TronException
1023 | */
1024 | public function updateToken(string $description,
1025 | string $url,
1026 | int $freeBandwidth = 0,
1027 | int $freeBandwidthLimit = 0,
1028 | string $owner_address = null)
1029 | {
1030 | if($owner_address == null) {
1031 | $owner_address = $this->address['hex'];
1032 | }
1033 |
1034 | $withdraw = $this->transactionBuilder->updateToken($description, $url, $freeBandwidth, $freeBandwidthLimit, $owner_address);
1035 | $signedTransaction = $this->signTransaction($withdraw);
1036 | $response = $this->sendRawTransaction($signedTransaction);
1037 |
1038 | return array_merge($response, $signedTransaction);
1039 | }
1040 |
1041 | /**
1042 | * Node list
1043 | *
1044 | * @return array
1045 | * @throws TronException
1046 | */
1047 | public function listNodes(): array
1048 | {
1049 | $nodes = $this->manager->request('wallet/listnodes');
1050 | return array_map(function($item) {
1051 | $address = $item['address'];
1052 | return sprintf('%s:%s', $this->toUtf8($address['host']), $address['port']);
1053 | }, $nodes['nodes']);
1054 | }
1055 |
1056 |
1057 | /**
1058 | * List the tokens issued by an account.
1059 | *
1060 | * @param string $address
1061 | * @return array
1062 | * @throws TronException
1063 | */
1064 | public function getTokensIssuedByAddress(string $address = null)
1065 | {
1066 | $address = (!is_null($address) ? $this->toHex($address) : $this->address['hex']);
1067 | return $this->manager->request('wallet/getassetissuebyaccount',[
1068 | 'address' => $address
1069 | ]);
1070 | }
1071 |
1072 | /**
1073 | * Query token by name.
1074 | *
1075 | * @param $tokenID
1076 | * @return array
1077 | * @throws TronException
1078 | */
1079 | public function getTokenFromID($tokenID = null)
1080 | {
1081 | return $this->manager->request('wallet/getassetissuebyname', [
1082 | 'value' => $this->stringUtf8toHex($tokenID)
1083 | ]);
1084 | }
1085 |
1086 | /**
1087 | * Query a range of blocks by block height
1088 | *
1089 | * @param int $start
1090 | * @param int $end
1091 | * @return array
1092 | * @throws TronException
1093 | */
1094 | public function getBlockRange(int $start = 0, int $end = 30)
1095 | {
1096 | if(!is_integer($start) || $start < 0) {
1097 | throw new TronException('Invalid start of range provided');
1098 | }
1099 |
1100 | if(!is_integer($end) || $end <= $start) {
1101 | throw new TronException('Invalid end of range provided');
1102 | }
1103 |
1104 | return $this->manager->request('wallet/getblockbylimitnext', [
1105 | 'startNum' => intval($start),
1106 | 'endNum' => intval($end) + 1
1107 | ])['block'];
1108 | }
1109 |
1110 | /**
1111 | * Query the latest blocks
1112 | *
1113 | * @param int $limit
1114 | * @return array
1115 | * @throws TronException
1116 | */
1117 | public function getLatestBlocks(int $limit = 1): array
1118 | {
1119 | if(!is_integer($limit) || $limit <= 0) {
1120 | throw new TronException('Invalid limit provided');
1121 | }
1122 |
1123 | return $this->manager->request('wallet/getblockbylatestnum', [
1124 | 'num' => $limit
1125 | ])['block'];
1126 | }
1127 |
1128 | /**
1129 | * Query the list of Super Representatives
1130 | *
1131 | * @return array
1132 | * @throws TronException
1133 | */
1134 | public function listSuperRepresentatives(): array
1135 | {
1136 | return $this->manager->request('wallet/listwitnesses')['witnesses'];
1137 | }
1138 |
1139 | /**
1140 | * Query the list of Tokens with pagination
1141 | *
1142 | * @param int $limit
1143 | * @param int $offset
1144 | * @return array
1145 | * @throws TronException
1146 | */
1147 | public function listTokens(int $limit = 0, int $offset = 0)
1148 | {
1149 | if(!is_integer($limit) || $limit < 0 || ($offset && $limit < 1)) {
1150 | throw new TronException('Invalid limit provided');
1151 | }
1152 |
1153 | if(!is_integer($offset) || $offset < 0) {
1154 | throw new TronException('Invalid offset provided');
1155 | }
1156 |
1157 | if(!$limit) {
1158 | return $this->manager->request('wallet/getassetissuelist')['assetIssue'];
1159 | }
1160 |
1161 | return $this->manager->request('wallet/getpaginatedassetissuelist', [
1162 | 'offset' => intval($offset),
1163 | 'limit' => intval($limit)
1164 | ])['assetIssue'];
1165 | }
1166 |
1167 | /**
1168 | * Get the time of the next Super Representative vote
1169 | *
1170 | * @return float
1171 | * @throws TronException
1172 | */
1173 | public function timeUntilNextVoteCycle(): float
1174 | {
1175 | $num = $this->manager->request('wallet/getnextmaintenancetime')['num'];
1176 |
1177 | if($num == -1) {
1178 | throw new TronException('Failed to get time until next vote cycle');
1179 | }
1180 |
1181 | return floor($num / 1000);
1182 | }
1183 |
1184 | /**
1185 | * Validate address
1186 | *
1187 | * @param string $address
1188 | * @param bool $hex
1189 | * @return array
1190 | * @throws TronException
1191 | */
1192 | public function validateAddress(string $address = null, bool $hex = false): array
1193 | {
1194 | $address = (!is_null($address) ? $address : $this->address['hex']);
1195 | if($hex) {
1196 | $address = $this->toHex($address);
1197 | }
1198 | return $this->manager->request('wallet/validateaddress', [
1199 | 'address' => $address
1200 | ]);
1201 | }
1202 |
1203 | /**
1204 | * Validate Tron Address (Locale)
1205 | *
1206 | * @param string|null $address
1207 | * @return bool
1208 | */
1209 | public function isAddress(string $address = null): bool
1210 | {
1211 | if(strlen($address) !== self::ADDRESS_SIZE)
1212 | return false;
1213 |
1214 | $address = Base58Check::decode($address, 0, 0, false);
1215 | $utf8 = hex2bin($address);
1216 |
1217 | if(strlen($utf8) !== 25) return false;
1218 | if(strpos($utf8 , chr(self::ADDRESS_PREFIX_BYTE)) !== 0) return false;
1219 |
1220 | $checkSum = substr($utf8, 21);
1221 | $address = substr($utf8, 0, 21);
1222 |
1223 | $hash0 = Hash::SHA256($address);
1224 | $hash1 = Hash::SHA256($hash0);
1225 | $checkSum1 = substr($hash1, 0, 4);
1226 |
1227 | if ($checkSum === $checkSum1)
1228 | return true;
1229 | return false;
1230 | }
1231 |
1232 | /**
1233 | * Deploys a contract
1234 | *
1235 | * @param $abi
1236 | * @param $bytecode
1237 | * @param $feeLimit
1238 | * @param $address
1239 | * @param int $callValue
1240 | * @param int $bandwidthLimit
1241 | * @return array
1242 | * @throws TronException
1243 | */
1244 | public function deployContract($abi, $bytecode, $feeLimit, $address, $callValue = 0, $bandwidthLimit = 0)
1245 | {
1246 | $payable = array_filter(json_decode($abi, true), function($v)
1247 | {
1248 | if($v['type'] == 'constructor' && $v['payable']) {
1249 | return $v['payable'];
1250 | }
1251 | return null;
1252 | });
1253 |
1254 | if($feeLimit > 1000000000) {
1255 | throw new TronException('fee_limit must not be greater than 1000000000');
1256 | }
1257 |
1258 | if($payable && $callValue == 0) {
1259 | throw new TronException('call_value must be greater than 0 if contract is type payable');
1260 | }
1261 |
1262 | if(!$payable && $callValue > 0) {
1263 | throw new TronException('call_value can only equal to 0 if contract type isn‘t payable');
1264 | }
1265 |
1266 | return $this->manager->request('wallet/deploycontract', [
1267 | 'owner_address' => $this->toHex($address),
1268 | 'fee_limit' => $feeLimit,
1269 | 'call_value' => $callValue,
1270 | 'consume_user_resource_percent' => $bandwidthLimit,
1271 | 'abi' => $abi,
1272 | 'bytecode' => $bytecode
1273 | ]);
1274 | }
1275 |
1276 | /**
1277 | * Get a list of exchanges
1278 | *
1279 | * @return array
1280 | * @throws TronException
1281 | */
1282 | public function listExchanges()
1283 | {
1284 | return $this->manager->request('/wallet/listexchanges', []);
1285 | }
1286 |
1287 | /**
1288 | * Query the resource information of the account
1289 | *
1290 | * @param string $address
1291 | * @return array
1292 | * @throws TronException
1293 | */
1294 | public function getAccountResources(string $address = null)
1295 | {
1296 | $address = (!is_null($address) ? $address : $this->address['hex']);
1297 |
1298 | return $this->manager->request('/wallet/getaccountresource', [
1299 | 'address' => $this->toHex($address)
1300 | ]);
1301 | }
1302 |
1303 | /**
1304 | * Create a new account
1305 | *
1306 | * @return TronAddress
1307 | * @throws TronException
1308 | */
1309 | public function createAccount(): TronAddress
1310 | {
1311 | return $this->generateAddress();
1312 | }
1313 |
1314 | public function getAddressHex(string $pubKeyBin): string
1315 | {
1316 | if (strlen($pubKeyBin) == 65) {
1317 | $pubKeyBin = substr($pubKeyBin, 1);
1318 | }
1319 |
1320 | $hash = Keccak::hash($pubKeyBin, 256);
1321 |
1322 | return self::ADDRESS_PREFIX . substr($hash, 24);
1323 | }
1324 |
1325 | public function getBase58CheckAddress(string $addressBin): string
1326 | {
1327 | $hash0 = Hash::SHA256($addressBin);
1328 | $hash1 = Hash::SHA256($hash0);
1329 | $checksum = substr($hash1, 0, 4);
1330 | $checksum = $addressBin . $checksum;
1331 |
1332 | return Base58::encode(Crypto::bin2bc($checksum));
1333 | }
1334 |
1335 | /**
1336 | * Generate new address
1337 | *
1338 | * @return TronAddress
1339 | * @throws TronException
1340 | */
1341 | public function generateAddress(): TronAddress
1342 | {
1343 | $ec = new EC('secp256k1');
1344 |
1345 | // Generate keys
1346 | $key = $ec->genKeyPair();
1347 | $priv = $ec->keyFromPrivate($key->priv);
1348 | $pubKeyHex = $priv->getPublic(false, "hex");
1349 |
1350 | $pubKeyBin = hex2bin($pubKeyHex);
1351 | $addressHex = $this->getAddressHex($pubKeyBin);
1352 | $addressBin = hex2bin($addressHex);
1353 | $addressBase58 = $this->getBase58CheckAddress($addressBin);
1354 |
1355 | return new TronAddress([
1356 | 'private_key' => $priv->getPrivate('hex'),
1357 | 'public_key' => $pubKeyHex,
1358 | 'address_hex' => $addressHex,
1359 | 'address_base58' => $addressBase58
1360 | ]);
1361 | }
1362 |
1363 | /**
1364 | * Helper function that will convert HEX to UTF8
1365 | *
1366 | * @param $str
1367 | * @return string
1368 | */
1369 | public function toUtf8($str): string {
1370 | return pack('H*', $str);
1371 | }
1372 |
1373 | /**
1374 | * Query token by id.
1375 | *
1376 | * @param string $token_id
1377 | * @return array
1378 | * @throws TronException
1379 | */
1380 | public function getTokenByID(string $token_id): array
1381 | {
1382 | if(!is_string($token_id))
1383 | throw new TronException('Invalid token ID provided');
1384 |
1385 | return $this->manager->request('/wallet/getassetissuebyid', [
1386 | 'value' => $token_id
1387 | ]);
1388 | }
1389 | }
1390 |
--------------------------------------------------------------------------------
/src/TronAddress.php:
--------------------------------------------------------------------------------
1 | response = $data;
23 |
24 | // Проверяем ключи, перед выводом результатов
25 | if(!$this->array_keys_exist($this->response, ['address_hex', 'private_key', 'public_key'])) {
26 | throw new TronException('Incorrectly generated address');
27 | }
28 | }
29 |
30 | /**
31 | * Получение адреса
32 | *
33 | * @param bool $is_base58
34 | * @return string
35 | */
36 | public function getAddress(bool $is_base58 = false): string
37 | {
38 | return $this->response[($is_base58 == false) ? 'address_hex' : 'address_base58'];
39 | }
40 |
41 | /**
42 | * Получение публичного ключа
43 | *
44 | * @return string
45 | */
46 | public function getPublicKey(): string
47 | {
48 | return $this->response['public_key'];
49 | }
50 |
51 | /**
52 | * Получение приватного ключа
53 | *
54 | * @return string
55 | */
56 | public function getPrivateKey(): string
57 | {
58 | return $this->response['private_key'];
59 | }
60 |
61 | /**
62 | * Получение результатов в массике
63 | *
64 | * @return array
65 | */
66 | public function getRawData(): array
67 | {
68 | return $this->response;
69 | }
70 |
71 | /**
72 | * Проверка нескольких ключей
73 | *
74 | * @param array $array
75 | * @param array $keys
76 | * @return bool
77 | */
78 | private function array_keys_exist(array $array, array $keys = []): bool
79 | {
80 | $count = 0;
81 | if (!is_array($keys)) {
82 | $keys = func_get_args();
83 | array_shift($keys);
84 | }
85 | foreach ($keys as $key) {
86 | if (isset( $array[$key]) || array_key_exists($key, $array)) {
87 | $count ++;
88 | }
89 | }
90 |
91 | return count($keys) === $count;
92 | }
93 | }
--------------------------------------------------------------------------------
/src/TronAwareTrait.php:
--------------------------------------------------------------------------------
1 | hexString2Address($string);
18 | }
19 |
20 | return $this->hexString2Utf8($string);
21 | }
22 |
23 | /**
24 | * Convert to Hex
25 | *
26 | * @param $str
27 | * @return string
28 | */
29 | public function toHex($str)
30 | {
31 | if(mb_strlen($str) == 34 && mb_substr($str, 0, 1) === 'T') {
32 | return $this->address2HexString($str);
33 | };
34 |
35 | return $this->stringUtf8toHex($str);
36 | }
37 |
38 | /**
39 | * Check the address before converting to Hex
40 | *
41 | * @param $sHexAddress
42 | * @return string
43 | */
44 | public function address2HexString($sHexAddress)
45 | {
46 | if(strlen($sHexAddress) == 42 && mb_strpos($sHexAddress, '41') == 0) {
47 | return $sHexAddress;
48 | }
49 | return Base58Check::decode($sHexAddress,0,3);
50 | }
51 |
52 | /**
53 | * Check Hex address before converting to Base58
54 | *
55 | * @param $sHexString
56 | * @return string
57 | */
58 | public function hexString2Address($sHexString)
59 | {
60 | if(!ctype_xdigit($sHexString)) {
61 | return $sHexString;
62 | }
63 |
64 | if(strlen($sHexString) < 2 || (strlen($sHexString) & 1) != 0) {
65 | return '';
66 | }
67 |
68 | return Base58Check::encode($sHexString,0,false);
69 | }
70 |
71 | /**
72 | * Convert string to hex
73 | *
74 | * @param $sUtf8
75 | * @return string
76 | */
77 | public function stringUtf8toHex($sUtf8)
78 | {
79 | return bin2hex($sUtf8);
80 | }
81 |
82 | /**
83 | * Convert hex to string
84 | *
85 | * @param $sHexString
86 | * @return string
87 | */
88 | public function hexString2Utf8($sHexString)
89 | {
90 | return hex2bin($sHexString);
91 | }
92 |
93 | /**
94 | * Convert to great value
95 | *
96 | * @param $str
97 | * @return BigInteger
98 | */
99 | public function toBigNumber($str) {
100 | return new BigInteger($str);
101 | }
102 |
103 | /**
104 | * Convert trx to float
105 | *
106 | * @param $amount
107 | * @return float
108 | */
109 | public function fromTron($amount): float {
110 | return (float) bcdiv((string)$amount, (string)1e6, 8);
111 | }
112 |
113 | /**
114 | * Convert float to trx format
115 | *
116 | * @param $double
117 | * @return int
118 | */
119 | public function toTron($double): int {
120 | return (int) bcmul((string)$double, (string)1e6,0);
121 | }
122 |
123 | /**
124 | * Convert to SHA3
125 | *
126 | * @param $string
127 | * @param bool $prefix
128 | * @return string
129 | * @throws \Exception
130 | */
131 | public function sha3($string, $prefix = true)
132 | {
133 | return ($prefix ? '0x' : ''). Keccak::hash($string, 256);
134 | }
135 | }
--------------------------------------------------------------------------------
/src/TronInterface.php:
--------------------------------------------------------------------------------
1 | 'https://api.trongrid.io',
17 | 'solidityNode' => 'https://api.trongrid.io',
18 | 'eventServer' => 'https://api.trongrid.io',
19 | 'explorer' => 'https://apilist.tronscan.org',
20 | 'signServer' => ''
21 | ];
22 |
23 | /**
24 | * Providers
25 | *
26 | * @var array
27 | */
28 | protected array $providers = [
29 | 'fullNode' => [],
30 | 'solidityNode' => [],
31 | 'eventServer' => [],
32 | 'explorer' => [],
33 | 'signServer' => []
34 | ];
35 |
36 | /**
37 | * Status Page
38 | *
39 | * @var array
40 | */
41 | protected array $statusPage = [
42 | 'fullNode' => 'wallet/getnowblock',
43 | 'solidityNode' => 'walletsolidity/getnowblock',
44 | 'eventServer' => 'healthcheck',
45 | 'explorer' => 'api/system/status'
46 | ];
47 |
48 | /**
49 | * @param $tron
50 | * @param $providers
51 | * @throws Exception\TronException
52 | */
53 | public function __construct($tron, $providers)
54 | {
55 | $this->providers = $providers;
56 |
57 | foreach ($providers as $key => $value)
58 | {
59 | //Do not skip the supplier is empty
60 | if ($value == null) {
61 | $this->providers[$key] = new HttpProvider(
62 | $this->defaultNodes[$key]
63 | );
64 | };
65 |
66 | if(is_string($providers[$key]))
67 | $this->providers[$key] = new HttpProvider($value);
68 |
69 | if(in_array($key, ['signServer']))
70 | continue;
71 |
72 | $this->providers[$key]->setStatusPage($this->statusPage[$key]);
73 | }
74 | }
75 |
76 | /**
77 | * List of providers
78 | *
79 | * @return array
80 | */
81 | public function getProviders() {
82 | return $this->providers;
83 | }
84 |
85 | /**
86 | * Full Node
87 | *
88 | * @throws TronException
89 | * @return HttpProviderInterface
90 | */
91 | public function fullNode() : HttpProviderInterface
92 | {
93 | if (!array_key_exists('fullNode', $this->providers)) {
94 | throw new TronException('Full node is not activated.');
95 | }
96 |
97 | return $this->providers['fullNode'];
98 | }
99 |
100 | /**
101 | * Solidity Node
102 | *
103 | * @throws TronException
104 | * @return HttpProviderInterface
105 | */
106 | public function solidityNode() : HttpProviderInterface
107 | {
108 | if (!array_key_exists('solidityNode', $this->providers)) {
109 | throw new TronException('Solidity node is not activated.');
110 | }
111 |
112 | return $this->providers['solidityNode'];
113 | }
114 |
115 | /**
116 | * Sign server
117 | *
118 | * @throws TronException
119 | * @return HttpProviderInterface
120 | */
121 | public function signServer(): HttpProviderInterface
122 | {
123 | if (!array_key_exists('signServer', $this->providers)) {
124 | throw new TronException('Sign server is not activated.');
125 | }
126 |
127 | return $this->providers['signServer'];
128 | }
129 |
130 | /**
131 | * TronScan server
132 | *
133 | * @throws TronException
134 | * @return HttpProviderInterface
135 | */
136 | public function explorer(): HttpProviderInterface
137 | {
138 | if (!array_key_exists('explorer', $this->providers)) {
139 | throw new TronException('explorer is not activated.');
140 | }
141 |
142 | return $this->providers['explorer'];
143 | }
144 |
145 | /**
146 | * Event server
147 | *
148 | * @throws TronException
149 | * @return HttpProviderInterface
150 | */
151 | public function eventServer(): HttpProviderInterface
152 | {
153 | if (!array_key_exists('eventServer', $this->providers)) {
154 | throw new TronException('Event server is not activated.');
155 | }
156 |
157 | return $this->providers['eventServer'];
158 | }
159 |
160 | /**
161 | * Basic query to nodes
162 | *
163 | * @param $url
164 | * @param array $params
165 | * @param string $method
166 | * @return array
167 | * @throws TronException
168 | */
169 | public function request($url, array $params = [], string $method = 'post')
170 | {
171 | $split = explode('/', $url);
172 | if(in_array($split[0], ['walletsolidity', 'walletextension'])) {
173 | $response = $this->solidityNode()->request($url, $params, $method);
174 | } elseif(in_array($split[0], ['event'])) {
175 | $response = $this->eventServer()->request($url, $params, 'get');
176 | } elseif (in_array($split[0], ['trx-sign'])) {
177 | $response = $this->signServer()->request($url, $params, 'post');
178 | } elseif(in_array($split[0], ['api'])) {
179 | $response = $this->explorer()->request($url, $params, 'get');
180 | }else {
181 | $response = $this->fullNode()->request($url, $params, $method);
182 | }
183 |
184 | return $response;
185 | }
186 |
187 | /**
188 | * Check connections
189 | *
190 | * @return array
191 | */
192 | public function isConnected(): array
193 | {
194 | $array = [];
195 | foreach ($this->providers as $key => $value) {
196 | $array[] = [
197 | $key => boolval($value->isConnected())
198 | ];
199 | }
200 |
201 | return $array;
202 | }
203 | }
--------------------------------------------------------------------------------
/src/trc20.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "constant": true,
4 | "inputs": [],
5 | "name": "name",
6 | "outputs": [
7 | {
8 | "name": "",
9 | "type": "string"
10 | }
11 | ],
12 | "payable": false,
13 | "stateMutability": "view",
14 | "type": "function"
15 | },
16 | {
17 | "constant": false,
18 | "inputs": [
19 | {
20 | "name": "spender",
21 | "type": "address"
22 | },
23 | {
24 | "name": "value",
25 | "type": "uint256"
26 | }
27 | ],
28 | "name": "approve",
29 | "outputs": [
30 | {
31 | "name": "",
32 | "type": "bool"
33 | }
34 | ],
35 | "payable": false,
36 | "stateMutability": "nonpayable",
37 | "type": "function"
38 | },
39 | {
40 | "constant": true,
41 | "inputs": [],
42 | "name": "totalSupply",
43 | "outputs": [
44 | {
45 | "name": "",
46 | "type": "uint256"
47 | }
48 | ],
49 | "payable": false,
50 | "stateMutability": "view",
51 | "type": "function"
52 | },
53 | {
54 | "constant": false,
55 | "inputs": [
56 | {
57 | "name": "sender",
58 | "type": "address"
59 | },
60 | {
61 | "name": "recipient",
62 | "type": "address"
63 | },
64 | {
65 | "name": "amount",
66 | "type": "uint256"
67 | }
68 | ],
69 | "name": "transferFrom",
70 | "outputs": [
71 | {
72 | "name": "",
73 | "type": "bool"
74 | }
75 | ],
76 | "payable": false,
77 | "stateMutability": "nonpayable",
78 | "type": "function"
79 | },
80 | {
81 | "constant": true,
82 | "inputs": [],
83 | "name": "decimals",
84 | "outputs": [
85 | {
86 | "name": "",
87 | "type": "uint8"
88 | }
89 | ],
90 | "payable": false,
91 | "stateMutability": "view",
92 | "type": "function"
93 | },
94 | {
95 | "constant": false,
96 | "inputs": [
97 | {
98 | "name": "spender",
99 | "type": "address"
100 | },
101 | {
102 | "name": "addedValue",
103 | "type": "uint256"
104 | }
105 | ],
106 | "name": "increaseAllowance",
107 | "outputs": [
108 | {
109 | "name": "",
110 | "type": "bool"
111 | }
112 | ],
113 | "payable": false,
114 | "stateMutability": "nonpayable",
115 | "type": "function"
116 | },
117 | {
118 | "constant": true,
119 | "inputs": [
120 | {
121 | "name": "account",
122 | "type": "address"
123 | }
124 | ],
125 | "name": "balanceOf",
126 | "outputs": [
127 | {
128 | "name": "",
129 | "type": "uint256"
130 | }
131 | ],
132 | "payable": false,
133 | "stateMutability": "view",
134 | "type": "function"
135 | },
136 | {
137 | "constant": true,
138 | "inputs": [],
139 | "name": "symbol",
140 | "outputs": [
141 | {
142 | "name": "",
143 | "type": "string"
144 | }
145 | ],
146 | "payable": false,
147 | "stateMutability": "view",
148 | "type": "function"
149 | },
150 | {
151 | "constant": false,
152 | "inputs": [
153 | {
154 | "name": "spender",
155 | "type": "address"
156 | },
157 | {
158 | "name": "subtractedValue",
159 | "type": "uint256"
160 | }
161 | ],
162 | "name": "decreaseAllowance",
163 | "outputs": [
164 | {
165 | "name": "",
166 | "type": "bool"
167 | }
168 | ],
169 | "payable": false,
170 | "stateMutability": "nonpayable",
171 | "type": "function"
172 | },
173 | {
174 | "constant": false,
175 | "inputs": [
176 | {
177 | "name": "recipient",
178 | "type": "address"
179 | },
180 | {
181 | "name": "amount",
182 | "type": "uint256"
183 | }
184 | ],
185 | "name": "transfer",
186 | "outputs": [
187 | {
188 | "name": "",
189 | "type": "bool"
190 | }
191 | ],
192 | "payable": false,
193 | "stateMutability": "nonpayable",
194 | "type": "function"
195 | },
196 | {
197 | "constant": true,
198 | "inputs": [
199 | {
200 | "name": "owner",
201 | "type": "address"
202 | },
203 | {
204 | "name": "spender",
205 | "type": "address"
206 | }
207 | ],
208 | "name": "allowance",
209 | "outputs": [
210 | {
211 | "name": "",
212 | "type": "uint256"
213 | }
214 | ],
215 | "payable": false,
216 | "stateMutability": "view",
217 | "type": "function"
218 | },
219 | {
220 | "inputs": [],
221 | "payable": false,
222 | "stateMutability": "nonpayable",
223 | "type": "constructor"
224 | },
225 | {
226 | "anonymous": false,
227 | "inputs": [
228 | {
229 | "indexed": true,
230 | "name": "from",
231 | "type": "address"
232 | },
233 | {
234 | "indexed": true,
235 | "name": "to",
236 | "type": "address"
237 | },
238 | {
239 | "indexed": false,
240 | "name": "value",
241 | "type": "uint256"
242 | }
243 | ],
244 | "name": "Transfer",
245 | "type": "event"
246 | },
247 | {
248 | "anonymous": false,
249 | "inputs": [
250 | {
251 | "indexed": true,
252 | "name": "owner",
253 | "type": "address"
254 | },
255 | {
256 | "indexed": true,
257 | "name": "spender",
258 | "type": "address"
259 | },
260 | {
261 | "indexed": false,
262 | "name": "value",
263 | "type": "uint256"
264 | }
265 | ],
266 | "name": "Approval",
267 | "type": "event"
268 | }
269 | ]
--------------------------------------------------------------------------------
/tests/TronTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($tron->isValidProvider($provider), true);
23 | }
24 |
25 | public function test_setAddress()
26 | {
27 | $tron = new Tron(new HttpProvider(self::FULL_NODE_API), new HttpProvider(self::SOLIDITY_NODE_API));
28 | $tron->setAddress(self::ADDRESS_HEX);
29 |
30 | $this->assertEquals($tron->getAddress()['hex'],self::ADDRESS_HEX);
31 | $this->assertEquals($tron->getAddress()['base58'], self::ADDRESS_BASE58);
32 | }
33 |
34 | public function test_setDefaultBlock()
35 | {
36 | $tron = new Tron(new HttpProvider(self::FULL_NODE_API),new HttpProvider(self::SOLIDITY_NODE_API));
37 | $tron->setDefaultBlock(1);
38 | $this->assertEquals($tron->getDefaultBlock(), 1);
39 |
40 | $tron->setDefaultBlock(-2);
41 | $this->assertEquals($tron->getDefaultBlock(),2);
42 |
43 | $tron->setDefaultBlock(0);
44 | $this->assertEquals($tron->getDefaultBlock(),0);
45 |
46 | $tron->setDefaultBlock();
47 | $this->assertEquals($tron->getDefaultBlock(),false);
48 |
49 | $tron->setDefaultBlock('latest');
50 | $this->assertEquals($tron->getDefaultBlock(),'latest');
51 | }
52 | }
--------------------------------------------------------------------------------