Skip to main content
The Sui TypeScript SDK (@mysten/sui) provides comprehensive tools for building web applications on Sui.
The TypeScript SDK has moved to a dedicated repository. Install the latest version from npm.

Installation

Install the SDK and its peer dependencies:
npm install @mysten/sui
Or with yarn:
yarn add @mysten/sui

Creating a Client

Connect to different Sui networks:
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';

// Mainnet
const mainnetClient = new SuiClient({
  url: getFullnodeUrl('mainnet'),
});

// Testnet
const testnetClient = new SuiClient({
  url: getFullnodeUrl('testnet'),
});

// Devnet
const devnetClient = new SuiClient({
  url: getFullnodeUrl('devnet'),
});

// Localnet
const localnetClient = new SuiClient({
  url: getFullnodeUrl('localnet'),
});

// Custom RPC
const customClient = new SuiClient({
  url: 'https://your-custom-rpc.com',
});

Reading Data

Get owned objects

import { SuiClient } from '@mysten/sui/client';

const client = new SuiClient({ url: getFullnodeUrl('testnet') });
const address = '0x...';

// Get all owned objects
const objects = await client.getOwnedObjects({
  owner: address,
  options: {
    showType: true,
    showContent: true,
    showDisplay: true,
  },
});

console.log('Owned objects:', objects.data);

// Paginate through objects
let hasMore = true;
let cursor = null;

while (hasMore) {
  const response = await client.getOwnedObjects({
    owner: address,
    cursor,
    limit: 50,
  });

  console.log('Objects:', response.data);
  hasMore = response.hasNextPage;
  cursor = response.nextCursor;
}

Get specific object

const objectId = '0x...';

const object = await client.getObject({
  id: objectId,
  options: {
    showType: true,
    showOwner: true,
    showContent: true,
    showDisplay: true,
    showBcs: true,
  },
});

console.log('Object:', object.data);

Get coins and balances

// Get all SUI coins
const coins = await client.getCoins({
  owner: address,
  coinType: '0x2::sui::SUI',
});

console.log('SUI coins:', coins.data);

// Get balance
const balance = await client.getBalance({
  owner: address,
  coinType: '0x2::sui::SUI',
});

console.log('Balance:', balance.totalBalance);

// Get all coin balances
const allBalances = await client.getAllBalances({
  owner: address,
});

allBalances.forEach((bal) => {
  console.log(`${bal.coinType}: ${bal.totalBalance}`);
});

Working with Keypairs

Generate new keypair

import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { Secp256k1Keypair } from '@mysten/sui/keypairs/secp256k1';
import { Secp256r1Keypair } from '@mysten/sui/keypairs/secp256r1';

// Ed25519 (recommended)
const ed25519Keypair = new Ed25519Keypair();

// Secp256k1
const secp256k1Keypair = new Secp256k1Keypair();

// Secp256r1  
const secp256r1Keypair = new Secp256r1Keypair();

// Get address
const address = ed25519Keypair.getPublicKey().toSuiAddress();
console.log('Address:', address);

Import from private key

import { fromSecretKey } from '@mysten/sui/keypairs/ed25519';
import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';

// From Sui private key (starts with 'suiprivkey')
const keypair = decodeSuiPrivateKey('suiprivkey1...');

// From raw bytes
const secretKey = new Uint8Array([/* ... */]);
const keypairFromBytes = fromSecretKey(secretKey);

Export private key

const privateKey = keypair.getSecretKey();
const exportedKey = keypair.export();
console.log('Private key:', exportedKey.privateKey);

Building Transactions

Simple transfer

import { Transaction } from '@mysten/sui/transactions';

const tx = new Transaction();

// Transfer SUI
tx.transferObjects(
  [tx.splitCoins(tx.gas, [1000000])], // 1000000 MIST = 0.001 SUI
  '0x...recipient...'
);

// Set sender and gas budget
tx.setSender(address);
tx.setGasBudget(10000000);

Complex transaction

const tx = new Transaction();

// Split a coin
const [coin] = tx.splitCoins(tx.gas, [1000000]);

// Call a Move function
tx.moveCall({
  target: '0xPACKAGE::module::function',
  arguments: [
    tx.pure.u64(42),
    tx.pure.address('0x...'),
    tx.object('0xOBJECT_ID'),
  ],
  typeArguments: ['0x2::sui::SUI'],
});

// Transfer result
tx.transferObjects([coin], recipient);

Calling Move functions

const tx = new Transaction();

// Call function with arguments
tx.moveCall({
  target: '${packageId}::nft::mint',
  arguments: [
    tx.pure.string('NFT Name'),
    tx.pure.string('Description'),
    tx.pure.string('https://image.url'),
  ],
});

// Call with type parameters
tx.moveCall({
  target: '${packageId}::token::transfer',
  arguments: [
    tx.object(tokenId),
    tx.pure.address(recipient),
  ],
  typeArguments: ['0x2::sui::SUI'],
});

Signing and Executing

Sign and execute transaction

import { Transaction } from '@mysten/sui/transactions';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';

const keypair = new Ed25519Keypair();
const client = new SuiClient({ url: getFullnodeUrl('testnet') });

const tx = new Transaction();
tx.transferObjects(
  [tx.splitCoins(tx.gas, [1000000])],
  recipient
);
tx.setSender(keypair.getPublicKey().toSuiAddress());

// Sign and execute
const result = await client.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair,
  options: {
    showEffects: true,
    showObjectChanges: true,
    showEvents: true,
  },
});

console.log('Transaction digest:', result.digest);
console.log('Effects:', result.effects);
console.log('Events:', result.events);

Dry run transaction

Test transaction without executing:
const dryRunResult = await client.dryRunTransactionBlock({
  transactionBlock: await tx.build({ client }),
});

console.log('Estimated gas:', dryRunResult.effects.gasUsed);
console.log('Status:', dryRunResult.effects.status);

Querying Events

// Query events by transaction
const events = await client.queryEvents({
  query: { Transaction: transactionDigest },
});

console.log('Events:', events.data);

// Query by Move event type
const nftEvents = await client.queryEvents({
  query: {
    MoveEventType: '0xPACKAGE::nft::NFTMinted',
  },
  limit: 50,
});

// Query by sender
const senderEvents = await client.queryEvents({
  query: { Sender: address },
  limit: 100,
});

// Paginate through events
let cursor = null;
while (true) {
  const response = await client.queryEvents({
    query: { MoveEventType: '0xPACKAGE::nft::NFTMinted' },
    cursor,
    limit: 50,
  });

  console.log('Events:', response.data);

  if (!response.hasNextPage) break;
  cursor = response.nextCursor;
}

Subscribing to Events

const unsubscribe = await client.subscribeEvent({
  filter: {
    MoveEventType: '0xPACKAGE::nft::NFTMinted',
  },
  onMessage: (event) => {
    console.log('New event:', event);
  },
});

// Unsubscribe when done
// unsubscribe();

Transaction Utilities

Get transaction details

const txDetails = await client.getTransactionBlock({
  digest: transactionDigest,
  options: {
    showInput: true,
    showEffects: true,
    showEvents: true,
    showObjectChanges: true,
    showBalanceChanges: true,
  },
});

console.log('Transaction:', txDetails);

Multi-get transactions

const transactions = await client.multiGetTransactionBlocks({
  digests: [digest1, digest2, digest3],
  options: {
    showEffects: true,
  },
});

Working with Display

// Get object display
const objectData = await client.getObject({
  id: objectId,
  options: {
    showDisplay: true,
  },
});

if (objectData.data?.display?.data) {
  const display = objectData.data.display.data;
  console.log('Name:', display.name);
  console.log('Image:', display.image_url);
  console.log('Description:', display.description);
}

Error Handling

import { SuiClient } from '@mysten/sui/client';

async function safeTransfer(
  client: SuiClient,
  keypair: Ed25519Keypair,
  recipient: string,
  amount: number
) {
  try {
    const tx = new Transaction();
    tx.transferObjects(
      [tx.splitCoins(tx.gas, [amount])],
      recipient
    );
    tx.setSender(keypair.getPublicKey().toSuiAddress());

    const result = await client.signAndExecuteTransaction({
      transaction: tx,
      signer: keypair,
    });

    if (result.effects?.status?.status !== 'success') {
      throw new Error(`Transaction failed: ${result.effects?.status?.error}`);
    }

    return result.digest;
  } catch (error) {
    console.error('Transfer failed:', error);
    throw error;
  }
}

Best Practices

1. Reuse client instances

// Good: Create once, use everywhere
const client = new SuiClient({ url: getFullnodeUrl('testnet') });

export function getClient() {
  return client;
}

2. Handle transaction failures

const result = await client.signAndExecuteTransaction({
  transaction: tx,
  signer: keypair,
});

if (result.effects?.status?.status === 'success') {
  console.log('Success!');
} else {
  console.error('Failed:', result.effects?.status?.error);
}

3. Use TypeScript types

import { SuiClient } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';

interface TransferParams {
  client: SuiClient;
  signer: Ed25519Keypair;
  recipient: string;
  amount: bigint;
}

async function transfer(params: TransferParams) {
  // Type-safe implementation
}

4. Cache expensive queries

const cache = new Map<string, any>();

async function getCachedObject(objectId: string) {
  if (cache.has(objectId)) {
    return cache.get(objectId);
  }

  const object = await client.getObject({ id: objectId });
  cache.set(objectId, object);
  return object;
}

Integration with React

import { useState, useEffect } from 'react';
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';

function useBalance(address: string) {
  const [balance, setBalance] = useState<string>('0');
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const client = new SuiClient({ url: getFullnodeUrl('testnet') });

    async function fetchBalance() {
      try {
        const result = await client.getBalance({ owner: address });
        setBalance(result.totalBalance);
      } catch (error) {
        console.error('Failed to fetch balance:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchBalance();
  }, [address]);

  return { balance, loading };
}

Next Steps

Build docs developers (and LLMs) love