amashukov / bchn-rpc-client-php
Typed JSON-RPC client for Bitcoin Cash Node (BCHN) in pure PHP over any PSR-18 client — wallet, UTXO, PSBT, raw-transaction and double-spend-proof calls in the BCHN dialect, CashTokens token data, exact satoshi amounts, never floats.
Package info
github.com/AndreyMashukov/bchn-rpc-client-php
pkg:composer/amashukov/bchn-rpc-client-php
Requires
- php: >=8.3
- psr/http-client: ^1.0
- psr/http-factory: ^1.1
- psr/http-message: ^2.0
Requires (Dev)
- amashukov/rector-php-rules: ^0.4.1
- friendsofphp/php-cs-fixer: ^3.50
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- rector/rector: ^2.0
Suggests
- amashukov/cashaddr-php: CashAddr codec to validate and normalize the addresses this client returns.
- amashukov/http-client-php: PSR-18 cURL client + retry/header-injection middleware ready to drive this RPC client.
Provides
None
Conflicts
None
Replaces
None
README
Typed JSON-RPC client for Bitcoin Cash Node (BCHN) in pure PHP over any PSR-18 client — wallet, UTXO, PSBT, raw-transaction and DSProof calls in the BCHN dialect, exact satoshi amounts.
A typed client for the Bitcoin Cash Node JSON-RPC API (tested against BCHN v29.2.0) built on any PSR-18 HTTP client. BCHN shares its RPC heritage with Bitcoin Core but its signatures differ — createwallet takes four parameters, getnewaddress has no address type, lockunspent has no persistent flag, sendrawtransaction / testmempoolaccept take a boolean allowhighfees instead of a max fee rate, sighash types carry FORKID, there is estimatefee instead of estimatesmartfee, and outputs may carry CashTokens tokenData. This client follows the BCHN signatures exactly and returns final readonly Value Objects. Every JSON number is read as a string before decoding, so BCH amounts become exact integer satoshis (BchAmount) and never pass through a float.
Features
- BCHN dialect — signatures checked against the BCHN JSON-RPC reference, method by method.
- Exact amounts —
BchAmountholds integer satoshis parsed from the node's decimal text; token amounts stay decimal strings (they exceed 53 bits). - Typed prevouts —
getRawTransaction()asks for verbosity 2 and turnsvin.value/vin.scriptPubKeyinto aPrevout;Transaction::fee()is computed from them and isnullwhen any input value is unknown (coinbase) — never guessed. When a pruned node without-txindexcannot fetch an input's transaction for the fee calculation, the transaction is read again at verbosity 1, so every prevout and the fee arenull; any other error (an unknown transaction) is still thrown. - Wallet entry times —
listsinceblockentries carrytime,timeReceivedand, once mined,blockTime. - Outputs without guessing — an output exposes an address only when the node reports exactly one; OP_RETURN and bare multisig outputs keep their script type, value and token data with
address = null. - CashTokens —
tokenData(category, fungible amount, NFT capability and commitment) on UTXOs, inputs and outputs;listUnspent(includeTokens: true)sets BCHN'squery_options.includeTokens. - Multi-wallet —
withWallet('hot-wallet')targets/wallet/hot-wallet. - Loud failures —
BchnRpcExceptioncarries the RPCcodeand the HTTP status; a reply that is not JSON (a proxy's HTML 502, an empty 401) is reported with its status and the first 256 bytes of the body.
Installation
composer require amashukov/bchn-rpc-client-php
Usage
use Amashukov\BchnRpc\BchnRpcClient; use Amashukov\BchnRpc\Vo\BchAmount; use Amashukov\BchnRpc\Vo\PsbtOutput; use Amashukov\HttpClient\CurlClient; use Nyholm\Psr7\Factory\Psr17Factory; $psr17 = new Psr17Factory(); $node = new BchnRpcClient(new CurlClient($psr17, $psr17, timeoutSeconds: 30), $psr17, $psr17, 'http://127.0.0.1:8332', 'rpcuser', 'rpcpassword'); $wallet = $node->withWallet('hot-wallet'); $node->getBlockchainInfo()->blocks; $wallet->getWalletInfo()->balance->satoshis; $address = $wallet->getNewAddress('deposit'); $info = $wallet->getAddressInfo($address); // ismine, hdkeypath foreach ($wallet->listSinceBlock($lastBlock, 6)->transactions as $entry) { // $entry->txid, $entry->vout, $entry->address, $entry->amount->satoshis, $entry->confirmations } $utxos = $wallet->listUnspent(1, 9999999, [], true, includeTokens: false); $psbt = $wallet->createPsbt([$utxos[0]->outpoint], [PsbtOutput::payTo('bitcoincash:qr7fzmep8g7h7ymfxy74lgc0v950j3r2959lhtxxsl', BchAmount::fromSatoshis(10_000))]); $signed = $wallet->walletProcessPsbt($psbt); // sighash ALL|FORKID $final = $wallet->finalizePsbt($signed->psbt); if ($wallet->testMempoolAccept((string) $final->hex)->allowed) { $txid = $wallet->sendRawTransaction((string) $final->hex); } $tx = $node->getRawTransaction($txid); $fee = $tx->fee(); // ?BchAmount $satPerByte = $node->estimateFee(); // int, rounded up $score = $node->getDsProofScore($txid); // 0.0 … 1.0
Methods
| Method | BCHN call |
|---|---|
getBlockchainInfo() |
getblockchaininfo |
getWalletInfo() |
getwalletinfo |
createWallet(name, disablePrivateKeys, blank, passphrase) |
createwallet |
loadWallet(name) |
loadwallet |
getNewAddress(label) |
getnewaddress |
getAddressInfo(address) |
getaddressinfo |
listSinceBlock(blockHash, targetConfirmations, includeWatchOnly, includeRemoved, includeChange, label) |
listsinceblock |
listUnspent(minconf, maxconf, addresses, includeUnsafe, includeTokens) |
listunspent |
listLockUnspent() / lockUnspent(unlock, outpoints) |
listlockunspent / lockunspent |
getTransaction(txid, includeWatchOnly) |
gettransaction |
getRawTransaction(txid, blockHash) |
getrawtransaction (verbosity 2, falls back to 1 when prevouts are unavailable) |
getMempoolEntry(txid) |
getmempoolentry (null when not in the mempool) |
decodeRawTransaction(hex) |
decoderawtransaction |
getTxOut(txid, vout, includeMempool) |
gettxout (null when spent) |
createPsbt / walletProcessPsbt / finalizePsbt / decodePsbt |
PSBT calls |
testMempoolAccept(hex, allowHighFees) / sendRawTransaction(hex, allowHighFees) |
broadcast |
estimateFee() |
estimatefee, converted to sat/byte |
walletPassphrase / walletLock / backupWallet |
wallet security |
getDsProofScore(txid) |
getdsproofscore |
BCHN has no getbalances; the confirmed, unconfirmed and immature balances come from getWalletInfo().
Requirements
- PHP 8.3+ on a 64-bit build
- A PSR-18 client + PSR-17 request / stream factories
Related packages
| Package | Role |
|---|---|
| amashukov/cashaddr-php | CashAddr codec and typed address value object |
| amashukov/bitcoin-cash-php | Bitcoin Cash meta-package |
| amashukov/http-client-php | PSR-18 cURL client + retry / header-injection middleware |
Quality
- PHPStan level 9, clean.
- php-cs-fixer
@PER-CSruleset. - GitHub Actions CI on every push.
- Every method is pinned by a test against a fixture shaped like the BCHN reference output.
License
MIT — see LICENSE.