| name | stellar-php-sdk |
| description | Build Stellar blockchain applications in PHP using soneso/stellar-php-sdk. Use when generating PHP code for transaction building, signing, Horizon API queries, Soroban RPC, smart contract deployment and invocation, XDR encoding/decoding, and SEP protocol integration. Covers all 26 operations, 50 Horizon endpoints, 12 RPC methods, and 20 SEP implementations with synchronous Guzzle HTTP patterns. |
| license | Apache 2.0 |
| compatibility | Requires PHP 8.0+, ext-bcmath, ext-gmp, and Composer |
| metadata | {"version":"1.0.3","sdk_version":"1.9.7","last_updated":"2026-05-06"} |
Stellar SDK for PHP
Overview
The soneso/stellar-php-sdk is a PHP 8.0+ library for the Stellar blockchain network. It provides 100% Horizon API coverage (50/50 endpoints), 100% Soroban RPC coverage (12/12 methods), 30/30 streaming endpoints, and 20 SEP implementations. All HTTP operations are synchronous using Guzzle 7. The root namespace is Soneso\StellarSDK.
Installation
composer require soneso/stellar-php-sdk
All code examples below assume <?php declare(strict_types=1); and relevant use imports.
If you can't find a constructor or method signature in this file or the topic references, grep references/api_reference.md — it has all public class/method signatures.
1. Stellar Basics
Fundamental Stellar concepts and SDK patterns.
Keys and KeyPairs
use Soneso\StellarSDK\Crypto\KeyPair;
$keyPair = KeyPair::random();
$accountId = $keyPair->getAccountId();
$secretSeed = $keyPair->getSecretSeed();
$keyPair = KeyPair::fromSeed(getenv('STELLAR_SECRET_SEED'));
$publicOnly = KeyPair::fromAccountId($accountId);
Accounts
use Soneso\StellarSDK\Crypto\KeyPair;
use Soneso\StellarSDK\StellarSDK;
use Soneso\StellarSDK\Util\FriendBot;
$keyPair = KeyPair::random();
$accountId = $keyPair->getAccountId();
FriendBot::fundTestAccount($accountId);
$sdk = StellarSDK::getTestNetInstance();
$account = $sdk->requestAccount($accountId);
echo 'Sequence: ' . $account->getSequenceNumber() . PHP_EOL;
echo 'Subentry Count: ' . $account->getSubentryCount() . PHP_EOL;
foreach ($account->getBalances() as $balance) {
if ($balance->getAssetType() === 'native') {
echo 'XLM: ' . $balance->getBalance() . PHP_EOL;
} else {
echo $balance->getAssetCode() . ': ' . $balance->getBalance() . PHP_EOL;
}
}
$exists = $sdk->accountExists('GDEF...');
Assets
use Soneso\StellarSDK\Asset;
$xlm = Asset::native();
$usdc = Asset::createNonNativeAsset('USDC', 'GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN');
$asset = Asset::createFromCanonicalForm('USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN');
Networks
use Soneso\StellarSDK\Network;
use Soneso\StellarSDK\StellarSDK;
$network = Network::testnet();
$sdk = StellarSDK::getTestNetInstance();
$network = new Network('My Custom Network ; Passphrase');
$sdk = new StellarSDK('https://my-horizon.example.com');
2. Horizon API - Fetching Data
Query patterns for retrieving blockchain data. All calls are synchronous.
Query Accounts
$sdk = StellarSDK::getTestNetInstance();
$response = $sdk->accounts()
->forSigner('GABC...')
->limit(10)
->order('desc')
->execute();
foreach ($response->getAccounts() as $acct) {
echo $acct->getAccountId() . PHP_EOL;
}
For single account queries, see $sdk->requestAccount() in Accounts above.
Query Transactions
$sdk = StellarSDK::getTestNetInstance();
$response = $sdk->transactions()
->forAccount('GABC...')
->limit(5)
->order('desc')
->execute();
foreach ($response->getTransactions() as $tx) {
echo $tx->getHash() . ' ledger=' . $tx->getLedger() . PHP_EOL;
}
$arr = $response->getTransactions()->toArray();
$nextPage = $sdk->transactions()->forAccount('GABC...')
->cursor(end($arr)->getPagingToken())->limit(5)->order('desc')->execute();
$tx = $sdk->requestTransaction('abc123def...');
For all Horizon endpoints, advanced queries, and pagination patterns:
Horizon API Reference
3. Horizon API - Streaming
Real-time update patterns using Server-Sent Events. Streaming runs in a blocking while(true) loop.
Stream Payments
use Soneso\StellarSDK\Responses\Operations\PaymentOperationResponse;
$sdk->payments()
->forAccount($accountId)
->cursor('now')
->stream(function (PaymentOperationResponse $payment) {
echo $payment->getFrom() . ' -> ' . $payment->getTo() . ': ' . $payment->getAmount() . PHP_EOL;
});
The same pattern applies to all streamable resources (transactions(), ledgers(), operations(), etc.). Auto-reconnects on server errors with a 10-second delay. Streams block indefinitely — use pcntl_fork() to run a stream alongside other operations (see streaming guide).
For reconnection patterns and all 30 streaming endpoints:
Horizon Streaming Guide
4. Transactions & Operations
Complete transaction lifecycle: Build -> Sign -> Submit.
Transaction Lifecycle
use Soneso\StellarSDK\Crypto\KeyPair;
use Soneso\StellarSDK\TransactionBuilder;
use Soneso\StellarSDK\PaymentOperationBuilder;
use Soneso\StellarSDK\Asset;
use Soneso\StellarSDK\Memo;
$sdk = StellarSDK::getTestNetInstance();
$network = Network::testnet();
$sourceKeyPair = KeyPair::fromSeed(getenv('STELLAR_SECRET_SEED'));
$sourceId = $sourceKeyPair->getAccountId();
$sourceAccount = $sdk->requestAccount($sourceId);
$paymentOp = (new PaymentOperationBuilder(
'GDEST...',
Asset::native(),
'100.50'
))->build();
$transaction = (new TransactionBuilder($sourceAccount))
->addOperation($paymentOp)
->addMemo(Memo::text('Payment for services'))
->setMaxOperationFee(200)
->build();
$transaction->sign($sourceKeyPair, $network);
$response = $sdk->submitTransaction($transaction);
if ($response->isSuccessful()) {
echo 'Success! Hash: ' . $response->getHash() . PHP_EOL;
} else {
$codes = $response->getExtras()->getResultCodes();
echo 'Failed: ' . $codes->getTransactionResultCode() . PHP_EOL;
echo 'Op codes: ' . json_encode($codes->getOperationsResultCodes()) . PHP_EOL;
}
Common Operations
Change Trust (Establish Trustline):
use Soneso\StellarSDK\ChangeTrustOperationBuilder;
$asset = Asset::createNonNativeAsset('USDC', 'GISSUER...');
$trustOp = (new ChangeTrustOperationBuilder($asset))->build();
Manage Sell Offer (DEX):
use Soneso\StellarSDK\ManageSellOfferOperationBuilder;
$selling = Asset::createNonNativeAsset('USDC', 'GISSUER...');
$buying = Asset::native();
$offerOp = (new ManageSellOfferOperationBuilder(
$selling, $buying, '100.00', '0.50'
))->build();
$offers = $sdk->offers()->forAccount($accountId)->execute()->getOffers();
$offerId = $offers->toArray()[0]->getOfferId();
$updateOp = (new ManageSellOfferOperationBuilder($selling, $buying, '200.00', '0.55'))
->setOfferId($offerId)->build();
For all 26 operations with parameters and examples:
Operations Reference
5. Soroban RPC API
RPC endpoint patterns for Soroban smart contract queries.
use Soneso\StellarSDK\Soroban\SorobanServer;
$server = new SorobanServer('https://soroban-testnet.stellar.org');
$health = $server->getHealth();
For all 12 RPC methods including event queries and transaction simulation:
RPC Reference
6. Smart Contracts
Contract deployment and invocation patterns using the high-level SorobanClient.
Deploy Contract
use Soneso\StellarSDK\Crypto\KeyPair;
use Soneso\StellarSDK\Network;
use Soneso\StellarSDK\Soroban\Contract\SorobanClient;
use Soneso\StellarSDK\Soroban\Contract\InstallRequest;
use Soneso\StellarSDK\Soroban\Contract\DeployRequest;
use Soneso\StellarSDK\Xdr\XdrSCVal;
$keyPair = KeyPair::fromSeed(getenv('STELLAR_SECRET_SEED'));
$rpcUrl = 'https://soroban-testnet.stellar.org';
$network = Network::testnet();
$wasmBytes = file_get_contents('/path/to/contract.wasm');
$wasmHash = SorobanClient::install(new InstallRequest(
sourceAccountKeyPair: $keyPair,
wasmBytes: $wasmBytes,
network: $network,
rpcUrl: $rpcUrl,
));
$client = SorobanClient::deploy(new DeployRequest(
sourceAccountKeyPair: $keyPair,
wasmHash: $wasmHash,
network: $network,
rpcUrl: $rpcUrl,
constructorArgs: [XdrSCVal::forSymbol('init')], // optional constructor args
));
echo 'Contract ID: ' . $client->getContractId() . PHP_EOL;
Invoke Contract Function
use Soneso\StellarSDK\Soroban\Contract\ClientOptions;
use Soneso\StellarSDK\Soroban\Contract\MethodOptions;
$client = SorobanClient::forClientOptions(new ClientOptions(
sourceAccountKeyPair: $keyPair,
contractId: 'CABC...',
network: $network,
rpcUrl: $rpcUrl,
));
$result = $client->invokeMethod('get_count');
echo 'Count: ' . $result->u32 . PHP_EOL;
$result = $client->invokeMethod('increment', [XdrSCVal::forU32(5)]);
$result = $client->invokeMethod('expensive_operation', [XdrSCVal::forSymbol('data')],
methodOptions: new MethodOptions(fee: 10000, timeoutInSeconds: 60));
For contract authorization, multi-auth workflows, and remote signing:
Smart Contracts Guide
7. XDR Encoding & Decoding
XDR (External Data Representation) is Stellar's binary serialization format.
Transaction XDR Roundtrip
use Soneso\StellarSDK\AbstractTransaction;
$xdrBase64 = $transaction->toEnvelopeXdrBase64();
$decoded = AbstractTransaction::fromEnvelopeBase64XdrString($xdrBase64);
echo 'Source: ' . $decoded->getSourceAccount()->getAccountId() . PHP_EOL;
Working with Soroban XDR Values (XdrSCVal)
use Soneso\StellarSDK\Xdr\XdrSCVal;
use Soneso\StellarSDK\Xdr\XdrSCAddress;
use Soneso\StellarSDK\Xdr\XdrSCMapEntry;
use Soneso\StellarSDK\Xdr\XdrUInt128Parts;
$symVal = XdrSCVal::forSymbol('transfer');
$addrVal = XdrSCVal::forAddress(XdrSCAddress::forAccountId('GABC...'));
$u128Val = XdrSCVal::forU128(new XdrUInt128Parts(hi: 0, lo: 1000000));
$vecVal = XdrSCVal::forVec([XdrSCVal::forU32(1), XdrSCVal::forU32(2)]);
$mapVal = XdrSCVal::forMap([new XdrSCMapEntry($symVal, XdrSCVal::forU32(42))]);
$base64 = $symVal->toBase64Xdr();
$decoded = XdrSCVal::fromBase64Xdr($base64);
To submit a pre-signed XDR envelope: $sdk->submitTransactionEnvelopeXdrBase64($signedXdrBase64).
For all XdrSCVal factory methods and type mapping:
XDR Reference | Contract Arguments
8. Error Handling & Troubleshooting
Horizon Errors
use Soneso\StellarSDK\Exceptions\HorizonRequestException;
try {
$account = $sdk->requestAccount('GINVALID...');
} catch (HorizonRequestException $e) {
$statusCode = $e->getStatusCode();
$retryAfter = $e->getRetryAfter();
$horizonError = $e->getHorizonErrorResponse();
if ($horizonError !== null) {
echo $horizonError->getTitle() . ': ' . $horizonError->getDetail() . PHP_EOL;
}
}
Transaction submission error handling (non-exception path) is shown in the Transaction Lifecycle example above.
Soroban RPC Errors
$txResponse = $server->getTransaction('abc123...');
if ($txResponse->error !== null) {
echo 'RPC error: ' . $txResponse->error->getMessage() . PHP_EOL;
}
match ($txResponse->status) {
'SUCCESS' => ,
'FAILED' => ,
'NOT_FOUND' => ,
};
For comprehensive error catalog, result codes, and retry patterns:
Troubleshooting Guide
9. Security Best Practices
Critical patterns: never hardcode secret seeds (use getenv() or vault), verify transaction details before signing, validate network passphrase, validate user inputs (account ID format, amount precision). Always use string amounts to avoid floating-point precision errors.
For complete security patterns including input validation, transaction verification, and key management:
Security Guide
10. SEP Implementations
The PHP SDK implements 20 Stellar Ecosystem Proposals (SEPs). Most commonly used: SEP-01 (Stellar TOML discovery), SEP-02 (Federation address resolution), SEP-05 (BIP-39 mnemonic key derivation), SEP-10 (Web Authentication for account ownership proof), SEP-24 (Interactive deposit/withdrawal flows), SEP-51 (XDR-JSON encoding for human-readable interchange of Stellar XDR types). All SEP classes are under the Soneso\StellarSDK\SEP\ namespace; SEP-51 lives under Soneso\StellarSDK\Xdr\ because it operates on XDR types directly.
For all SEP examples and the complete implementation table:
SEP Reference
11. Advanced Features
- Multi-signature accounts — add signers + set thresholds in a SINGLE transaction to avoid lockout → Multi-Sig Example
- Sponsored reserves — three-operation pattern (Begin → Create → End), both parties sign → Sponsorship Example
- Fee bump transactions —
FeeBumpTransactionBuilder → Fee Bump Example
- Liquidity pools — AMM with
AssetTypePoolShare → Pools Example
- Muxed accounts —
MuxedAccount with M... addresses → Muxed Example
- Async submission —
submitAsyncTransaction() → Async Example
Reference Documentation
Links to comprehensive reference guides:
Common Pitfalls
Stroop precision: Stellar amounts have 7 decimal places. Always use string amounts to avoid floating-point errors.
$amount = 100.1234567;
$amount = '100.1234567';
Sequence number staleness: TransactionBuilder->build() mutates the source account's sequence number. A good practice is to fetch the account immediately before building. Don't increment manually — build() handles it. Stale sequence numbers cause tx_bad_seq errors.
$account = $sdk->requestAccount($accountId);
$tx = (new TransactionBuilder($account))->addOperation($op)->build();
$sdk->submitTransaction($tx);
$account = $sdk->requestAccount($accountId);
$account->incrementSequenceNumber();
$tx = (new TransactionBuilder($account))->addOperation($op)->build();
Sequence numbers are BigInteger objects:
use phpseclib3\Math\BigInteger;
$seqNum = $account->getSequenceNumber() - 1;
$seqNum = $account->getSequenceNumber()->subtract(new BigInteger(1));
$account = new Account($account->getAccountId(), $seqNum);
Collection count — use method, not function:
count($account->getSigners());
$account->getSigners()->count();
Insufficient signatures return op_bad_auth, not tx_bad_auth: When a multi-sig transaction lacks enough signature weight, the transaction-level code is tx_failed and the auth failure is in the operation codes.
$txCode = $extras->getResultCodesTransaction();
if ($txCode === 'tx_bad_auth') { ... }
$opCodes = $extras->getResultCodesOperation();
Fee calculation: The fee is per operation. For a transaction with N operations at setMaxOperationFee(200), the total fee is N * 200 stroops. The minimum base fee is 100 stroops per operation (StellarConstants::MIN_BASE_FEE_STROOPS).