An open API service indexing awesome lists of open source software.

https://github.com/gmajor-encrypt/php-substrate-api

substrate rpc php lib
https://github.com/gmajor-encrypt/php-substrate-api

blockchain php rpc substrate

Last synced: 2 months ago
JSON representation

substrate rpc php lib

Awesome Lists containing this project

README

          

# php-substrate-api

---
PHP Substrate RPC Api

## Requirement

* php >=8.0 (install ffi https://www.php.net/manual/en/intro.ffi.php)
* curl (https://www.php.net/manual/en/curl.installation.php)
* json (https://www.php.net/manual/en/json.installation.php)
* sodium (https://www.php.net/manual/en/sodium.installation.php)
* gmp (https://www.php.net/manual/en/gmp.installation.php)
* dom (https://www.php.net/manual/en/dom.installation.php)
* mbstring (https://www.php.net/manual/en/mbstring.installation.php)

## Installation

If you want to install php-substrate-api in an existing project
```sh
composer require gmajor/php-substrate-api
```

if it is a new project
```sh
mkdir new_project && cd new_project && composer init --stability=dev && composer require gmajor/php-substrate-api
```

## Basic Usage

### Autoloading

Codec supports `PSR-4` autoloaders.

```php
close(); // close websocket connection
```

* Read RPC Data

```php
rpc->{$pallet_name}->{$method}, like
// call rpc system_health
$res = $client->rpc->system->health();
var_dump($res); #{"peers": 31, "isSyncing": false, "shouldHavePeers": true}
// or call rpc chain_getFinalizedHead
$client->rpc->chain->getFinalizedHead();

$client->close(); // do not forget close websocket connection !
```

* Hasher

We currently support 6 hash methods, including Blake2_128,Blake2_256,Twox128,Twox256,Twox64Concat,Blake2_128Concat。

```php
ByHasherName("Blake2_128", "20be52a5a80cad065651ec35fcb1a212bc669aabb52d68d8780a41e29ec9c83e");
// Blake2_256
$hasher->ByHasherName("Blake2_256", "20be52a5a80cad065651ec35fcb1a212bc669aabb52d68d8780a41e29ec9c83e")
// Twox128
$hasher->TwoxHash("Key", 128)
// Twox128
$hasher->TwoxHash("Sudo", 256)
// XXHash64
$hasher->XXHash64(0, "test");
// Twox64Concat
$hasher->ByHasherName("Twox64Concat", "0xad2ecd66275a1ded")
// Blake2_128Concat
$hasher->ByHasherName("Blake2_128Concat", "20be")
````

* Storage key

When you access storage using Substrate RPC(like
rpc [state_getStorage](https://polkadot.js.org/docs/substrate/rpc#getstoragechildkey-prefixedstoragekey-key-storagekey-at-hash-optionstoragedata)
, you need to provide the key associated with the item,

```php
process("metadata", new ScaleBytes($metadataV14RawValue))["metadata"];
// Timestamp.now storage key
$hasher = new Hasher();
print_r(StorageKey::encode($hasher,"Timestamp", "now", $metadata, []));
// Staking.Bonded storage key with param accountId
print_r(StorageKey::encode($hasher,"System", "Account", $metadata, ["0x1c79a5ada2ff0d55aaa65dfeaf0cba667babf312f9bf100444279b34cd769e49"]))

```

* Json RPC

RPC methods that are Remote Calls available by default and allow you to interact with the actual node, query, and
submit.

```php
// On the api, these are exposed via .rpc.., like this
$client->rpc->system->health(); // for rpc system_health
$client->rpc->author->rotateKeys(); // for rpc author_rotateKeys
$client->rpc->state->getMetadata("hash"); // for rpc state_getMetadata
```

All rpc interface has been declare at https://github.com/gmajor-encrypt/php-substrate-api/tree/master/src/Rpc/JsonRpc

More detailed RPC documentation can be found at https://polkadot.js.org/docs/substrate/rpc

* Send extrinsics

Below is a simple example of sending a token, you can use tx.. to send any transaction

```php
"8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"];
$wsClient = new SubstrateRpc($endpoint);
$hasher = new Hasher();
$wsClient->setSigner(KeyPair::initKeyPair("sr25519", $AliceSeed, $hasher),$hasher);
$tx = $wsClient->tx;
$result = $tx->Balances->transfer($BobId, 12345);
var_dump($result); // transaction hash

// if you want waiting transaction exec, you can set tx with option
$tx->withOpt(["subscribe" => true]);
$result = $tx->Balances->transfer($BobId, 12345);
var_dump($result); // Will not return until execution is complete
$wsClient->close()
````

### Keyring

The Keyring allows you to perform operations on these keys (such as sign/verify) and never exposes the secretKey

to the outside world. Support ed25519(Edwards https://ed25519.cr.yp.to/) or sr25519(
schnorrkel https://github.com/w3f/schnorrkel)

```php
sign("msg");
// verify this message
$keyPair->verify($signature, "123");
```

### Contract

#### Metadata support

The metadata is used to describe a contract in a language agnostic way. Metadata can declare the storage and executable
methods and types contained in the contract

We currently support ink metadata v0,v1,v2,v3,v4.

```php
register_type($scale->getGenerator(), "some_prefix");
```

#### Deploy contract

After declaring a Contract class, you can call the new method to create a contract.

About how to build ink contract, you can refer to
this https://docs.substrate.io/tutorials/smart-contracts/prepare-your-first-contract/

Below is an example.

```php
setSigner(KeyPair::initKeyPair("sr25519",$seed, $hasher),$hasher);
$contract = new Contract($wsClient->tx);
// $inputData = constructor_selector + encode(args...)
$result = $contract->new($contract_code, $inputData); // with default option

# If you need to additionally set the gas limit and storageDepositLimit, you can set it like this
$result = $contract->new($contract_code, $inputData,[], ["gasLimit"=>100000,"storageDepositLimit"=>50000]); // with default option
```

#### Read Contract state

Reading the storage on the contract does not consume any gas, so anyone can read the contract.

You can simply read the contract through ```$contract->state->{$method}($param1,$param2)```

```php
setSigner(KeyPair::initKeyPair("sr25519", $seed, $hash),$hash);
// get abi
$v4 = ContractMetadataV4::to_obj(json_decode(file_get_contents(__DIR__ . '/ink/ink_v4.json'), true));
// register contract type
$v4->register_type($wsClient->tx->codec->getGenerator(), "testAbiMetadataV4Parse");

// read contract
$contract = new Contract($wsClient->tx, $contractAddress, $v4);
// call get method
$execResult = $contract->state->get();
// parse exec Result
$result =ContractExecResult::getDecodeResult($execResult, $wsClient->tx->codec)
print_r($result);
```

#### Send Contract transaction

Sending contract transactions is very similar to executing extrinsic. You can simply exec the contract through
```$contract->call->{$method}($param1,$param2,$option=[])```

```php
setSigner(KeyPair::initKeyPair("sr25519", $this->AliceSeed, $hasher),$hasher);

// register contract type
$v4 = ContractMetadataV4::to_obj(json_decode(file_get_contents(__DIR__ . '/ink/ink_v4.json'), true));
$v4->register_type($wsClient->tx->codec->getGenerator(), "testAbiMetadataV4Parse");

// send contract transaction
$contract = new Contract($wsClient->tx, $contractAddress, $v4);
$result = $contract->call->flip([]); // with default option

// If you need to additionally set the gas limit and storageDepositLimit, you can set it like this
$result = $contract->call->flip(["storageDepositLimit"=>$limit,["gasLimit"=>["refTime"=>$refTime,"proofSize"=>$proofSize]] ]);
print_r($result);// extrinsic_hash
```

#### Generate contract address

Since the address algorithm of the contract is fixed, it is easy to calculate the deployed contract address

```php
createTypeByTypeString("bytes");
Address::GenerateAddress($hasher, $deployer, $codeHash, $bytes->encode($inputData), $bytes->encode($salt)));

```

### Example

More examples can refer to the test file https://github.com/gmajor-encrypt/php-substrate-api/tree/master/test/Rpc

## Test

```bash
make test
```

## Troubleshooting

### FFI error FFI\Exception: Failed loading '../php-substrate-api/vendor/gmajor/sr25519-bindings/src/Crypto/sr25519.so'

The current default sr25519-bindings FFI is for mac. Unfortunately, php composer currently does not support automatic
compilation after install, so manual compilation is required. You can run this script

```bash
## For darwin
cd vendor/gmajor/sr25519-bindings/go && go build -buildmode=c-shared -o ../src/Crypto/sr25519.dylib .
## For linux
cd vendor/gmajor/sr25519-bindings/go && go build -buildmode=c-shared -o ../src/Crypto/sr25519.so .
```

### WebSocket\ConnectionException: Could not open socket to "127.0.0.1:9944"

In the test,The keyPair used in the test process is //Alice, **ws://127.0.0.1:9944** is used by default as the node for
testing SendTransaction. This node can start any private network settings by itself. You can also set the node address
through environment variables.

```base
export RPC_URL=ws://....
```

### gmajor/php-substrate-api v0.1.0 requires textalk/websocket dev-master -> found ``xxxx`` but it does not match your minimum-stability

You need set [minimum-stability](https://getcomposer.org/doc/04-schema.md#minimum-stability) like this ``"minimum-stability": "dev"`` in composer.

## Resources

- [sr25519](https://github.com/gmajor-encrypt/sr25519-bindings)
- [polkadot.js](http://polkadot.js.org/)
- [substrate.dev](https://docs.substrate.io/v3/runtime/custom-rpcs/)
- [substrate-api-sidecar](https://github.com/paritytech/substrate-api-sidecar)

## License

The package is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT)