| name | sui-ts-sdk |
| description | 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. |
Sui TypeScript SDK Skill
SDK Versions
Targets: @mysten/sui 2.22.0 (^2.0). Tested: 2026-07-18.
Compatibility notes: Sui 2.x removed SuiClient from @mysten/sui/client, @mysten/sui/cryptography/hash, and event pub/sub (subscribeEvent has no v2 equivalent — use an indexer or the gRPC checkpoint stream). If your install is on 1.x, stop and either upgrade or follow the 1.x patterns in your installed package's README — do not mix.
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.
1. Package & Imports
The SDK package is @mysten/sui. The old package name @mysten/sui.js was renamed at v1.0 and must not be used.
npm install @mysten/sui
npm install @mysten/sui.js
All imports use subpath exports from @mysten/sui:
import { Transaction } from '@mysten/sui/transactions';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { TransactionBlock } from '@mysten/sui.js';
import { Transaction } from '@mysten/sui';
ESM-only (v2+): Set "type": "module" in package.json and update tsconfig.json:
{ "compilerOptions": { "moduleResolution": "NodeNext", "module": "NodeNext" } }
2. Client Setup
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 (permanent deactivation: 2026-07-31; public endpoints shutting down July 2026). Since sui 2.20.4 every JSON-RPC client/transport API and JSON-RPC-specific type is marked @deprecated in the SDK — expect strikethrough in editors and deprecation lint warnings.
import { SuiGrpcClient } from '@mysten/sui/grpc';
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
});
For legacy JSON-RPC, GraphQL clients, and gRPC service clients, see references/advanced-patterns.md.
Network URLs
| Network | gRPC base URL |
|---|
| Mainnet | https://fullnode.mainnet.sui.io:443 |
| Testnet | https://fullnode.testnet.sui.io:443 |
| Devnet | https://fullnode.devnet.sui.io:443 |
3. Transaction Construction
A Programmable Transaction Block (PTB) is built using the Transaction class. The class was renamed from TransactionBlock at v1.0:
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
import { TransactionBlock } from '@mysten/sui.js/transactions';
const txb = new TransactionBlock();
Cloning a transaction
const newTx = Transaction.from(existingTx);
const newTx = new TransactionBlock(existingTx);
Serialization
const json = await tx.toJSON();
const restored = Transaction.from(json);
const bytes = tx.serialize();
4. Pure Value Inputs
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.
tx.pure.u8(255);
tx.pure.u16(65535);
tx.pure.u32(4294967295);
tx.pure.u64(1000000n);
tx.pure.u128(1000000n);
tx.pure.u256(1000000n);
tx.pure.bool(true);
tx.pure.string('hello');
tx.pure.address('0xSomeAddress');
tx.pure.id('0xSomeObjectId');
tx.pure.vector('u64', [100n, 200n, 300n]);
tx.pure.vector('address', [addr1, addr2]);
tx.pure.vector('bool', [true, false]);
tx.pure.option('u64', 42n);
tx.pure.option('u64', null);
import { bcs } from '@mysten/sui/bcs';
tx.pure(bcs.U64.serialize(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 }));
5. Object Inputs
Use tx.object(id) for object inputs. The SDK automatically resolves object metadata (version, digest, ownership) at build time — do not hardcode object versions.
tx.object('0xSomeObjectId');
tx.object.system();
tx.object.clock();
tx.object.random();
tx.object.denyList();
tx.object.option({
type: '0xpkg::mod::MyType',
value: '0xSomeObjectId',
});
tx.object(Inputs.ObjectRef({
objectId: '0x...',
version: '42',
digest: 'abc...',
}));
Receiving objects
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.
6. Built-in Commands
splitCoins
Creates new coins by splitting from a source coin. Returns an array of coin references:
const [coin] = tx.splitCoins(tx.gas, [1000]);
const [coin1, coin2] = tx.splitCoins(tx.gas, [1000, 2000]);
const [portion] = tx.splitCoins(tx.object('0xMyCoin'), [500]);
mergeCoins
Merges coins into a destination coin:
tx.mergeCoins(tx.object('0xDestCoin'), [
tx.object('0xCoinA'),
tx.object('0xCoinB'),
]);
transferObjects
Transfers one or more objects to a recipient address:
const [coin] = tx.splitCoins(tx.gas, [1000]);
tx.transferObjects([coin], '0xRecipientAddress');
tx.transferObjects(
[tx.object('0xObj1'), tx.object('0xObj2')],
'0xRecipientAddress',
);
tx.transferObjects([tx.gas], '0xRecipientAddress');
moveCall
Calls a Move function:
tx.moveCall({
target: '0xPackageId::module_name::function_name',
arguments: [
tx.object('0xSomeObject'),
tx.pure.u64(1000),
],
typeArguments: ['0x2::sui::SUI'],
});
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],
});
tx.transferObjects([result], myAddress);
makeMoveVec
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],
});
publish
Publishing a Move package from TS is occasional and overlaps the sui-deployer
skill. For the tx.publish({ modules, dependencies }) pattern and the
sui move build --dump-bytecode-as-base64 step, see
references/advanced-patterns.md
or the sui-deployer skill.
7. Command Result Chaining
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();
const [coin] = tx.splitCoins(tx.gas, [1_000_000]);
const [receipt] = tx.moveCall({
target: '0xpkg::shop::buy_item',
arguments: [tx.object(shopId), coin, tx.pure.string('sword')],
});
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],
});
For advanced PTB composability (multi-step swap+stake, flash loan hot potato patterns), see references/advanced-patterns.md.
8. Gas Coin
tx.gas is a special reference to the gas payment coin:
const [coin] = tx.splitCoins(tx.gas, [100]);
tx.mergeCoins(tx.gas, [tx.object('0xOtherCoin')]);
tx.transferObjects([tx.gas], recipient);
tx.moveCall({
target: '0xpkg::mod::deposit',
arguments: [tx.object(vaultId), tx.gas],
});
Gas configuration
The SDK automatically sets gas price, budget, and selects gas payment coins. Override only when needed:
tx.setGasPrice(1000);
tx.setGasBudget(10_000_000);
tx.setGasPayment([{
objectId: '0x...',
version: '1',
digest: '...',
}]);
tx.setSender('0xSenderAddress');
Address-balance gas + expiration (@mysten/sui ≥2.22.1): when a transaction uses address-balance gas (explicit empty gas payment) and a preset gas budget — i.e. backend gas selection is skipped — with no expiration set, gRPC/GraphQL resolution now auto-sets a ValidDuring expiration from the simulation's effects epoch. Explicitly set expirations — including { None: true } — are preserved, and kind-only resolution / backend gas selection paths (budget or payment unset) are unaffected. On 2.22.0 (this skill's tested pin) no expiration is synthesized.
9. Transaction Intents — coinWithBalance
For 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();
tx.setSender(keypair.toSuiAddress());
tx.transferObjects(
[
coinWithBalance({ balance: 1_000_000 }),
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.
10. Execution & Status Checking
Sign and execute
const result = await client.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});
if (result.$kind === 'FailedTransaction') {
throw new Error(
`Transaction failed: ${result.FailedTransaction.status.error?.message}`,
);
}
Execution with include options
const result = await client.core.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
include: { effects: true, events: true, balanceChanges: true, objectTypes: true },
});
On SuiGrpcClient (sui ≥2.22), transaction methods also accept include: { protoJson: true } — the result then carries a protoJson field with the raw protobuf-JSON gRPC response (works on getTransaction, waitForTransaction, executeTransaction, signAndExecuteTransaction, simulateTransaction).
Waiting for indexing
After execution, wait before follow-up queries:
await client.waitForTransaction({ digest: result.digest });
For separate sign + execute, multi-sig, keypairs, offline building, client extensions, v1→v2 migration, and common mistakes, see:
For sponsored transactions specifically, see references/ptbs-advanced.md (concept) and references/examples.md §3 (complete code).
11. Common Query Patterns
Core API (recommended)
const obj = await client.core.getObject({
objectId: '0xObjId',
include: { content: true, owner: true },
});
const objects = await client.core.listOwnedObjects({ owner: '0xAddress', include: { content: true } });
const coins = await client.core.listCoins({ owner: '0xAddress' });
const balances = await client.core.listBalances({ owner: '0xAddress' });
const txn = await client.core.getTransaction({ digest: 'Digest', include: { effects: true, events: true } });
const simResult = await client.core.simulateTransaction({ transaction: txBytes, include: { effects: true } });
const fields = await client.core.listDynamicFields({ parentId: '0xParentObjId' });
All core methods accept an AbortSignal via options.signal. Note: before sui 2.20.4 SuiGrpcClient accepted signal but never forwarded it (requests could not be cancelled); 2.20.4 fixes this, including MVR resolveType/resolvePackage resolution.
gRPC service clients (lower-level)
For the lower-level service clients (ledgerService, transactionExecutionService,
movePackageService, etc.), see
references/advanced-patterns.md.
Verifying signatures (@mysten/sui/verify, boolean forms sui ≥2.19)
import { verifyTransactionSignature, isValidTransactionSignature } from '@mysten/sui/verify';
const pubkey = await verifyTransactionSignature(txBytes, signature, { address: senderAddress });
const ok = await isValidTransactionSignature(txBytes, signature, { address: senderAddress });
Siblings: isValidSignature(bytes, sig, { address? }) (no client option) and
isValidPersonalMessageSignature(message, sig, { client?, address? }). The verify*
functions now delegate to these internally.
12. What the Sui TS SDK is NOT
| Mistake | Correct approach |
|---|
import ... from '@mysten/sui.js' | Use @mysten/sui — .js suffix removed at v1.0 |
new TransactionBlock() | Use new Transaction() — renamed at v1.0 |
client.signAndExecuteTransactionBlock() | Use client.signAndExecuteTransaction() |
Hardcoding object versions in tx.object() | Let SDK resolve automatically (except offline builds) |
| Manual BCS for basic types | Use tx.pure.u64(), tx.pure.address(), etc. |
Not checking result.$kind after execution | Always check for FailedTransaction |
| Querying state immediately after execution | Use client.waitForTransaction() first |
Using SuiClient / getFullnodeUrl | Removed in v2 — use SuiGrpcClient |
tx.serialize() | Use await tx.toJSON() |
Integration
Called By
sui-full-stack (Phase 2: Development)
sui-frontend (PTB construction patterns)
Calls
sui-docs-query - Query latest SDK documentation
References
Related @mysten packages (niche / pre-1.0, not yet covered in depth)
@mysten/payment-kit (PaymentKitClient, paymentKit client $extend() plugin, payment URIs), experimental — structured on-chain payment flows. Reach for it instead of hand-rolling coin transfers when you need payment-request semantics.
@mysten/pas (PASClient) — Permissioned Assets Standard; regulated/compliance-gated asset transfers. Niche.
See Also
- ptbs-advanced.md — Read when building a sponsored transaction, reading pruned/archival history, or mapping a use-case to the right client API