Migrating from JSON-RPC
Migrate from JSON-RPC to the new Core API using SuiGrpcClient or SuiGraphQLClient.
This guide covers migrating from SuiJsonRpcClient to the new client APIs. The JSON-RPC API is
being deprecated in favor of SuiGrpcClient and SuiGraphQLClient.
We recommend using SuiGrpcClient for most operations and SuiGraphQLClient for complex queries
like filtering transactions and events.
Choosing a client
| Client | Best For |
|---|---|
SuiGrpcClient | Most operations, SDK integrations, real-time data |
SuiGraphQLClient | Complex queries, filtering transactions/events, historical data |
Quick migration to gRPC
For most use cases, migrate to 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',
+ });Both clients use the same full node URLs, so you can use the same endpoint when migrating.
Core API methods
The gRPC client should work with almost all mysten SDKs as a drop in replacement for the JSON-RPC client. When using the client directly, the methods and data returned will not be exactly the same as what was available in JSON-RPC.
Methods replaced by core API
These JSON-RPC methods have direct replacements in the core API:
| JSON-RPC Method | Core API Replacement |
|---|---|
getCoins | listCoins |
getAllCoins | listOwnedObjects with type: '0x2::coin::Coin' |
getAllBalances | listBalances |
getOwnedObjects | listOwnedObjects |
multiGetObjects | getObjects |
getDynamicFields | listDynamicFields |
getDynamicFieldObject | getDynamicField |
devInspectTransactionBlock | simulateTransaction with checksEnabled: false |
dryRunTransactionBlock | simulateTransaction |
getNormalizedMoveFunction | getMoveFunction |
getMoveFunctionArgTypes | getMoveFunction |
Example: Migrating devInspectTransactionBlock
- const result = await jsonRpcClient.devInspectTransactionBlock({
- sender: '0xabc...',
- transactionBlock: tx,
- });
- const returnValues = result.results?.[0]?.returnValues;
+ const result = await client.core.simulateTransaction({
+ transaction: tx,
+ checksEnabled: false,
+ include: { commandResults: true },
+ });
+ const returnValues = result.commandResults?.[0]?.returnValues;Example: Migrating getOwnedObjects
- const { data } = await jsonRpcClient.getOwnedObjects({
- owner: '0xabc...',
- options: { showContent: true },
- });
+ const { objects } = await grpcClient.listOwnedObjects({
+ owner: '0xabc...',
+ include: { content: true },
+ });Methods replaced by gRPC services
These JSON-RPC methods can be replaced by calling gRPC service clients directly:
| JSON-RPC Method | gRPC Service Replacement |
|---|---|
getCheckpoint | ledgerService.getCheckpoint |
getLatestCheckpointSequenceNumber | ledgerService.getServiceInfo (read checkpointHeight) |
getCurrentEpoch | ledgerService.getEpoch (omit epoch for the current) |
getLatestSuiSystemState | ledgerService.getEpoch with system_state in the read mask |
getCommitteeInfo | ledgerService.getEpoch with committee in the read mask |
getProtocolConfig | ledgerService.getEpoch with protocol_config in the read mask |
getCoinMetadata | stateService.getCoinInfo (response includes metadata) |
getTotalSupply | stateService.getCoinInfo (response includes treasury) |
getNormalizedMoveModule | movePackageService.getPackage (response includes all modules) |
getNormalizedMoveModulesByPackage | movePackageService.getPackage |
getNormalizedMoveStruct | movePackageService.getDatatype |
resolveNameServiceAddress | nameService.lookupName |
resolveNameServiceNames | nameService.reverseLookupName |
getCheckpoints and getEpochs (paginated listings) have no gRPC equivalent — use the
checkpoints / epochs GraphQL queries instead. getValidatorsApy also has no replacement:
there is no canonical definition of validator APY, so it must be computed client-side from
validator exchange rates (see Validator APY below).
Example: using gRPC service clients
import { SuiGrpcClient } from '@mysten/sui/grpc';
const client = new SuiGrpcClient({
baseUrl: 'https://fullnode.mainnet.sui.io:443',
network: 'mainnet',
});
// Get the latest checkpoint sequence number (replaces getLatestCheckpointSequenceNumber)
const { response: info } = await client.ledgerService.getServiceInfo({});
const latestCheckpoint = info.checkpointHeight;
// Get a specific checkpoint by sequence number. `checkpointId` is required (a oneof);
// the read mask defaults to `sequence_number,digest`, so request more fields as needed.
const { response } = await client.ledgerService.getCheckpoint({
checkpointId: { oneofKind: 'sequenceNumber', sequenceNumber: latestCheckpoint },
});
// Get the current epoch (omit `epoch` for the current one). The system state,
// committee, and protocol config are NOT returned by default — you must request
// them via the read mask (the default mask is just `epoch`).
const { response: epoch } = await client.ledgerService.getEpoch({
readMask: { paths: ['epoch', 'system_state', 'committee', 'protocol_config'] },
});
const systemState = epoch.epoch?.systemState;
const committee = epoch.epoch?.committee;
const protocolConfig = epoch.epoch?.protocolConfig;
// Get coin metadata and total supply in one call
const { response: coinInfo } = await client.stateService.getCoinInfo({
coinType: '0x2::sui::SUI',
});
const metadata = coinInfo.metadata;
const totalSupply = coinInfo.treasury?.totalSupply;
// Get Move package information (includes all modules)
const { response: pkg } = await client.movePackageService.getPackage({
packageId: '0x2',
});
// Get a specific Move datatype (struct or enum)
const { response: datatype } = await client.movePackageService.getDatatype({
packageId: '0x2',
moduleName: 'coin',
name: 'Coin',
});
// Resolve SuiNS name
const { response: address } = await client.nameService.lookupName({
name: 'example.sui',
});Methods requiring GraphQL
Some JSON-RPC methods don't have gRPC equivalents and require using SuiGraphQLClient instead:
| JSON-RPC Method | GraphQL Alternative |
|---|---|
queryTransactionBlocks | transactions query |
multiGetTransactionBlocks | multiGetTransactionEffects query |
queryEvents | events query |
getCheckpoints | checkpoints query |
getEpochs | epochs query |
getCoinMetadata | coinMetadata query (or gRPC stateService.getCoinInfo) |
getTotalSupply | coinMetadata query (or gRPC stateService.getCoinInfo) |
getStakes | address.stakedSuis query |
getStakesByIds | multiGetObjects query |
tryGetPastObject | Historical object queries |
getNetworkMetrics | Use indexer |
getAddressMetrics | Use indexer |
getMoveCallMetrics | Use indexer |
Setting up GraphQL client
import { SuiGraphQLClient } from '@mysten/sui/graphql';
const graphqlClient = new SuiGraphQLClient({
url: 'https://sui-mainnet.mystenlabs.com/graphql',
network: 'mainnet',
});Querying transactions
Replace queryTransactionBlocks with a GraphQL query:
const result = await graphqlClient.query({
query: `
query QueryTransactions($sender: SuiAddress, $first: Int, $after: String) {
transactions(
first: $first
after: $after
filter: { sentAddress: $sender }
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
digest
effects {
status
epoch { epochId }
}
}
}
}
`,
variables: {
sender: '0xabc...',
first: 10,
},
});Available transaction filters:
sentAddress: Filter by sender addressaffectedAddress: Filter by any address involved in the transactionaffectedObject: Filter by object ID that was affectedfunction: Filter by Move function called (for example,0x2::coin::transfer)kind: Filter by transaction kind (SYSTEMorPROGRAMMABLE)atCheckpoint/beforeCheckpoint/afterCheckpoint- Filter by checkpoint
Querying events
Replace queryEvents with a GraphQL query:
const result = await graphqlClient.query({
query: `
query QueryEvents($type: String, $first: Int, $after: String) {
events(
first: $first
after: $after
filter: { type: $type }
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
transactionModule {
package { address }
name
}
sender { address }
contents {
type { repr }
bcs
}
}
}
}
`,
variables: {
type: '0x2::coin::CoinCreated',
first: 10,
},
});Available event filters:
type: Filter by event type (package, package::module, or full type)module: Filter by emitting modulesender: Filter by transaction senderatCheckpoint/beforeCheckpoint/afterCheckpoint: Filter by checkpoint
Fetching multiple transactions
Replace multiGetTransactionBlocks with a GraphQL query:
const result = await graphqlClient.query({
query: `
query MultiGetTransactions($digests: [String!]!) {
multiGetTransactionEffects(keys: $digests) {
transaction {
digest
transactionBcs
}
status
epoch { epochId }
}
}
`,
variables: {
digests: ['digest1', 'digest2', 'digest3'],
},
});Querying historical objects
Replace tryGetPastObject with a GraphQL query specifying a version:
const result = await graphqlClient.query({
query: `
query GetObjectAtVersion($id: SuiAddress!, $version: UInt53!) {
object(address: $id, version: $version) {
address
version
digest
asMoveObject {
contents {
type { repr }
bcs
}
}
}
}
`,
variables: {
id: '0x123...',
version: 42,
},
});Querying coin metadata
Replace getCoinMetadata and getTotalSupply with a GraphQL query:
const result = await graphqlClient.query({
query: `
query GetCoinMetadata($coinType: String!) {
coinMetadata(coinType: $coinType) {
name
symbol
description
decimals
iconUrl
supply
}
}
`,
variables: {
coinType: '0x2::sui::SUI',
},
});Querying staked SUI
Replace getStakes with a GraphQL query:
const result = await graphqlClient.query({
query: `
query GetStakes($owner: SuiAddress!) {
address(address: $owner) {
stakedSuis {
nodes {
principal
stakeActivationEpoch
estimatedReward
contents {
bcs
}
}
}
}
}
`,
variables: {
owner: '0xabc...',
},
});Validator APY
There is no replacement for getValidatorsApy, and one will not be added. There is no canonical
definition of validator APY — no other chain's core/general-purpose RPCs offer this endpoint — so it
must be computed client-side from validator staking-pool exchange rates.
The general approach: read each validator's exchangeRates (a table of pool_token_amount /
sui_amount per epoch) and estimate the annualized rate from the change in exchange rate over the
trailing epochs. Two concrete reference implementations:
- The
jsonrpc-altimplementation insui-indexer-alt-jsonrpc, which is compact and readable. - This GraphQL approach, which fetches the precursor exchange-rate information in a single query.
Treat whichever formula you adopt as a definition of validator APY, not the definition.
Response format differences
The gRPC client uses the core API response format, which differs from JSON-RPC responses. See the
@mysten/sui migration guide
for details on the new response format.
Key differences:
// Transaction result access
- const status = result.effects?.status?.status;
+ const tx = result.Transaction ?? result.FailedTransaction;
+ const status = tx.effects.status.success;
// Include options
- { showEffects: true, showEvents: true }
+ { effects: true, events: true }Client extensions
Client extensions work the same way with both clients:
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { deepbook } from '@mysten/deepbook-v3';
import { suins } from '@mysten/suins';
const client = new SuiGrpcClient({
baseUrl: 'https://fullnode.mainnet.sui.io:443',
network: 'mainnet',
}).$extend(deepbook({ address: myAddress }), suins());
// Use extended functionality
await client.deepbook.checkManagerBalance(manager, asset);
await client.suins.getName('0xabc...');See also
- SuiGrpcClient Documentation - Full gRPC client documentation
- SuiGraphQLClient Documentation - GraphQL client documentation
- Core API - Transport-agnostic API methods
- gRPC Overview - Sui gRPC API documentation