Examples
End-to-end walkthroughs: move a coin, call a Move function, use dynamic fields, and drive Sui from TypeScript.
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.
Examples
The examples in this module's manifest cover the operations nearly every Sui app needs: querying objects, sending SUI, publishing a package, calling a Move function, and reading dynamic fields. All code uses the @mysten/sui TypeScript SDK and the sui CLI.
The shape of a Sui transaction
Unlike EVM transactions (a single calldata blob), a Sui transaction is a transaction block — an ordered list of commands that can reference each other. The pattern to internalize:
build commands → set gas budget → signAndExecute → read effects
Most "why didn't this work" moments come from forgetting one of: the gas budget, the showEffects options flag, or checking effects.status instead of only the digest.
Move-first thinking
Because state is objects, "smart contract" development is really "package" development: you publish a Move package, and its entry functions become the app's callable surface. The CLI examples show the full loop (publish → call → read) using the same package ID throughout.
Examples
Publish a Move package with the CLI
# move/ — a directory with Move.toml and sources/cat > move/Move.toml <<039;EOF039;[package]name = "counter"version = "0.1.0"[dependencies]Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }EOFsui client publish --gas-budget 50000000# output includes: Created Objects: [ { "0x<pkg_id>" ... } ]# that address is your package ID — prefix all moveCall targets with it.
The Sui CLI wraps publishing, key management, and RPC calls. The package ID returned is used as the target prefix for every entry function in the package.
Call an entry function
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();// Move call: the first argument is the package, module, and function.tx.moveCall({target: `${PACKAGE_ID}::counter::increment`,arguments: [tx.object(COUNTER_ID)],});tx.setGasBudget(20_000_000);const res = await client.signAndExecuteTransaction({signer: kp,transaction: tx,options: { showEffects: true },});console.log("digest:", res.digest);// Read the new value:const obj = await client.getObject({id: COUNTER_ID,options: { showContent: true },});console.log(obj.data?.content);
moveCall composes a command in the transaction block; the object arguments are resolved by the client before signing.
Read a dynamic field
import { SuiClient, getFullnodeUrl } from "@mysten/sui/client";const client = new SuiClient({ url: getFullnodeUrl("mainnet") });// Dynamic fields live on objects; you address them by parent + field name.const parent = "0x<parent_object>";const fieldName = "balance";const field = await client.getDynamicFieldObject({parentId: parent,name: { type: "0x1::string::String", value: fieldName },});console.log(field.data?.content);
Dynamic fields let an object own other data without fixed struct layout — the SDK addresses them by parent object + typed name.
Edge Cases
- Gas budget errors surface as a failed status in effects, not a revert — check res.effects?.status before trusting a digest.
- Wrapped or delegated objects are not queryable the same way as normal objects; getObject returns no data.content for them — expect it and handle gracefully.
- Package publishing is irreversible in the sense that upgrades require a package-specific upgrade capability; design upgrade paths into your packages from the start.
