llms.txt
@mysten/sui v2.0 and a new dApp Kit are here! Check out the migration guide
Mysten Labs SDKs
MigrationsMigrate to 2.0

Migrating from JSON-RPC

Migrate deprecated JSON-RPC code to shared gRPC and GraphQL top-level methods

JSON-RPC APIs are deprecated in the Sui TypeScript SDK. For most application code, migrate from SuiJsonRpcClient to SuiGrpcClient and call the gRPC client's top-level methods. Use SuiGraphQLClient when the code needs custom indexed queries, historical object versions, or custom GraphQL selection sets. Standard transaction and event queries are available directly on both clients.

Create one client for the transport you want to use. Application code should usually call top-level methods like client.getObject() and client.listTransactions(). SDKs and libraries should accept ClientWithCoreApi and call client.core.<method>().

Choosing a target client

ClientUse For
SuiGrpcClientStandard reads, writes, simulations, transaction/event queries, and streams
SuiGraphQLClientThe same shared methods plus custom indexed queries and historical object versions
SuiJsonRpcClientMaintaining legacy JSON-RPC code while migrating

Quick migration to gRPC

Replace SuiJsonRpcClient with SuiGrpcClient:

- import { SuiJsonRpcClient, getJsonRpcFullnodeUrl } from '@mysten/sui/jsonRpc';
+ import { SuiGrpcClient } from '@mysten/sui/grpc';

- const client = new SuiJsonRpcClient({
-   url: getJsonRpcFullnodeUrl('mainnet'),
-   network: 'mainnet',
- });
+ const client = new SuiGrpcClient({
+   baseUrl: 'https://fullnode.mainnet.sui.io:443',
+   network: 'mainnet',
+ });

Full node hosts commonly expose both JSON-RPC and gRPC. When migrating, pass the full node endpoint as baseUrl instead of url, and verify the protocol and port used by your node provider.

Use top-level methods in apps

The gRPC and GraphQL clients expose top-level methods for the common client API. These methods use the same options and response shapes as client.core, with transport-specific additions where the transport can expose more data.

This includes transaction and event methods. Use getTransaction, waitForTransaction, listTransactions, and listEvents directly instead of dropping down to a raw gRPC service or a custom GraphQL query for standard queries.

const { object } = await client.getObject({
	objectId: '0x...',
	include: { content: true, display: true },
});

const { balance } = await client.getBalance({
	owner: '0x...',
});

const result = await client.signAndExecuteTransaction({
	transaction,
	signer,
	include: { effects: true, balanceChanges: true },
});

For SDKs and shared libraries, accept ClientWithCoreApi and use client.core so the caller can provide SuiGrpcClient, SuiGraphQLClient, or a legacy SuiJsonRpcClient during migration:

import type { ClientWithCoreApi } from '@mysten/sui/client';

export async function readObject(client: ClientWithCoreApi, objectId: string) {
	return client.core.getObject({
		objectId,
		include: { content: true },
	});
}

Method replacements

Replace legacy JSON-RPC method names with the gRPC top-level method when one exists. In SDK code, use the same replacement under client.core.

JSON-RPC MethodApp Code Replacement
getObjectgetObject
multiGetObjectsgetObjects
getOwnedObjectslistOwnedObjects for an exact StructType filter
getCoinslistCoins
getAllBalancesPaginate listBalances and map its normalized response
getBalancegetBalance
getCoinMetadatagetCoinMetadata
getDynamicFieldslistDynamicFields
getDynamicFieldObjectgetDynamicField or client.core.getDynamicObjectField
getTransactionBlockgetTransaction
multiGetTransactionBlocksMultiple getTransaction calls
executeTransactionBlockexecuteTransaction
waitForTransactionwaitForTransaction
dryRunTransactionBlocksimulateTransaction
devInspectTransactionBlocksimulateTransaction with the sender set and checksEnabled: false
queryTransactionBlockslistTransactions
queryEventslistEvents
getNormalizedMoveFunctiongetMoveFunction
getMoveFunctionArgTypesNo direct equivalent; getMoveFunction returns normalized signatures
resolveNameServiceAddressnameService.lookupName or GraphQL query
resolveNameServiceNamesNo direct equivalent for listing every name assigned to an address

getMoveFunction exposes normalized parameter signatures, but it does not reproduce the legacy Pure, Object, and object-access classifications from getMoveFunctionArgTypes. Likewise, defaultNameServiceName and raw gRPC nameService.reverseLookupName return only the configured default name; they do not replace the paginated list returned by resolveNameServiceNames. Keep a legacy endpoint or use an application indexer when those exact results are required.

Some composed helpers are currently exposed through client.core rather than as top-level gRPC or GraphQL methods:

const { protocolConfig } = await client.core.getProtocolConfig();
const { systemState } = await client.core.getCurrentSystemState();
const { chainIdentifier } = await client.core.getChainIdentifier();

Object and coin reads

Migrating getOwnedObjects

- const { data } = await jsonRpcClient.getOwnedObjects({
-   owner: '0xabc...',
-   filter: { StructType: '0x2::coin::Coin<0x2::sui::SUI>' },
-   options: { showContent: true },
- });
+ const { objects } = await client.listOwnedObjects({
+   owner: '0xabc...',
+   type: '0x2::coin::Coin<0x2::sui::SUI>',
+   include: { content: true },
+ });

Only the legacy StructType filter maps directly to listOwnedObjects.type. Legacy package, module, owner, object ID, version, and boolean-composition filters do not have top-level Core equivalents. A custom GraphQL ObjectFilter can cover package, module, and owner-kind cases. For the remaining filters, use an indexer or paginate and filter the normalized results in application code.

Migrating getCoins

- const coins = await jsonRpcClient.getCoins({
-   owner: '0xabc...',
-   coinType: '0x2::sui::SUI',
- });
+ const coins = await client.listCoins({
+   owner: '0xabc...',
+   coinType: '0x2::sui::SUI',
+ });

Migrating getAllBalances

Unlike getAllBalances, listBalances is paginated. It also returns normalized balance, coinBalance, and addressBalance fields instead of the legacy CoinBalance shape with coinObjectCount, totalBalance, and lockedBalance. Follow cursor while hasNextPage is true and map the result explicitly if existing code depends on the legacy shape.

Migrating object include options

- const object = await jsonRpcClient.getObject({
-   id: objectId,
-   options: {
-     showBcs: true,
-     showContent: true,
-     showDisplay: true,
-   },
- });
+ const { object } = await client.getObject({
+   objectId,
+   include: {
+     content: true,
+     json: true,
+     display: true,
+   },
+ });

Use include.content for BCS-encoded Move struct bytes. It is the most stable cross-transport shape for parsing application data.

Transaction execution and simulation

Legacy JSON-RPC methods accept serialized transaction blocks as base64 strings. executeTransaction accepts bytes, while simulateTransaction accepts bytes or a Transaction, so decode existing strings before passing them to the new client:

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

Migrating getTransactionBlock

- const result = await jsonRpcClient.getTransactionBlock({
-   digest,
-   options: {
-     showEffects: true,
-     showEvents: true,
-     showInput: true,
-   },
- });
+ const result = await client.getTransaction({
+   digest,
+   include: {
+     effects: true,
+     events: true,
+     transaction: true,
+   },
+ });

Both SuiGrpcClient and SuiGraphQLClient expose getTransaction. SDKs can make the same request through client.core.getTransaction.

For multiple digests, call the same top-level method for each transaction:

- const results = await jsonRpcClient.multiGetTransactionBlocks({
-   digests,
-   options: { showEffects: true },
- });
+ const results = await Promise.all(
+   digests.map((digest) =>
+     client.getTransaction({
+       digest,
+       include: { effects: true },
+     }),
+   ),
+ );

The raw gRPC ledgerService.batchGetTransactions method remains available when an application specifically needs the transport's batch wire API, but it is not required for ordinary client code.

Migrating executeTransactionBlock

- const result = await jsonRpcClient.executeTransactionBlock({
-   transactionBlock: bytes,
-   signature,
-   options: {
-     showEffects: true,
-     showEvents: true,
-   },
- });
+ const result = await client.executeTransaction({
+   transaction: fromBase64(bytes),
+   signatures: [signature],
+   include: {
+     effects: true,
+     events: true,
+   },
+ });

- const status = result.effects?.status.status;
+ const tx = result.Transaction ?? result.FailedTransaction;
+ const success = tx.status.success;

Migrating signAndExecuteTransaction

- const result = await jsonRpcClient.signAndExecuteTransaction({
-   transaction,
-   signer,
-   options: { showEffects: true },
- });
+ const result = await client.signAndExecuteTransaction({
+   transaction,
+   signer,
+   include: { effects: true },
+ });

+ if (result.$kind === 'FailedTransaction') {
+   throw new Error(result.FailedTransaction.status.error?.message ?? 'Transaction failed');
+ }

Migrating waitForTransaction

- const result = await jsonRpcClient.waitForTransaction({
-   digest,
-   options: { showEffects: true },
-   timeout: 60_000,
-   pollInterval: 2_000,
- });
+ const result = await client.waitForTransaction({
+   digest,
+   include: { effects: true },
+   timeout: 60_000,
+   pollSchedule: [0, 2_000],
+ });

waitForTransaction is a top-level method on both SuiGrpcClient and SuiGraphQLClient. It can also accept the result of executeTransaction or signAndExecuteTransaction through its result option.

Migrating dryRunTransactionBlock

- const result = await jsonRpcClient.dryRunTransactionBlock({
-   transactionBlock: tx,
- });
+ const result = await client.simulateTransaction({
+   transaction: fromBase64(tx),
+   include: {
+     effects: true,
+     balanceChanges: true,
+   },
+ });

Migrating devInspectTransactionBlock

- const result = await jsonRpcClient.devInspectTransactionBlock({
-   sender: '0xabc...',
-   transactionBlock: tx,
- });
- const returnValues = result.results?.[0]?.returnValues;
+ const transaction = Transaction.fromKind(tx);
+ transaction.setSender('0xabc...');
+ const result = await client.simulateTransaction({
+   transaction,
+   checksEnabled: false,
+   include: { commandResults: true },
+ });
+ const returnValues = result.commandResults?.[0]?.returnValues;

Transaction and event queries

listTransactions and listEvents are first-class methods on SuiGrpcClient, SuiGraphQLClient, and the Core API. Application code should call the top-level method on its chosen client. Reusable SDK code should call the same method through client.core.

Migrating queryTransactionBlocks

- const result = await jsonRpcClient.queryTransactionBlocks({
-   filter: { FromAddress: '0xabc...' },
-   options: { showEffects: true },
-   limit: 10,
- });
- const digests = result.data.map((tx) => tx.digest);
+ const page = await client.listTransactions({
+   filter: { sender: '0xabc...' },
+   include: { effects: true },
+   limit: 10,
+   order: 'descending',
+ });
+ const digests = page.transactions.map(
+   (tx) => (tx.Transaction ?? tx.FailedTransaction).digest,
+ );

Common transaction filter mappings:

JSON-RPC FiltergRPC/GraphQL Top-Level Filter
FromAddresssender
MoveFunctionfunction
ToAddress, FromOrToAddress, ChangedObject, AffectedObjectUse raw gRPC ledger filters or a custom GraphQL query

The response contains normalized transaction results plus ledger-position cursors:

for (const result of page.transactions) {
	const transaction = result.Transaction ?? result.FailedTransaction;
	console.log(transaction.digest, result.$kind);
}

const nextPage = page.hasNextPage
	? await client.listTransactions({
			filter: { sender: '0xabc...' },
			include: { effects: true },
			before: page.endCursor,
			limit: 10,
		})
	: null;

The legacy JSON-RPC transaction and event queries default to descending order, while listTransactions and listEvents default to ascending order. Pass order: 'descending' when preserving the legacy default.

Migrating queryEvents

- const result = await jsonRpcClient.queryEvents({
-   query: { MoveEventType: '0x2::coin::CoinCreated' },
-   limit: 10,
-   order: 'descending',
- });
+ const result = await client.listEvents({
+   filter: { eventType: '0x2::coin::CoinCreated' },
+   limit: 10,
+   order: 'descending',
+ });
+ for (const event of result.events) {
+   console.log(event.eventType, event.transactionDigest, event.json);
+ }

Common event filter mappings:

JSON-RPC FiltergRPC/GraphQL Top-Level Filter
Sendersender
MoveModuleemitModule: 'package::module'
MoveEventModuleeventType: 'package::module'
MoveEventTypeeventType: 'package::module::Event'

The top-level query methods handle pagination and cursor normalization. For richer filters, such as combined predicates, affected addresses, affected objects, or checkpoint ranges, use the raw ledgerService on SuiGrpcClient or a custom GraphQL query.

Use after: result.endCursor to continue an ascending query and before: result.endCursor to continue a descending query. startCursor identifies the first item in a page and can be used with after to poll for newer transactions or events.

Native gRPC replacements

Some JSON-RPC methods map to gRPC service clients rather than top-level methods:

JSON-RPC MethodgRPC Replacement
getCheckpointledgerService.getCheckpoint
getCheckpointsledgerService.listCheckpoints
getLatestCheckpointSequenceNumberledgerService.getServiceInfo and read checkpointHeight
getCurrentEpochledgerService.getEpoch with no epoch argument
getCommitteeInfoledgerService.getEpoch with committee in the read mask
getLatestSuiSystemStateclient.core.getCurrentSystemState or ledgerService.getEpoch
getProtocolConfigclient.core.getProtocolConfig or ledgerService.getEpoch
getTotalSupplystateService.getCoinInfo and read treasury.totalSupply
getNormalizedMoveModulemovePackageService.getPackage
getNormalizedMoveModulesByPackagemovePackageService.getPackage
getNormalizedMoveStructmovePackageService.getDatatype
import { SuiGrpcClient } from '@mysten/sui/grpc';

const client = new SuiGrpcClient({
	baseUrl: 'https://fullnode.mainnet.sui.io:443',
	network: 'mainnet',
});

const { response: info } = await client.ledgerService.getServiceInfo({});
const latestCheckpoint = info.checkpointHeight;

if (latestCheckpoint == null) {
	throw new Error('The server did not return a checkpoint height');
}

const { response } = await client.ledgerService.getCheckpoint({
	checkpointId: { oneofKind: 'sequenceNumber', sequenceNumber: latestCheckpoint },
	readMask: { paths: ['sequence_number', 'digest', 'summary.timestamp'] },
});

console.log(response.checkpoint?.sequenceNumber, response.checkpoint?.summary?.timestamp);

Subscriptions

Replace deprecated JSON-RPC websocket subscriptions with the gRPC subscriptionService:

JSON-RPC MethodgRPC Service Replacement
subscribeTransactionsubscriptionService.subscribeTransactions
subscribeEventsubscriptionService.subscribeEvents
const stream = client.subscriptionService.subscribeEvents({
	filter: {
		terms: [
			{
				literals: [
					{
						negated: false,
						predicate: {
							oneofKind: 'eventType',
							eventType: { eventType: '0x2::coin::CoinCreated' },
						},
					},
				],
			},
		],
	},
	readMask: { paths: ['event_type', 'contents', 'json', 'checkpoint', 'transaction_digest'] },
});

for await (const frame of stream.responses) {
	if (frame.event) {
		console.log(frame.event.eventType, frame.event.json);
	}
}

Subscriptions begin at the current tip of the chain and do not resume automatically. For gap recovery, retain the last frame.watermark.cursor, open the new subscription to establish its first-frame position, and replay with the paired raw client.ledgerService.listEvents() call using the same protobuf filter and options.after cursor. Repeat the raw list call as the index advances until it reaches the new subscription's start position. Do not pass the subscription filter or cursor to top-level client.listEvents(): its Core filter and base64 cursor are different types.

When to use GraphQL

Use SuiGraphQLClient when the replacement needs a custom indexed query, a historical object version, or a custom selection set. Standard transaction and event history does not require a custom GraphQL query; call graphqlClient.listTransactions() or graphqlClient.listEvents() directly.

JSON-RPC MethodAlternative
getEpochsGraphQL epochs query
tryGetPastObjectGraphQL object(address:, version:) query
getStakesNo current gRPC/Core/GraphQL equivalent; use a staking indexer
getStakesByIdsNo current gRPC/Core/GraphQL equivalent; use a staking indexer
getNetworkMetricsUse an indexer or analytics-specific GraphQL schema
getAddressMetricsUse an indexer or analytics-specific GraphQL schema
getMoveCallMetricsUse an indexer or analytics-specific GraphQL schema
import { SuiGraphQLClient } from '@mysten/sui/graphql';
import { graphql } from '@mysten/sui/graphql/schema';

const graphqlClient = new SuiGraphQLClient({
	url: 'https://sui-mainnet.mystenlabs.com/graphql',
	network: 'mainnet',
});

const historicalObjectQuery = graphql(`
	query GetObjectAtVersion($id: SuiAddress!, $version: UInt53!) {
		object(address: $id, version: $version) {
			address
			version
			digest
			asMoveObject {
				contents {
					type {
						repr
					}
					bcs
				}
			}
		}
	}
`);

const result = await graphqlClient.query({
	query: historicalObjectQuery,
	variables: {
		id: '0x123...',
		version: 42,
	},
});

Validator APY

There is no direct SDK replacement for getValidatorsApy. There is no canonical definition of validator APY, so compute the metric from validator staking-pool exchange rates or use an application-specific indexer.

Two reference implementations:

Treat whichever formula you adopt as a definition of validator APY, not the definition.

Response format differences

gRPC and GraphQL top-level methods return the Core API response format, which differs from legacy JSON-RPC response shapes.

// Transaction result access
- const status = result.effects?.status?.status;
+ const tx = result.Transaction ?? result.FailedTransaction;
+ const success = tx.status.success;

// Include options
- { showEffects: true, showEvents: true }
+ { effects: true, events: true }

See the @mysten/sui migration guide for transaction executor response changes.

Client extensions

Client extensions work with SuiGrpcClient and any client that implements ClientWithCoreApi:

import { deepbook } from '@mysten/deepbook-v3';
import { suins } from '@mysten/suins';
import { SuiGrpcClient } from '@mysten/sui/grpc';

const client = new SuiGrpcClient({
	baseUrl: 'https://fullnode.mainnet.sui.io:443',
	network: 'mainnet',
}).$extend(deepbook({ address: myAddress }), suins());

await client.deepbook.checkManagerBalance(manager, asset);
await client.suins.getNameRecord('example.sui');

See also

On this page