sandermuller / laravel-solana-sdk
Laravel wrapper for sandermuller/solana-php-sdk — service provider, facades, env-driven config, and artisan commands for Solana RPC.
Package info
github.com/SanderMuller/laravel-solana-sdk
pkg:composer/sandermuller/laravel-solana-sdk
Requires
- php: ^8.3
- illuminate/console: ^11.0||^12.0||^13.0
- illuminate/contracts: ^11.0||^12.0||^13.0
- illuminate/support: ^11.0||^12.0||^13.0
- sandermuller/solana-php-sdk: ^0.1.0
Requires (Dev)
- dg/bypass-finals: ^1.9
- driftingly/rector-laravel: ^2.3
- larastan/larastan: ^3.9.3
- laravel/pint: ^1.29
- mockery/mockery: ^1.6
- mrpunyapal/rector-pest: ^0.2.7
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0||^10.0||^11.0
- pestphp/pest: ^3.0||^4.0
- pestphp/pest-plugin-arch: ^3.0||^4.0
- pestphp/pest-plugin-laravel: ^3.0||^4.0
- phpstan/extension-installer: ^1.4.3
- phpstan/phpstan-deprecation-rules: ^2.0.4
- phpstan/phpstan-phpunit: ^2.0.16
- phpstan/phpstan-strict-rules: ^2.0.10
- rector/rector: ^2.4.1
- rector/type-perfect: ^2.1.2
- sandermuller/package-boost: ^0.15.0
- spaze/phpstan-disallowed-calls: ^4.10
- symplify/phpstan-extensions: ^12.0.2
- tomasvotruba/cognitive-complexity: ^1.1
- tomasvotruba/type-coverage: ^2.1
README
Laravel wrapper around sandermuller/solana-php-sdk — service
provider, facades, env-driven config, and artisan commands so you can
call Solana RPC from a Laravel app without wiring containers yourself.
use SanderMuller\LaravelSolanaSdk\Facades\Solana; $balance = Solana::getBalance('SomeWalletAddressBase58'); // lamports as float $blockhash = Solana::latestBlockhash(); // typed BlockhashInfo $status = Solana::sendAndConfirmTransaction($tx, [$payer]);
Contents
- Requirements
- Install
- Configuration
- Facades
- Multi-endpoint transport
- Confirming transactions on the queue
- PubSub / WebSocket
- Artisan commands
- Testing
- Upgrading
- Changelog
- Contributing
- Security
- Credits
- License
Requirements
- PHP
^8.3 - Laravel
^11.0 || ^12.0 || ^13.0 ext-sodium(transitively required by the core SDK)
Install
composer require sandermuller/laravel-solana-sdk
The service provider auto-discovers. Publish the config if you want to customise defaults:
php artisan vendor:publish --tag=solana-sdk-config
Configuration
config/solana-sdk.php exposes four knobs:
return [ 'network' => env('SOLANA_NETWORK', 'mainnet'), 'token_program_id' => env('SOLANA_TOKEN_PROGRAM_ID', 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'), 'transport' => [ 'mode' => env('SOLANA_TRANSPORT_MODE', 'fallback'), 'urls' => array_values(array_filter([ env('SOLANA_RPC_URL'), env('SOLANA_RPC_URL_FALLBACK'), ])), 'headers' => [], 'timeout' => (float) env('SOLANA_RPC_TIMEOUT', 30.0), 'retry' => [ 'max_attempts' => (int) env('SOLANA_RPC_RETRY_ATTEMPTS', 3), 'base_delay_ms' => (int) env('SOLANA_RPC_RETRY_BASE_MS', 100), 'max_delay_ms' => (int) env('SOLANA_RPC_RETRY_MAX_MS', 2_000), ], ], 'commands' => [ 'enabled' => env('SOLANA_COMMANDS_ENABLED', true), ], ];
SOLANA_NETWORK accepts mainnet, mainnet-beta, testnet, devnet
(also main, test, dev). The RPC endpoint is derived from the
Network enum in the core SDK.
Facades
use SanderMuller\LaravelSolanaSdk\Facades\Solana; use SanderMuller\LaravelSolanaSdk\Facades\SolanaRpc; $balance = Solana::getBalance('SomeWalletAddressBase58'); $info = Solana::accountInfo('SomeWalletAddressBase58'); // typed AccountInfo $blockhash = Solana::latestBlockhash(); // typed BlockhashInfo // Send + poll until confirmed in a single call: $status = Solana::sendAndConfirmTransaction($tx, [$payer]); // Low-level JSON-RPC escape hatch $result = SolanaRpc::call('getSlot');
Solana proxies the typed Connection API (~60 RPC methods covering
~80 % of the Solana JSON-RPC spec — accounts, blocks, slots, signatures,
tokens, supply, stake, vote, inflation). SolanaRpc proxies the raw
SolanaRpcClient for calls the typed facade does not yet cover.
Both bindings are also reachable via plain DI:
use SanderMuller\SolanaPhpSdk\Connection; class WalletController { public function show(Connection $solana, string $address): array { return ['balance' => $solana->getBalance($address)]; } }
Programs, builders, signers
The core SDK ships first-class program builders (SystemProgram,
SplTokenProgram, Token2022Program, MemoProgram, StakeProgram,
VoteProgram, AddressLookupTableProgram, ComputeBudgetProgram,
MetaplexProgram, AnchorIdl, …), a sanitize-safe
TransactionBuilder, a Util\PriorityFee helper, and a
Contracts\MessageSigner interface (with Signing\InMemoryMessageSigner
as the local adapter). Use them directly — no wrapping required:
use SanderMuller\SolanaPhpSdk\TransactionBuilder; use SanderMuller\SolanaPhpSdk\Programs\SystemProgram; use SanderMuller\LaravelSolanaSdk\Facades\Solana; $blockhash = Solana::latestBlockhash(); $tx = TransactionBuilder::make() ->feePayer($payer->publicKey) ->recentBlockhash($blockhash) ->addInstruction(SystemProgram::transfer($payer->publicKey, $to, $lamports)) ->addSigner($payer) ->build(); $status = Solana::sendAndConfirmTransaction($tx, [$payer]);
Multi-endpoint transport
Set SOLANA_RPC_URL (and optionally SOLANA_RPC_URL_FALLBACK) to route
through your own RPC provider with automatic retry + fallback. Leave
both unset to keep the public-endpoint default.
SOLANA_RPC_URL=https://mainnet.helius-rpc.com/?api-key=… SOLANA_RPC_URL_FALLBACK=https://api.mainnet-beta.solana.com SOLANA_TRANSPORT_MODE=fallback # or round_robin SOLANA_RPC_TIMEOUT=30 SOLANA_RPC_RETRY_ATTEMPTS=3
For more elaborate setups (custom auth headers, multiple fallbacks)
publish the config and edit transport directly — the wrapper hands
the array straight to SanderMuller\SolanaPhpSdk\Rpc\TransportFactory.
Confirming transactions on the queue
The core SDK ships SanderMuller\SolanaPhpSdk\Queue\ConfirmTransactionJob
out of the box. Dispatch it after sendTransaction so the long-tail
confirmation phase becomes a background job that fires
TransactionConfirmed / TransactionExpired events:
use SanderMuller\LaravelSolanaSdk\Facades\Solana; use SanderMuller\SolanaPhpSdk\Queue\ConfirmTransactionJob; $blockhash = Solana::latestBlockhash(); $signature = Solana::sendTransaction($tx, [$payer]); ConfirmTransactionJob::dispatch( signature: $signature, lastValidBlockHeight: $blockhash->lastValidBlockHeight, context: ['order_id' => $order->id], );
Listen for SanderMuller\SolanaPhpSdk\Events\TransactionConfirmed and
TransactionExpired in EventServiceProvider.
PubSub / WebSocket
SolanaPubSubClient is bound transient against the configured network,
so you can typehint it directly:
use SanderMuller\SolanaPhpSdk\Services\SolanaPubSubClient; class WatchSignatures { public function handle(SolanaPubSubClient $pubsub, string $signature): void { $pubsub->enableAutoReconnect(); $pubsub->signatureSubscribe($signature); foreach ($pubsub->listen() as $event) { // … } } }
Artisan commands
| Command | Purpose |
|---|---|
solana:balance {address} |
Print SOL balance + lamports |
solana:airdrop {address} {sol=1} |
Request devnet/testnet airdrop |
solana:account {address} |
Dump raw account info JSON |
solana:tx {signature} |
Look up a transaction by signature |
solana:health |
RPC health + version |
solana:tokens {owner} |
List SPL token accounts owned by an address |
solana:fees {addresses?*} |
Recent prioritization fee samples |
Disable all bundled commands with SOLANA_COMMANDS_ENABLED=false (they
hit live RPC; production may want them off).
Testing
composer test
Stubbing RPC in tests
Call Solana::fake() to swap the bound SolanaRpcClient for the core
SDK's InMemoryRpcStub so the SDK never hits the network:
use SanderMuller\LaravelSolanaSdk\Facades\Solana; it('reads the balance', function (): void { Solana::fake()->script([ 'getBalance' => ['value' => 5_000_000], ]); expect(Solana::getBalance($address))->toBe(5_000_000.0); expect(Solana::fakedStub()?->methodCalls())->toContain('getBalance'); });
Wire the core Pest expectations (toBeConfirmed, toHaveCustomCode,
toBeInstructionError) once from tests/Pest.php:
use SanderMuller\SolanaPhpSdk\Testing\PestExpectations; PestExpectations::register();
The wrapper's own test suite runs against Orchestra Testbench with the package provider auto-registered. Network-dependent tests are not shipped — facade unit tests just verify resolution + container shape.
Upgrading
See UPGRADING.md.
Changelog
See CHANGELOG.md. Updated automatically on release publish.
Contributing
PRs welcome. Run the local gauntlet before opening one:
vendor/bin/pest # tests vendor/bin/pint --test # style vendor/bin/phpstan # static analysis vendor/bin/rector --dry-run
The package is intentionally a thin wrapper — net-new RPC methods,
program builders, and DTOs belong in
sandermuller/solana-php-sdk. The wrapper only adds Laravel
glue (@method lines on the Solana facade, env-driven config keys,
container bindings, solana:* commands). See
CLAUDE.md for the full scope rules.
Security
See SECURITY.md.
Credits
License
The MIT License (MIT). See LICENSE.