Antler by Autoflux

Examples

End-to-end walkthroughs: move a coin, call a Move function, use dynamic fields, and drive Sui from TypeScript.

Path: examples

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

Publish a Move package with the CLI
bash
# move/ — a directory with Move.toml and sources/
cat > move/Move.toml <<&#039;EOF&#039;
[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" }
EOF
 
sui client publish --gas-budget 50000000
# output includes: Created Objects: [ { "0x<pkg_id>" ... } ]
# that address is your package ID — prefix all moveCall targets with it.
STATUSexample

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

Call an entry function
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();
// 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);
STATUSexample

moveCall composes a command in the transaction block; the object arguments are resolved by the client before signing.

Read a dynamic field

Read a dynamic field
typescript
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);
STATUSexample

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.