BNB Smart Chain

BNB Smart Chain

PHP

web3.php is the main library PHP developers use to talk JSON-RPC to BNB Smart Chain, letting a server-side app query balances, read BEP-20 token state, or submit transactions on chain 56 without shelling out to another runtime. Install it with composer require web3p/web3.php. One design quirk shapes how you use it: the API is callback-based rather than returning values directly, so the cleanest approach is to wrap those calls in a dedicated service class that exposes simple methods to the rest of your code.

Because every web3.php call hands its result to a closure instead of returning it, sprinkling those callbacks through controllers quickly becomes unreadable. In a Laravel project especially, funnel them through a single service class — the controller asks the service for a BSC balance and gets a clean value back, while the callback plumbing stays hidden behind that boundary.

Web3.php

A client nests three objects: new Web3(new HttpProvider(new HttpRequestManager("https://bsc.therpc.io/YOUR_API_KEY"))) gives you a handle to BNB Smart Chain over HTTP. From there, each eth method takes a function($err, $result) callback. Discipline matters here. Always inspect $err first and bail if it's set, because reading $result after a failed chain-56 request yields a null or partial value that will bite you downstream.

<?php
use Web3\Web3;
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpRequestManager;
class EthereumService
{
private $web3;
public function __construct()
{
$this->web3 = new Web3(new HttpProvider(new HttpRequestManager('https://bsc.therpc.io/YOUR_API_KEY')));
}
public function getBalance(string $address): string
{
$balance = null;
$this->web3->eth->getBalance($address, 'latest', function ($err, $result) use (&$balance) {
if ($err !== null) {
throw new Exception($err->getMessage());
}
$balance = $result;
});
return $this->web3->utils->fromWei($balance, 'ether');
}
public function sendTransaction(array $transaction): string
{
$hash = null;
$this->web3->eth->sendTransaction($transaction, function ($err, $result) use (&$hash) {
if ($err !== null) {
throw new Exception($err->getMessage());
}
$hash = $result;
});
return $hash;
}
}

Laravel Integration

In Laravel, keep the BSC endpoint out of your code by reading it from configuration — config("services.ethereum.node_url") resolves from an environment variable, so the URL with your API key never lands in version control. For read-heavy screens, wrap balance lookups in Cache::remember with a short TTL: repeated requests for the same address are served from the cache instead of hammering chain 56, which keeps response times low and your request volume sane.

<?php
namespace App\Services;
use Web3\Web3;
use Illuminate\Support\Facades\Cache;
class EthereumService
{
private $web3;
public function __construct()
{
$this->web3 = new Web3(config('services.ethereum.node_url'));
}
public function getCachedBalance(string $address): string
{
return Cache::remember("eth_balance_{$address}", 300, function () use ($address) {
return $this->getBalance($address);
});
}
public function getTransactionCount(string $address): int
{
$count = null;
$this->web3->eth->getTransactionCount($address, 'latest', function ($err, $result) use (&$count) {
if ($err !== null) {
throw new Exception($err->getMessage());
}
$count = hexdec($result);
});
return $count;
}
}

Smart Contract Integration

For contract work, web3.php ships a Contract class you initialise with the ABI, then bind to a deployed address using ->at(), for example a BEP-20 token live on chain 56. A value read follows the same callback rhythm as the rest of the library: invoke ->call() with the function name and arguments, passing a closure that receives the decoded result so you can pull a token balance or symbol out of BNB Smart Chain.

<?php
use Web3\Contract;
class SmartContractService
{
private $contract;
public function __construct(string $abi, string $contractAddress)
{
$web3 = new Web3(new HttpProvider(new HttpRequestManager('https://bsc.therpc.io/YOUR_API_KEY')));
$this->contract = new Contract($web3->provider, $abi);
$this->contract->at($contractAddress);
}
public function callMethod(string $method, array $params = [])
{
$result = null;
$this->contract->call($method, $params, function ($err, $response) use (&$result) {
if ($err !== null) {
throw new Exception($err->getMessage());
}
$result = $response;
});
return $result;
}
}

Error Handling

Rather than passing raw callback $err values up the stack, define a domain EthereumException. Throw it from inside your service when a BSC call fails. Wrap the original error and you preserve the underlying detail while giving callers a single, intentional exception type to catch. A controller then writes one try/catch around its chain-56 interaction and responds consistently no matter where the failure originated.

<?php
class EthereumException extends Exception
{
private $ethError;
public function __construct(string $message, $ethError = null)
{
parent::__construct($message);
$this->ethError = $ethError;
}
public function getEthError()
{
return $this->ethError;
}
}
class SafeEthereumService
{
public function safeGetBalance(string $address): string
{
try {
return $this->getBalance($address);
} catch (Exception $e) {
throw new EthereumException(
"Failed to get balance for address: {$address}",
$e
);
}
}
}

Ready to call this in production?

Free tier covers personal projects. Pay-as-you-go scales without a card.