Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Solana Php Sdk Laravel Package

tightenco/solana-php-sdk

A PHP SDK for building on Solana. Create and manage keypairs, build, sign, and send transactions, query accounts and program data, and interact with on-chain programs through a clean, framework-friendly API that fits naturally into Laravel and plain PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Composer install (pinned). This is a plain Composer library — not a Laravel package — so there is no service provider to discover. Pin the exact version; pre-1.0 caret ranges (^0.3) will silently upgrade you across breaking changes.

composer require tightenco/solana-php-sdk:0.3.2

Check the environment before writing code. Two prerequisites fail late and loudly only at runtime:

  • ext-sodium — required for ed25519 keypair generation and loading. It fails at keypair construction, not at composer install. Verify it in local, CI, queue workers, and deploy targets.
  • A PSR-18 HTTP client (Guzzle is the usual choice) — the RPC client needs one to reach Solana.
php -m | grep sodium   # must print "sodium"

Where to look first. In order:

  1. SolanaRpcClient — the JSON-RPC wrapper. Its call() method is the escape hatch for any RPC method the SDK doesn't wrap explicitly, and (as of v0.3.2) it accepts custom headers.
  2. Keypair / public-key utilities — loading a signer from a secret, deriving an address.
  3. Transaction building/signing/sending helpers — the write path.

First use case: devnet, read-only. Start with getBalance or getAccountInfo against devnet. Reads are reversible, require no signing, no custody story, and prove the wiring (endpoint, HTTP client, sodium) end to end.

use Tightenco\SolanaPhpSdk\Connection; // confirm exact class in vendor/ after install

$connection = new Connection('https://api.devnet.solana.com');

$balance = $connection->call('getBalance', [
    '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
]);

Bind that client as a singleton in a service provider and pull the endpoint from config/services.php / .env. Do not instantiate it per request, and do not hardcode URLs or API keys at call sites.

Only after a read round-trip works should you attempt a devnet SOL transfer with a throwaway keypair.

Implementation Patterns

Wrap the SDK behind an internal interface. Roughly a day of work; it converts an upstream bump from an incident into a one-file edit.

interface SolanaGateway
{
    public function balance(string $address): int;
    public function ownsToken(string $wallet, string $mint, int $min = 1): bool;
    public function transferSol(string $from, string $to, int $lamports): string; // signature
}

Everything else in the app — controllers, jobs, Livewire components — talks to SolanaGateway, never to the SDK classes.

Configuration shape. Externalize endpoint, commitment level, and provider auth headers:

// config/services.php
'solana' => [
    'endpoint' => env('SOLANA_ENDPOINT'),           // e.g. Helius/QuickNode URL
    'commitment' => env('SOLANA_COMMITMENT', 'confirmed'),
    'headers' => [
        'Authorization' => 'Bearer ' . env('SOLANA_RPC_TOKEN'),
    ],
],

The headers array is the direct beneficiary of v0.3.2's call() header support — it makes authenticated commercial RPC providers (Helius, QuickNode, Triton) and internal egress proxies a config change rather than a fork. Rotate tokens here; never at call sites.

Read pattern — token/NFT ownership gate. This is the highest-value day-one workflow: "prove you hold X."

public function ownsToken(string $wallet, string $mint, int $min = 1): bool
{
    $res = $this->rpc->call('getTokenAccountsByOwner', [
        $wallet,
        ['mint' => $mint],
        ['encoding' => 'jsonParsed', 'commitment' => config('services.solana.commitment')],
    ]);

    $held = collect($res['result']['value'] ?? [])
        ->sum(fn ($acct) => (int) ($acct['account']['data']['parsed']['info']['tokenAmount']['amount'] ?? 0));

    return $held >= $min;
}

Return a boolean to the domain layer and cache it briefly — these are hot on page loads.

Write pattern — build, sign with ALL signers at once, submit, then poll. v0.3.2 corrected the signing path so every signer must be supplied in a single call. Build your transfer, collect signers into one array, sign once.

$tx = /* build transfer instruction(s) via SDK helpers */;
$tx->sign($payerKeypair, ...$additionalSigners);   // all at once, every time
$signature = $connection->sendTransaction($tx, ['encoding' => 'base64']);

Treat confirmation as your job. The SDK submits; it does not reliably wait for finality. Poll getSignatureStatuses with a bounded retry/backoff and handle the three failure modes explicitly: expired blockhash, dropped transaction, insufficient fee.

Route every write through a queued job. Controllers should enqueue and return a pending state; the job performs build→sign→send→confirm and records the outcome. This gives you Laravel's retry/backoff, keeps long RPC round-trips off the request, and centralizes signing in one spot. It is also the seam where a future KMS/HSM signer drops in.

Load keypairs from a single service. One KeypairProvider that reads from .env (devnet) or a secret store, returns a Keypair, and is the only code that touches raw secret bytes. Every signing path resolves its signer there. This is not custody — it's the boundary you'll replace when custody becomes real.

Testing. Stand up solana-test-validator in CI and point the gateway at http://localhost:8899. Assert on-chain outcomes, not just "no exception thrown" — a transaction can fail and still return cleanly from sendTransaction.

Gotchas and Tips

  • ext-sodium failures appear at runtime, not install. A green composer install says nothing. Add a startup assertion or a build-time check in every image including queue workers.
  • Pin exact, never caret. At 0.3.x SemVer is not a contract. Add a changelog-review gate to your dependency-bump runbook, with special attention to signature and transaction-construction changes — that's where v0.3.2's fix landed.
  • All signers at once (v0.3.2). If you wrote code against v0.3.1 partial-signing behavior, audit it now. A missing signer does not throw — it produces a transaction that fails on-chain.
  • Silent failures are the default failure mode. Expired blockhash, wrong commitment, fork rollback, duplicate submission, wrong fee payer — these surface as on-chain errors, not PHP exceptions. Catch them by polling signature status, not by wrapping sendTransaction in try/catch.
  • Commitment level matters and should be explicit. processed is fast and unreliable; confirmed is the sane default; finalized is slow. Set it once in config and pass it on every call that accepts it.
  • Header support is the production unblocker. If you're still on v0.3.1 and forking to add provider auth, upgrade instead — v0.3.2 removes the fork.
  • Not a Laravel package. No auto-discovered service provider, no published config. You write the binding yourself — budget for it.
  • Not a custody solution. This signs with raw private keys in-process. Hot keys in .env are fine for devnet and unacceptable for a production treasury. Do not pitch this to stakeholders as "included."
  • Thin on program composition. No Anchor IDL / typed instruction support. If the roadmap needs CPI-heavy custom program calls, the TypeScript tooling is materially ahead — that's the boundary where a small Node sidecar starts earning its keep.
  • Debugging tip. Log the outbound RPC payload and the raw response JSON at debug level behind a flag. When a call misbehaves, the difference between an SDK bug and a bad parameter is usually visible in the raw body, not the exception.
  • The wrapper pays for itself. Every upstream bump that touches signing should hit exactly one file in your codebase. If it hits more, the abstraction leaked.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route