Antler by Autoflux

Developer SDK

The @mysten/sui TypeScript SDK and the Rust sui-sdk: clients, transaction blocks, keypairs, and object queries.

Path: sdk

Third-party documentation. This is independently authored analysis of the public Sui codebase — not the official docs, and not reviewed or endorsed by the Sui team.

Developer SDK

The official developer surfaces are the @mysten/sui TypeScript SDK and the sui-sdk Rust crate, plus the sui CLI. This module focuses on the TypeScript SDK, which is what most application builders use.

The four pillars of the SDK

  • ClientsSuiClient talks to any full node's JSON-RPC: queries (getObject, getCoins, queryTransactionBlocks) and submission (signAndExecuteTransaction).
  • Transaction blocksTransaction is a declarative builder for a set of commands: moveCall, splitCoins, transferObjects, mergeCoins, transfer, plus explicit gas configuration. Commands can reference each other's outputs.
  • Keypairs & signersEd25519Keypair, Secp256k1Keypair, and wallet signer adapters. Signing happens locally; the SDK sends the signed bytes to the RPC.
  • GraphQL client (newer addition) — an alternative, strongly-typed query surface for more complex data shapes, alongside the REST-style JSON-RPC.

Reading state

  • client.getObject() — the canonical read; options control how much content/owner/version is returned.
  • client.getCoins() / getBalance — coin enumeration with pagination; balances are computed from coin objects.
  • client.getDynamicFieldObject — read a named field from a parent object.
  • client.queryTransactionBlocks — filter history by sender, package, object, or digest.

Writing state

  1. Build a Transaction block.
  2. Set a gas budget (required — there's no automatic gas estimation).
  3. signAndExecuteTransaction and read effects.status, effects.objectChanges, and digest.

Every command returns a reference you can feed into the next command, which is how multi-step flows (split → transfer) are composed in one transaction.

The CLI

sui client manages keys, addresses, gas, and RPC endpoints; sui move build/publish/upgrade compiles and publishes packages. The CLI is the fastest way to bootstrap: create an address, request test SUI on the faucet, publish a package, then move to the SDK for app logic.

Edge cases

  • Gas budget is mandatory and unforgiving: too low → InsufficientGas and a failed status. Estimate with devInspectTransaction first, or set a comfortable buffer.
  • Command references are by index/output, not by string handles — passing the wrong object reference produces confusing version errors at submit time.
  • Coin operations need a coin object, not a balance; the SDK's helpers (e.g. tx.gas) exist precisely so you don't have to pick a coin manually for common flows.

Examples

Query an object

Query an object
typescript
import { SuiClient, getFullnodeUrl } from "@mysten/sui/client";
 
const client = new SuiClient({ url: getFullnodeUrl("mainnet") });
 
const obj = await client.getObject({
id: "0x<object_id>",
options: { showContent: true, showOwner: true },
});
 
if (obj.data?.content) {
console.log(JSON.stringify(obj.data.content, null, 2));
} else {
console.log("Object not found or wrapped");
}
STATUSexample

Every piece of Sui state is an object you can query by ID. Dynamic fields and child objects are discoverable through the same client.

Send SUI to another address

Send SUI to another address
typescript
import { SuiClient, getFullnodeUrl } from "@mysten/sui/client";
import { Transaction } from "@mysten/sui/transactions";
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
import { fromB64 } from "@mysten/sui/utils";
 
const client = new SuiClient({ url: getFullnodeUrl("testnet") });
const kp = Ed25519Keypair.fromSecretKey(fromB64(PRIVATE_KEY_B64));
 
const tx = new Transaction();
const [coin] = tx.splitCoins(tx.gas, [tx.pure.u64(1_000_000_000n)]); // 1 SUI
const recipient = "0x<recipient>";
tx.transferObjects([coin], tx.pure.address(recipient));
 
tx.setGasBudget(10_000_000);
 
const res = await client.signAndExecuteTransaction({
signer: kp,
transaction: tx,
options: { showEffects: true, showEvents: true },
});
console.log(res.digest);
STATUSexample

Transfers are object transfers: split the gas coin, then move the child coin. Note the explicit gas budget — Sui does not do dynamic gas estimation the way EVM chains do.

Query all coins for an address

Query all coins for an address
typescript
import { SuiClient, getFullnodeUrl } from "@mysten/sui/client";
 
const client = new SuiClient({ url: getFullnodeUrl("mainnet") });
 
const coins = await client.getCoins({
owner: "0x<address>",
});
console.log("total coins:", coins.data.length);
 
// Paginate with cursor if you have many coins:
while (coins.hasNextPage) {
const next = await client.getCoins({
owner: "0x<address>",
cursor: coins.nextCursor!,
});
coins.data.push(...next.data);
coins.hasNextPage = next.hasNextPage;
coins.nextCursor = next.nextCursor;
}
STATUSexample

Balances on Sui are a set of coin objects, not a single number — paginate with the cursor API for wallets with many coins.

Edge Cases

  • Every Sui transaction must declare a gas budget — if it's too low the tx fails with InsufficientGas and no state changes, so quote with headroom.
  • Owned-object transactions are not ordered by consensus; two concurrent txs moving the same object may both fail (version/sequence conflict) and must be retried — do not assume retry-free idempotency.
  • Dynamic fields are not typed by the framework — a wrongly-typed read can panic the module; validate with the SDK's parse helpers before trusting returned content.

Interface

Interface
typescript
import { SuiClient, getFullnodeUrl } from "@mysten/sui/client";
import { Transaction } from "@mysten/sui/transactions";
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
import { fromB64 } from "@mysten/sui/utils";
 
// A client talks to any Sui RPC (full node):
const client = new SuiClient({ url: getFullnodeUrl("mainnet") });
 
// Keypairs sign transactions; a keypair wraps a secret key + pubkey.
const kp = Ed25519Keypair.fromSecretKey(fromB64(PRIVATE_KEY_B64));
 
// Transaction blocks are declarative: add commands, then send.
const tx = new Transaction();
tx.moveCall({
target: `0x<package>::counter::increment`,
arguments: [tx.object(COUNTER_ID)],
});
const res = await client.signAndExecuteTransaction({
signer: kp,
transaction: tx,
options: { showEffects: true },
});
console.log(res.effects?.status, res.digest);
 
STATUSinterface