inodrahq / sui-sdk
The most complete PHP SDK for the Sui blockchain — 6 signature schemes, auto-resolving transactions, typed responses, JSON-RPC + gRPC
Requires
- php: >=8.2
- ext-bcmath: *
- ext-openssl: *
- ext-sodium: *
- bitwasp/bech32: ^0.0.1
- google/protobuf: ^5.0
- grpc/grpc: ^1.74
- guzzlehttp/guzzle: ^7.0
- inodrahq/bcs: ^1.0
Requires (Dev)
- phpstan/phpstan: ^2
- phpunit/phpunit: ^11
Suggests
- ext-grpc: Required for native gRPC transport (gRPC-Web works without it)
README
A comprehensive PHP SDK for the Sui blockchain.
- 6 signature schemes -- Ed25519, Secp256k1, Secp256r1, Multisig, zkLogin, Passkey
- Auto-resolving Transaction builder -- gas price, budget, and coin selection handled automatically
- 40+ RPC methods with typed response objects
- Dual transport -- gPRC and JSON-RPC with automatic fallback
- BIP-39 / BIP-32 key derivation with mnemonic generation
- Convenience methods -- transfer, stake, merge, send in one call
Requirements
- PHP 8.2+
- ext-sodium
- ext-bcmath
- ext-openssl
- ext-grpc (optional, for native gRPC transport -- gRPC-Web works without it)
Installation
composer require inodrahq/sui-sdk
Quick Start
use Inodrahq\SuiSdk\Wallet; use Inodrahq\SuiSdk\SuiClient; // Create a wallet $wallet = new Wallet(); // random keypair echo $wallet->getAddress(); // 0x... // Or import from bech32 private key $wallet = new Wallet('suiprivkey1...'); // Or from a mnemonic $wallet = Wallet::fromMnemonic('abandon abandon abandon ...'); // Connect to testnet (JSON-RPC) $client = SuiClient::forNetwork('testnet'); // Connect to testnet (gRPC -- requires ext-grpc) $client = SuiClient::forNetworkGrpc('testnet'); // Check balance $balance = $client->getBalance($wallet->getAddress()); echo $balance->totalBalance; // in MIST
Features
Auto-Resolving Transaction Builder
The Transaction class automatically resolves gas price, gas budget (via dry-run), and gas coin selection. No manual gas configuration needed.
use Inodrahq\SuiSdk\Transaction; $tx = new Transaction($client); $tx->setSender($wallet->getAddress()); // Gas price, budget, and coins are resolved automatically $coin = $tx->splitCoins($tx->gas(), [$tx->pure('u64', '1000000000')]); $tx->transferObjects([$coin], $tx->pure('address', $recipient)); // Build, sign, and execute in one call $result = $tx->execute($wallet); echo $result->digest;
Objects are also auto-resolved from the network:
// Automatically fetches object data and determines shared vs owned $obj = $tx->object('0x5'); $tx->moveCall('0x3::sui_system::request_add_stake', [], [ $obj, $coin, $tx->pure('address', $validatorAddress), ]);
You can still override any gas parameter manually:
$tx->setGasPrice(1000); $tx->setGasBudget(10_000_000); $tx->setGasPayment($objectRefs);
Signature Schemes
The SDK supports all six Sui signature schemes.
use Inodrahq\SuiSdk\Wallet; // Ed25519 (default) $wallet = new Wallet('suiprivkey1...'); // Secp256k1 -- auto-detected from key encoding $wallet = new Wallet('suiprivkey1...'); // Secp256r1 -- auto-detected from key encoding $wallet = new Wallet('suiprivkey1...'); // All wallets share the same API $wallet->getAddress(); // 0x-prefixed Sui address $wallet->sign($txData); // returns base64 signature $wallet->signPersonalMessage('Hello, Sui!');
Multisig:
use Inodrahq\SuiSdk\Crypto\MultiSig\MultiSigPublicKey; $msig = new MultiSigPublicKey([$pk1, $pk2, $pk3], [1, 1, 1], 2); $address = $msig->toSuiAddress(); $combinedSig = $msig->combineSignatures([$sig1, $sig2]);
zkLogin:
use Inodrahq\SuiSdk\Crypto\ZkLogin\ZkLoginAddress; $address = ZkLoginAddress::fromJwtClaims($iss, $aud, $sub, $salt);
Passkey:
use Inodrahq\SuiSdk\Crypto\Passkey\PasskeyAuthenticator;
Typed Responses
All RPC methods return typed objects with IDE autocomplete support.
$balance = $client->getBalance($address); echo $balance->totalBalance; // string echo $balance->coinObjectCount; // int echo $balance->coinType; // string $coins = $client->getCoins($address); foreach ($coins->data as $coin) { echo $coin->coinObjectId; // IDE autocomplete works echo $coin->balance; } $tx = $client->getTransactionBlock($digest); echo $tx->effects->status->status; // "success" echo $tx->effects->gasUsed->computationCost;
Wallet Management
use Inodrahq\SuiSdk\Wallet; // Generate with mnemonic (BIP-39) $result = Wallet::generateWithMnemonic(); $wallet = $result['wallet']; $mnemonic = $result['mnemonic']; // 12 words, save this! // 24-word mnemonic $result = Wallet::generateWithMnemonic(256); // Import from mnemonic $wallet = Wallet::fromMnemonic('raccoon chalk symbol ...'); // Import from bech32 private key (SIP-15) $wallet = new Wallet('suiprivkey1...'); // Access keys $wallet->getAddress(); // 0x-prefixed hex $wallet->getPublicKey(); // hex $wallet->getPrivateKey(); // bech32 $wallet->getPrivateKeyHex(); // 0x-prefixed hex
Network Presets
use Inodrahq\SuiSdk\SuiClient; use Inodrahq\SuiSdk\Network; // JSON-RPC (default) $client = SuiClient::forNetwork('mainnet'); $client = SuiClient::forNetwork('testnet'); $client = SuiClient::forNetwork('devnet'); $client = SuiClient::forNetwork('localnet'); // gRPC (requires ext-grpc) $client = SuiClient::forNetworkGrpc('mainnet'); $client = SuiClient::forNetworkGrpc('testnet'); $client = SuiClient::forNetworkGrpc('devnet'); // With custom headers (e.g., API key) $client = SuiClient::forNetwork('mainnet', [ 'x-api-key' => 'your-api-key', ]); // Network URLs Network::getRpcUrl('mainnet'); // https://fullnode.mainnet.sui.io:443 Network::getFaucetUrl('testnet'); // https://faucet.testnet.sui.io/v2/gas Network::getExplorerUrl('mainnet'); // https://suivision.xyz/txblock/
Convenience Methods
High-level methods that handle coin selection, gas, and transaction building automatically:
// Transfer SUI (handles coin selection + gas automatically) $result = $client->transferSui($wallet, $recipientAddress, '1000000000'); // 1 SUI // Transfer any owned object $result = $client->transferObject($wallet, $objectId, $recipientAddress); // Merge all SUI coins into one $result = $client->mergeAllCoins($wallet); // Send non-SUI coins (USDC, etc.) $result = $client->sendCoin($wallet, '0x...::usdc::USDC', $recipientAddress, '1000000'); // Stake SUI with a validator (minimum 1 SUI = 1_000_000_000 MIST) $result = $client->stake($wallet, $validatorAddress, '1000000000'); // Unstake (withdraw) a StakedSui object $result = $client->unstake($wallet, $stakedSuiObjectId);
Transaction Builder (Low-Level)
Build complex Programmable Transaction Blocks (PTBs) manually:
use Inodrahq\SuiSdk\TransactionBuilder; use Inodrahq\SuiSdk\Bcs\Argument; $builder = new TransactionBuilder(); $builder->setSender($wallet->getAddress()); // Split coins $amount = $builder->addPureU64(1_000_000_000); $coin = $builder->splitCoins(Argument::gasCoin(), [$amount]); // Transfer $dest = $builder->addPureAddress($recipientAddress); $builder->transferObjects([$coin], $dest); // Set gas $builder->setGasBudget(10_000_000); // Sign and execute $tx = $builder->build(); $result = $client->signAndExecuteTransaction($tx, $wallet);
Available Commands
// SplitCoins -- split a coin into smaller coins $result = $builder->splitCoins($coin, [$amount1, $amount2]); // TransferObjects -- transfer objects to a destination $builder->transferObjects([$obj1, $obj2], $destination); // MergeCoins -- merge source coins into a destination coin $builder->mergeCoins($destinationCoin, [$source1, $source2]); // MoveCall -- call a Move function $result = $builder->moveCall($package, $module, $function, $typeArgs, $args); // Publish -- publish a Move package $upgradeCap = $builder->publish($moduleBytes, $dependencies); // Upgrade -- upgrade a Move package $result = $builder->upgrade($moduleBytes, $dependencies, $package, $ticket); // MakeMoveVec -- create a vector of Move values $vec = $builder->makeMoveVec($typeTag, [$elem1, $elem2]);
Pure Input Helpers
$builder->addPureU64(1000); $builder->addPureU8(42); $builder->addPureBool(true); $builder->addPureString('hello'); $builder->addPureBytes("\x01\x02\x03"); $builder->addPureAddress('0x...');
Object Inputs
use Inodrahq\SuiSdk\Bcs\ObjectArg; use Inodrahq\SuiSdk\Bcs\SuiAddress; use Inodrahq\SuiSdk\Bcs\U64; // Owned or immutable object $arg = $builder->addObjectInput(ObjectArg::immOrOwnedObject($objectRef)); // Shared object (e.g., SuiSystemState at 0x5) $arg = $builder->addObjectInput(ObjectArg::sharedObject( new SuiAddress('0x5'), // object ID new U64(1), // initial shared version true, // mutable )); // Receiving object $arg = $builder->addObjectInput(ObjectArg::receiving($objectRef));
Full RPC API
All 40+ Sui RPC methods are supported:
// Coin queries $client->getCoins($owner); $client->getAllCoins($owner); $client->getBalance($owner); $client->getAllBalances($owner); $client->getCoinMetadata($coinType); $client->getTotalSupply($coinType); // Objects $client->getObject($id, $options); $client->multiGetObjects($ids, $options); $client->getOwnedObjects($owner, $query, $cursor, $limit); $client->getDynamicFields($parentId); $client->getDynamicFieldObject($parentId, $name); // Move $client->getMoveFunctionArgTypes($package, $module, $function); $client->getNormalizedMoveModule($package, $module); $client->getNormalizedMoveFunction($package, $module, $function); $client->getNormalizedMoveStruct($package, $module, $struct); $client->getNormalizedMoveModulesByPackage($package); // Transactions $client->getTransactionBlock($digest, $options); $client->multiGetTransactionBlocks($digests, $options); $client->getTotalTransactionBlocks(); $client->dryRunTransactionBlock($txBytesBase64); $client->devInspectTransactionBlock($sender, $txBytesBase64, $gasPrice); $client->executeTransactionBlock($txBytes, $signatures, $options, $requestType); $client->signAndExecuteTransaction($txData, $wallet); $client->waitForTransaction($digest, $timeout); $client->queryTransactionBlocks($query, $cursor, $limit, $descendingOrder); $client->queryEvents($query, $cursor, $limit, $descendingOrder); // Governance $client->getReferenceGasPrice(); $client->getStakes($owner); $client->getStakesByIds($stakedSuiIds); $client->getLatestSuiSystemState(); $client->getValidatorsApy(); $client->getCommitteeInfo($epoch); // Chain $client->getChainIdentifier(); $client->getLatestCheckpointSequenceNumber(); $client->getCheckpoint($id); $client->getCheckpoints($cursor, $limit, $descendingOrder); $client->getRpcApiVersion(); $client->getProtocolConfig($version); // Name Service $client->resolveNameServiceAddress($name); $client->resolveNameServiceNames($address); // History $client->tryGetPastObject($objectId, $version, $options); $client->tryMultiGetPastObjects($pastObjects, $options);
Paginated Responses
use Inodrahq\SuiSdk\PaginatedResponse; // All paginated methods return PaginatedResponse $page = $client->getCoins($owner); echo $page->count(); // number of items echo $page->hasNextPage; // bool echo $page->nextCursor; // cursor for next page // Iterate through all pages foreach ($client->iterateCoins($owner) as $page) { foreach ($page->data as $coin) { echo $coin->coinObjectId . "\n"; } }
Signature Verification
use Inodrahq\SuiSdk\Crypto\Verifier; // Verify a transaction signature $isValid = Verifier::verifyTransactionSignature($signature, $txBytes); // Verify a personal message signature $isValid = Verifier::verifyPersonalMessage($signature, $message); // Extract info from a signature $publicKey = Verifier::extractPublicKey($signature); // hex $address = Verifier::extractAddress($signature); // 0x...
Transport Layer
The SDK supports pluggable transports. Both JSON-RPC and gRPC use the same SuiClient API -- all methods work identically regardless of transport.
JSON-RPC (default)
use Inodrahq\SuiSdk\SuiClient; use Inodrahq\SuiSdk\Transport\JsonRpcTransport; // Using network preset $client = SuiClient::forNetwork('mainnet'); // Custom endpoint with headers $client = new SuiClient(new JsonRpcTransport('https://fullnode.mainnet.sui.io:443', [ 'x-api-key' => 'your-key', ]));
gRPC
gRPC provides better performance with binary serialization and HTTP/2. Requires the ext-grpc PHP extension:
# macOS pecl install grpc # Ubuntu/Debian pecl install grpc echo "extension=grpc.so" > /etc/php/8.2/mods-available/grpc.ini phpenmod grpc # Verify php -m | grep grpc
use Inodrahq\SuiSdk\SuiClient; use Inodrahq\SuiSdk\Transport\GrpcTransport; // Using network preset $client = SuiClient::forNetworkGrpc('testnet'); // Custom endpoint $client = new SuiClient(new GrpcTransport('fullnode.mainnet.sui.io:443')); // All methods work the same as JSON-RPC $balance = $client->getBalance($address); $result = $client->transferSui($wallet, $recipient, '1000000000');
gRPC coverage: Most RPC methods have gRPC equivalents. The following methods are JSON-RPC only and will automatically fall back to JSON-RPC via the MethodRouter:
| Method | Reason |
|---|---|
getStakes |
No gRPC equivalent |
getStakesByIds |
No gRPC equivalent |
getLatestSuiSystemState |
No gRPC equivalent |
getValidatorsApy |
No gRPC equivalent |
getCommitteeInfo |
No gRPC equivalent |
queryTransactionBlocks |
No gRPC equivalent |
queryEvents |
No gRPC equivalent |
getCheckpoints |
No gRPC equivalent |
devInspectTransactionBlock |
No gRPC equivalent |
getRpcApiVersion |
JSON-RPC specific |
Examples
Deploy a Move Package
$moduleBytes = file_get_contents('build/MyPackage/bytecode_modules/my_module.mv'); $dep1 = new SuiAddress('0x1'); // MoveStdlib $dep2 = new SuiAddress('0x2'); // Sui Framework $builder = new TransactionBuilder(); $builder->setSender($wallet->getAddress()); $upgradeCap = $builder->publish([$moduleBytes], [$dep1, $dep2]); $sender = $builder->addPureAddress($wallet->getAddress()); $builder->transferObjects([$upgradeCap], $sender); $builder->setGasBudget(100_000_000); $result = $client->signAndExecuteTransaction($builder->build(), $wallet);
Stake SUI
// Using the auto-resolving Transaction builder $tx = new Transaction($client); $tx->setSender($wallet->getAddress()); $amount = $tx->pure('u64', '1000000000'); $coin = $tx->splitCoins($tx->gas(), [$amount]); $tx->moveCall('0x3::sui_system::request_add_stake', [], [ $tx->object('0x5'), // SuiSystemState (auto-resolved as shared) $coin, $tx->pure('address', $validatorAddress), ]); $result = $tx->execute($wallet);
Or use the convenience method:
$result = $client->stake($wallet, $validatorAddress, '1000000000');
Send USDC
$result = $client->sendCoin( $wallet, '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC', $recipientAddress, '1000000', // 1 USDC (6 decimals) );
Testing
composer test
655 tests covering:
- BIP-39 mnemonic generation/validation with Sui test vectors
- SIP-15 wallet key encoding/decoding
- All 6 signature schemes (Ed25519, Secp256k1, Secp256r1, Multisig, zkLogin, Passkey)
- Transaction BCS parity with Rust (byte-for-byte)
- Ed25519 / Secp256k1 / Secp256r1 signature parity
- Auto-resolving Transaction builder (gas estimation, coin selection)
- All RPC methods (mocked transport)
- Transaction builder (all 7 command types)
- Signature verification roundtrips
- Typed response deserialization
- Network presets and transport configuration
- gRPC transport and method routing
- Paginated response iteration
- Convenience methods (transfer, stake, merge, send)
- Integration transfer test (testnet)
License
MIT