inodrahq/sui-sdk

The most complete PHP SDK for the Sui blockchain — 6 signature schemes, auto-resolving transactions, typed responses, JSON-RPC + gRPC

Maintainers

Package info

github.com/inodrahq/php-sui-sdk

pkg:composer/inodrahq/sui-sdk

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-03-10 22:58 UTC

This package is auto-updated.

Last update: 2026-03-10 23:01:29 UTC


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