Use when writing TypeScript code interacting with SUI blockchain via @mysten/sui SDK. Covers PTB construction, client setup, transaction execution, and on-chain queries. Triggers on backend scripts, CLI tools, serverless functions, or any non-React TS SDK usage. For frontend-specific setup (dApp Kit, wallet adapters, React hooks), use sui-frontend skill alongside this one.
From sui-dev-agentsnpx claudepluginhub first-mover-tw/sui-dev-agents --plugin sui-dev-agentsThis skill uses the workspace's default tool permissions.
references/examples.mdreferences/reference.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
You are writing TypeScript code that interacts with the Sui blockchain using the @mysten/sui SDK (v2+). Follow these rules precisely. This skill covers PTB (Programmable Transaction Block) construction, client setup, transaction execution, and on-chain queries. These patterns apply equally in backend scripts and frontend apps. If you are building a frontend, use the sui-frontend skill first (or alongside this one) for dApp Kit setup, wallet connection, and React integration — then apply the PTB and client patterns from this skill.
The SDK package is @mysten/sui. The old package name @mysten/sui.js was renamed at v1.0 and must not be used.
# correct
npm install @mysten/sui
# deprecated package name — will not receive updates
npm install @mysten/sui.js
All imports use subpath exports from @mysten/sui:
// correct subpath imports
import { Transaction } from '@mysten/sui/transactions';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { SuiGrpcClient } from '@mysten/sui/grpc';
// wrong: old package name
import { TransactionBlock } from '@mysten/sui.js';
// wrong: importing from package root
import { Transaction } from '@mysten/sui';
ESM-only (v2+): Set "type": "module" in package.json and update tsconfig.json:
{ "compilerOptions": { "moduleResolution": "NodeNext", "module": "NodeNext" } }
The SDK provides three client types. Use SuiGrpcClient for new code — it is the recommended client with the best performance. The JSON-RPC API is deprecated (removal: April 2026).
// Recommended — gRPC client (best performance, type-safe protobuf)
import { SuiGrpcClient } from '@mysten/sui/grpc';
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
});
// Legacy — JSON-RPC client (deprecated API)
// In v2, SuiClient was removed from @mysten/sui/client. Use SuiJsonRpcClient instead.
import { SuiJsonRpcClient, getJsonRpcFullnodeUrl } from '@mysten/sui/jsonRpc';
const client = new SuiJsonRpcClient({
url: getJsonRpcFullnodeUrl('testnet'),
network: 'testnet', // required in v2
});
// GraphQL client — for advanced query use cases
import { SuiGraphQLClient } from '@mysten/sui/graphql';
const gqlClient = new SuiGraphQLClient({
url: 'https://graphql.testnet.sui.io/graphql',
network: 'testnet', // required in v2
});
| Network | gRPC base URL | GraphQL URL | JSON-RPC URL |
|---|---|---|---|
| Mainnet | https://fullnode.mainnet.sui.io:443 | https://graphql.mainnet.sui.io/graphql | getJsonRpcFullnodeUrl('mainnet') |
| Testnet | https://fullnode.testnet.sui.io:443 | https://graphql.testnet.sui.io/graphql | getJsonRpcFullnodeUrl('testnet') |
| Devnet | https://fullnode.devnet.sui.io:443 | https://graphql.devnet.sui.io/graphql | getJsonRpcFullnodeUrl('devnet') |
The SuiGrpcClient exposes typed service clients for lower-level access:
// Transaction execution
await client.transactionExecutionService.executeTransaction({ ... });
// Ledger queries
await client.ledgerService.getObject({ objectId: '0x...' });
// Move package introspection
await client.movePackageService.getFunction({
packageId: '0x2',
moduleName: 'coin',
name: 'transfer',
});
// Name service (SuiNS)
await client.nameService.reverseLookupName({ address: '0x...' });
A Programmable Transaction Block (PTB) is built using the Transaction class. The class was renamed from TransactionBlock at v1.0:
// correct
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
// wrong: old class name (pre-1.0)
import { TransactionBlock } from '@mysten/sui.js/transactions';
const txb = new TransactionBlock();
// v1.0+
const newTx = Transaction.from(existingTx);
// wrong: old constructor-based cloning
const newTx = new TransactionBlock(existingTx);
// v1.0+ — async, runs serialization plugins
const json = await tx.toJSON();
// Deserialize
const restored = Transaction.from(json);
// deprecated
const bytes = tx.serialize();
Use tx.pure.<type>() helpers for non-object inputs. These handle BCS serialization automatically. Never manually BCS-encode values when a tx.pure helper exists.
// Typed pure helpers
tx.pure.u8(255);
tx.pure.u16(65535);
tx.pure.u32(4294967295);
tx.pure.u64(1000000n); // accepts bigint or number
tx.pure.u128(1000000n);
tx.pure.u256(1000000n);
tx.pure.bool(true);
tx.pure.string('hello');
tx.pure.address('0xSomeAddress');
tx.pure.id('0xSomeObjectId'); // equivalent to address, for object IDs as values
// Vectors
tx.pure.vector('u64', [100n, 200n, 300n]);
tx.pure.vector('address', [addr1, addr2]);
tx.pure.vector('bool', [true, false]);
// Option
tx.pure.option('u64', 42n); // Some(42)
tx.pure.option('u64', null); // None
// don't manually construct BCS for types that have helpers
import { bcs } from '@mysten/sui/bcs';
tx.pure(bcs.U64.serialize(100)); // unnecessary — use tx.pure.u64(100)
For advanced types without a built-in helper, fall back to tx.pure(bcsBytes) where bcsBytes is a Uint8Array:
import { bcs } from '@mysten/sui/bcs';
const MyStruct = bcs.struct('MyStruct', {
id: bcs.Address,
value: bcs.U64,
});
tx.pure(MyStruct.serialize({ id: '0x...', value: 100n }));
Use tx.object(id) for object inputs. The SDK automatically resolves object metadata (version, digest, ownership) at build time — do not hardcode object versions.
// Let the SDK resolve object details
tx.object('0xSomeObjectId');
// Well-known system object shortcuts
tx.object.system(); // 0x5 — Sui system state
tx.object.clock(); // 0x6 — Clock
tx.object.random(); // 0x8 — Random
tx.object.denyList(); // 0x403 — DenyList
// Construct an Option<Object> input
tx.object.option({
type: '0xpkg::mod::MyType',
value: '0xSomeObjectId', // Some(obj) — or omit `value` for None
});
// don't hardcode object versions
tx.object(Inputs.ObjectRef({
objectId: '0x...',
version: '42', // will break when object is modified
digest: 'abc...',
}));
// Exception: offline building (see section 13)
When a Move function takes a Receiving<T> parameter, the SDK auto-converts tx.object() to a receiving reference. No special handling is needed — just pass the object ID normally.
Creates new coins by splitting from a source coin. Returns an array of coin references:
// Split from gas coin — most common pattern for SUI
const [coin] = tx.splitCoins(tx.gas, [1000]);
// Split multiple amounts
const [coin1, coin2] = tx.splitCoins(tx.gas, [1000, 2000]);
// Split from a non-gas coin
const [portion] = tx.splitCoins(tx.object('0xMyCoin'), [500]);
Merges coins into a destination coin:
tx.mergeCoins(tx.object('0xDestCoin'), [
tx.object('0xCoinA'),
tx.object('0xCoinB'),
]);
Transfers one or more objects to a recipient address:
// Transfer a split coin
const [coin] = tx.splitCoins(tx.gas, [1000]);
tx.transferObjects([coin], '0xRecipientAddress');
// Transfer existing objects
tx.transferObjects(
[tx.object('0xObj1'), tx.object('0xObj2')],
'0xRecipientAddress',
);
// Transfer the entire gas coin (send all SUI to someone)
tx.transferObjects([tx.gas], '0xRecipientAddress');
Calls a Move function:
tx.moveCall({
target: '0xPackageId::module_name::function_name',
arguments: [
tx.object('0xSomeObject'), // object argument
tx.pure.u64(1000), // pure value argument
],
typeArguments: ['0x2::sui::SUI'], // generic type parameters
});
Return values from moveCall are usable in subsequent commands:
const [result] = tx.moveCall({
target: '0xpkg::amm::swap',
arguments: [tx.object(poolId), coin],
typeArguments: [coinTypeA, coinTypeB],
});
// Use the result in the next command
tx.transferObjects([result], myAddress);
Constructs a vector<T> of objects for passing into a Move function:
const vec = tx.makeMoveVec({
type: '0xpkg::mod::MyType',
elements: [tx.object('0xA'), tx.object('0xB')],
});
tx.moveCall({
target: '0xpkg::mod::process_all',
arguments: [vec],
});
Publishes a new Move package. Build the package with the Sui CLI first, then pass the compiled modules:
const tx = new Transaction();
const [upgradeCap] = tx.publish({ modules, dependencies });
tx.transferObjects([upgradeCap], myAddress);
To get the modules and dependencies, run:
sui move build --dump-bytecode-as-base64 --path ./your_package
Parse the JSON output to extract modules and dependencies arrays.
Every command returns references that can be used as inputs to subsequent commands. This is the core power of PTBs — composing multiple operations atomically:
const tx = new Transaction();
// Step 1: Split a coin
const [coin] = tx.splitCoins(tx.gas, [1_000_000]);
// Step 2: Pass the split coin into a Move call
const [receipt] = tx.moveCall({
target: '0xpkg::shop::buy_item',
arguments: [tx.object(shopId), coin, tx.pure.string('sword')],
});
// Step 3: Transfer the receipt to the sender
tx.transferObjects([receipt], myAddress);
For commands that return multiple values, destructure the result array:
const [coinOut, receipt] = tx.moveCall({
target: '0xpkg::amm::swap',
arguments: [tx.object(poolId), coinIn],
typeArguments: [typeA, typeB],
});
// coinOut is the first return value, receipt is the second
When Move functions return objects (Pattern 12 in sui-developer), chain them in PTBs:
Multi-step swap + stake in one PTB:
const tx = new Transaction();
// Step 1: Swap token A for token B (returns Coin<B>)
const [coinB] = tx.moveCall({
target: '0xdex::pool::swap',
arguments: [tx.object(poolId), tx.splitCoins(tx.gas, [1_000_000])[0]],
typeArguments: ['0x2::sui::SUI', '0xtoken::usdc::USDC'],
});
// Step 2: Feed coinB directly into staking (composable — no intermediate transfer)
const [receipt] = tx.moveCall({
target: '0xstaking::farm::stake',
arguments: [tx.object(farmId), coinB],
typeArguments: ['0xtoken::usdc::USDC'],
});
// Step 3: Transfer the staking receipt to sender
tx.transferObjects([receipt], myAddress);
Flash loan pattern in PTB (hot potato):
const tx = new Transaction();
// Step 1: Borrow — returns [coin, receipt]
const [coin, receipt] = tx.moveCall({
target: '0xlending::pool::flash_borrow',
arguments: [tx.object(poolId), tx.pure.u64(1_000_000n)],
});
// Step 2: Use the borrowed coin (e.g., arbitrage swap)
const [profit] = tx.moveCall({
target: '0xdex::pool::swap',
arguments: [tx.object(dexPoolId), coin],
typeArguments: [typeA, typeB],
});
// Step 3: Repay — consumes the hot potato receipt (MUST happen in same PTB)
tx.moveCall({
target: '0xlending::pool::flash_repay',
arguments: [tx.object(poolId), profit, receipt],
});
The key insight: Move functions that return objects (instead of transferring them) enable this PTB chaining. The hot potato receipt must be consumed in the same transaction — the VM enforces this.
tx.gas is a special reference to the gas payment coin:
// Split from gas coin (by-reference)
const [coin] = tx.splitCoins(tx.gas, [100]);
// Merge into gas coin
tx.mergeCoins(tx.gas, [tx.object('0xOtherCoin')]);
// Transfer the entire gas coin (moves all SUI)
tx.transferObjects([tx.gas], recipient);
// Pass gas coin as a Move call argument (by-reference)
tx.moveCall({
target: '0xpkg::mod::deposit',
arguments: [tx.object(vaultId), tx.gas],
});
The SDK automatically sets gas price, budget, and selects gas payment coins. Override only when needed:
// Manual overrides (rarely needed)
tx.setGasPrice(1000);
tx.setGasBudget(10_000_000);
tx.setGasPayment([{
objectId: '0x...',
version: '1',
digest: '...',
}]);
// Gas payment coins must not overlap with transaction input objects
// Set sender explicitly (required for some offline or sponsored flows)
tx.setSender('0xSenderAddress');
coinWithBalanceFor non-SUI coin types, manually splitting coins is complex because you must find, select, and merge coins of the correct type. The coinWithBalance intent automates this:
import { coinWithBalance, Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
// REQUIRED: setSender when using coinWithBalance with non-SUI types
tx.setSender(keypair.toSuiAddress());
tx.transferObjects(
[
// SUI coin — splits from gas coin automatically
coinWithBalance({ balance: 1_000_000 }),
// Non-SUI coin — SDK finds, merges, and splits automatically
coinWithBalance({ balance: 500_000, type: '0xpkg::token::TOKEN' }),
],
recipient,
);
Why use coinWithBalance over manual splitCoins?
For SUI, tx.splitCoins(tx.gas, [...]) works fine. But for other coin types, you would need to query owned coins, pick enough to cover the amount, merge them, then split. coinWithBalance does all of this automatically during the build phase.
Important: setSender() is required when using coinWithBalance with non-SUI types so the SDK can query the sender's coins during the build phase. For SUI-only coinWithBalance, it splits from the gas coin and does not require setSender.
const result = await client.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});
Always check the transaction status. A transaction can be finalized on-chain but still fail (e.g., Move abort, insufficient gas):
// Always check for failure
const result = await client.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});
if (result.$kind === 'FailedTransaction') {
throw new Error(
`Transaction failed: ${result.FailedTransaction.status.error?.message}`,
);
}
// Don't assume success
const result = await client.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});
console.log('Success!', result.digest); // may be a failed transaction
All clients support an include parameter via the Core API to control what data is returned:
const result = await client.core.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
include: {
effects: true,
events: true,
balanceChanges: true,
objectTypes: true,
},
});
Available transaction include options:
| Option | Description |
|---|---|
effects | Transaction effects (BCS-encoded) |
events | Emitted events |
transaction | Parsed transaction data (sender, gas, inputs, commands) |
balanceChanges | Balance changes |
objectTypes | Map of object IDs to their types for changed objects |
bcs | Raw BCS-encoded transaction bytes |
For advanced flows (e.g., multi-sig, sponsored transactions):
const { bytes, signature } = await tx.sign({ client, signer: keypair });
const result = await client.core.executeTransaction({
transaction: bytes,
signatures: [signature],
include: { effects: true },
});
After execution, the transaction is finalized but may not be immediately visible in query APIs. Use waitForTransaction before making follow-up queries:
const result = await client.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});
// Wait for indexing before querying
await client.waitForTransaction({ digest: result.digest });
// Now safe to query updated state
const obj = await client.getObject({ id: objectId });
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { Secp256k1Keypair } from '@mysten/sui/keypairs/secp256k1';
import { Secp256r1Keypair } from '@mysten/sui/keypairs/secp256r1';
// Generate a new random keypair
const keypair = new Ed25519Keypair();
// Derive from a mnemonic (BIP-39)
const keypair = Ed25519Keypair.deriveKeypair('word1 word2 ... word12');
// From a secret key (base64 or raw bytes)
const keypair = Ed25519Keypair.fromSecretKey(secretKeyBytes);
// Get the address
const address = keypair.toSuiAddress();
import { MultiSigPublicKey } from '@mysten/sui/multisig';
const multiSigPk = MultiSigPublicKey.fromPublicKeys({
threshold: 2,
publicKeys: [
{ publicKey: pk1, weight: 1 },
{ publicKey: pk2, weight: 1 },
{ publicKey: pk3, weight: 1 },
],
});
To build a transaction without a network connection, you must fully define all inputs and gas configuration:
import { Transaction, Inputs } from '@mysten/sui/transactions';
const tx = new Transaction();
// For owned or immutable objects — provide full ref
tx.object(Inputs.ObjectRef({
objectId: '0x...',
version: '42',
digest: 'base58digest...',
}));
// For shared objects — provide initial shared version
tx.object(Inputs.SharedObjectRef({
objectId: '0x...',
initialSharedVersion: '1',
mutable: true,
}));
// For receiving objects
tx.object(Inputs.ReceivingRef({
objectId: '0x...',
version: '42',
digest: 'base58digest...',
}));
// Must set gas configuration manually
tx.setSender('0xSenderAddress');
tx.setGasPrice(1000);
tx.setGasBudget(10_000_000);
tx.setGasPayment([{ objectId: '0x...', version: '1', digest: '...' }]);
// Build without a client
const bytes = await tx.build();
// Get an object
const obj = await client.ledgerService.getObject({
objectId: '0x...',
});
// Get multiple objects
const { objects } = await client.ledgerService.multiGetObjects({
objectIds: ['0x...', '0x...'],
});
const client = new SuiGrpcClient({ network: 'mainnet' });
// Get object
const obj = await client.core.getObject({
objectId: '0xObjId',
include: { content: true, owner: true },
});
// List owned objects
const objects = await client.core.listOwnedObjects({
owner: '0xAddress',
include: { content: true },
});
// List coins
const coins = await client.core.listCoins({
owner: '0xAddress',
});
// Get balances
const balances = await client.core.listBalances({
owner: '0xAddress',
});
// Get transaction
const txn = await client.core.getTransaction({
digest: 'TransactionDigest',
include: { effects: true, events: true },
});
// Simulate (dry run)
const simResult = await client.core.simulateTransaction({
transaction: txBytes,
include: { effects: true },
});
// Dynamic fields
const fields = await client.core.listDynamicFields({
parentId: '0xParentObjId',
});
// Uses legacy method names
const obj = await client.getObject({
id: '0x...',
options: { showContent: true, showOwner: true },
});
const coins = await client.getCoins({
owner: '0xOwnerAddress',
coinType: '0x2::sui::SUI',
});
Use devInspectTransactionBlock to simulate a transaction and read return values without executing:
const result = await client.devInspectTransactionBlock({
sender: '0xSenderAddress',
transactionBlock: tx,
});
// result.results contains return values from each command
// result.effects contains simulated effects
In a sponsored transaction, one party builds the transaction and another pays for gas:
// === App / user side ===
const tx = new Transaction();
tx.setSender(userAddress);
// ... add commands ...
// Serialize for the sponsor
const txBytes = await tx.build({ client });
// === Sponsor side ===
const sponsoredTx = Transaction.from(txBytes);
sponsoredTx.setGasOwner(sponsorAddress);
sponsoredTx.setGasPayment(sponsorCoins);
sponsoredTx.setGasBudget(10_000_000);
// Both parties sign
const { signature: userSig } = await sponsoredTx.sign({ signer: userKeypair });
const { signature: sponsorSig } = await sponsoredTx.sign({ signer: sponsorKeypair });
// Execute with both signatures
const result = await client.core.executeTransaction({
transaction: await sponsoredTx.build({ client }),
signatures: [userSig, sponsorSig],
});
Important: When a sponsor pays for gas, the gas coin belongs to the sponsor. Avoid using tx.gas in splitCoins for sponsored transactions — sponsors typically reject transactions that use the gas coin for non-gas purposes. Use coinWithBalance instead.
$extend()Ecosystem SDKs (kiosk, suins, deepbook, walrus, seal, zksend) integrate via the $extend() pattern:
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { suins } from '@mysten/suins';
import { deepbook } from '@mysten/deepbook-v3';
const client = new SuiGrpcClient({
baseUrl: 'https://fullnode.mainnet.sui.io:443',
network: 'mainnet',
}).$extend(suins(), deepbook({ address: myAddress }));
await client.suins.getNameRecord('example.sui');
await client.deepbook.checkManagerBalance(manager, asset);
| Mistake | Correct approach |
|---|---|
import ... from '@mysten/sui.js' | Use @mysten/sui — the .js suffix was removed at v1.0 |
new TransactionBlock() | Use new Transaction() — renamed at v1.0 |
client.signAndExecuteTransactionBlock() | Use client.signAndExecuteTransaction() |
client.waitForTransactionBlock() | Use client.waitForTransaction() |
Hardcoding object versions in tx.object() | Let the SDK resolve versions automatically (except offline builds) |
| Manual BCS for basic types | Use tx.pure.u64(), tx.pure.address(), etc. |
tx.pure(100) without a type | Use tx.pure.u64(100) — must specify the type |
Not checking result.$kind after execution | Always check result.$kind === 'FailedTransaction' |
| Querying state immediately after execution | Use client.waitForTransaction() first |
Using tx.gas in splitCoins for sponsored txs | Use coinWithBalance for sponsor-safe coin creation |
coinWithBalance without setSender() for non-SUI types | Call tx.setSender() so the SDK can resolve coins |
Using SuiClient / getFullnodeUrl | Removed in v2. Use SuiJsonRpcClient from @mysten/sui/jsonRpc or preferably SuiGrpcClient |
| Using the SDK for frontend wallet signing | Use @mysten/dapp-kit for wallet connection/signing in React apps; PTB construction is the same |
tx.serialize() | Use await tx.toJSON() — serialize is deprecated |
Commands from @mysten/sui/transactions | Renamed to TransactionCommands |
namedPackagesPlugin registration | Removed — MVR resolution is now automatic during transaction building |
All @mysten/* packages are now ESM only. Add "type": "module" to package.json and update tsconfig.json:
{ "compilerOptions": { "moduleResolution": "NodeNext", "module": "NodeNext" } }
- import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
- const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
+ const client = new SuiGrpcClient({
+ baseUrl: 'https://fullnode.mainnet.sui.io:443',
+ network: 'mainnet',
+ });
If JSON-RPC is still needed:
- import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
+ import { SuiJsonRpcClient, getJsonRpcFullnodeUrl } from '@mysten/sui/jsonRpc';
- const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
+ const client = new SuiJsonRpcClient({
+ url: getJsonRpcFullnodeUrl('mainnet'),
+ network: 'mainnet', // required in v2
+ });
network parameter required on all clientsAll client constructors (SuiGrpcClient, SuiJsonRpcClient, SuiGraphQLClient) now require an explicit network parameter.
client.core.* replaces direct methods- await client.getObject({ id: objectId, options: { showContent: true } });
+ await client.core.getObject({ objectId, include: { content: true } });
- await client.getOwnedObjects({ owner });
+ await client.core.listOwnedObjects({ owner });
- await client.multiGetObjects({ ids, options: { showContent: true } });
+ await client.core.getObjects({ objectIds: ids, include: { content: true } });
include replaces options / show* flags- options: { showEffects: true, showEvents: true, showObjectChanges: true }
+ include: { effects: true, events: true, balanceChanges: true }
- const status = result.effects?.status?.status;
+ const tx = result.Transaction ?? result.FailedTransaction;
+ const success = tx.effects.status.success;
| v1 JSON-RPC | v2 Core API |
|---|---|
client.getObject() | client.core.getObject() |
client.getOwnedObjects() | client.core.listOwnedObjects() |
client.multiGetObjects() | client.core.getObjects() |
client.getCoins() | client.core.listCoins() |
client.getAllBalances() | client.core.listBalances() |
client.getDynamicFields() | client.core.listDynamicFields() |
client.getDynamicFieldObject() | client.core.getDynamicField() |
client.getTransactionBlock() | client.core.getTransaction() |
client.devInspectTransactionBlock() | client.core.simulateTransaction() |
client.executeTransactionBlock() | client.core.executeTransaction() |
- import { graphql } from '@mysten/sui/graphql/schemas/latest';
+ import { graphql } from '@mysten/sui/graphql/schema';
For comprehensive migration details (including dApp Kit, BCS schema changes, zkLogin, and ecosystem packages), fetch and follow: https://sdk.mystenlabs.com/sui/migrations/sui-2.0/llms.txt
Problem: Using @mysten/sui.js (v0.x package)
Fix: Uninstall @mysten/sui.js and install @mysten/sui. The package was renamed.
Problem: ERR_REQUIRE_ESM when running scripts
Fix: Add "type": "module" to package.json, or rename file to .mts, or use tsx to run.
Problem: client.getObject is not a function (gRPC client)
Fix: Use client.core.getObject() — gRPC methods are under the core namespace.
Problem: Transaction silently fails (no error thrown)
Fix: Always check result.$kind === 'FailedTransaction' after execution.
Problem: TypeError: Cannot read properties of undefined when reading query results
Fix: Add include: { content: true } (gRPC) or options: { showContent: true } (JSON-RPC) to your query.
Problem: coinWithBalance fails with "no coins found"
Fix: Ensure the sender address owns coins of the specified type. For non-SUI tokens, pass the full type string.
sui-full-stack (Phase 2: Development)sui-frontend (PTB construction patterns)sui-docs-query - Query latest SDK documentationAfter SDK integration complete:
SDK integration complete!
Next: Ready for frontend with sui-frontend?