@mysten/sui 2.23.2 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +9 -9
  3. package/dist/bcs/bcs.d.mts +6 -6
  4. package/dist/client/core.d.mts +1 -0
  5. package/dist/client/core.d.mts.map +1 -1
  6. package/dist/client/core.mjs +4 -1
  7. package/dist/client/core.mjs.map +1 -1
  8. package/dist/client/mvr.d.mts.map +1 -1
  9. package/dist/client/mvr.mjs +1 -0
  10. package/dist/client/mvr.mjs.map +1 -1
  11. package/dist/client/types.d.mts +7 -0
  12. package/dist/client/types.d.mts.map +1 -1
  13. package/dist/cryptography/signature.d.mts +6 -6
  14. package/dist/graphql/client.d.mts +6 -1
  15. package/dist/graphql/client.d.mts.map +1 -1
  16. package/dist/graphql/client.mjs +18 -2
  17. package/dist/graphql/client.mjs.map +1 -1
  18. package/dist/graphql/core.d.mts +5 -4
  19. package/dist/graphql/core.d.mts.map +1 -1
  20. package/dist/graphql/core.mjs +60 -14
  21. package/dist/graphql/core.mjs.map +1 -1
  22. package/dist/graphql/generated/queries.d.mts.map +1 -1
  23. package/dist/graphql/generated/queries.mjs +8 -1
  24. package/dist/graphql/generated/queries.mjs.map +1 -1
  25. package/dist/graphql/generated/tada-env.d.mts +16 -0
  26. package/dist/grpc/client.d.mts +6 -1
  27. package/dist/grpc/client.d.mts.map +1 -1
  28. package/dist/grpc/client.mjs +17 -2
  29. package/dist/grpc/client.mjs.map +1 -1
  30. package/dist/grpc/core.d.mts +1 -0
  31. package/dist/grpc/core.d.mts.map +1 -1
  32. package/dist/grpc/core.mjs +20 -0
  33. package/dist/grpc/core.mjs.map +1 -1
  34. package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
  35. package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
  36. package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
  37. package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
  38. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +4 -4
  39. package/dist/jsonRpc/client.d.mts.map +1 -1
  40. package/dist/jsonRpc/client.mjs +60 -15
  41. package/dist/jsonRpc/client.mjs.map +1 -1
  42. package/dist/jsonRpc/core.d.mts +6 -1
  43. package/dist/jsonRpc/core.d.mts.map +1 -1
  44. package/dist/jsonRpc/core.mjs +25 -7
  45. package/dist/jsonRpc/core.mjs.map +1 -1
  46. package/dist/version.mjs +1 -1
  47. package/dist/version.mjs.map +1 -1
  48. package/dist/zklogin/bcs.d.mts +14 -14
  49. package/docs/bcs.md +2 -2
  50. package/docs/clients/core.md +151 -690
  51. package/docs/clients/executing.md +113 -0
  52. package/docs/clients/graphql.md +91 -46
  53. package/docs/clients/grpc.md +239 -142
  54. package/docs/clients/index.md +87 -51
  55. package/docs/clients/querying.md +539 -0
  56. package/docs/executors.md +28 -13
  57. package/docs/index.md +1 -1
  58. package/docs/llms-index.md +18 -17
  59. package/docs/migrations/sui-2.0/dapp-kit.md +3 -3
  60. package/docs/migrations/sui-2.0/deepbook-v3.md +2 -2
  61. package/docs/migrations/sui-2.0/index.md +46 -25
  62. package/docs/migrations/sui-2.0/json-rpc-migration.md +462 -284
  63. package/docs/migrations/sui-2.0/kiosk.md +10 -9
  64. package/docs/migrations/sui-2.0/sdk-maintainers.md +17 -11
  65. package/docs/migrations/sui-2.0/sui.md +47 -29
  66. package/docs/migrations/sui-2.0/suins.md +2 -2
  67. package/docs/migrations/sui-2.0/walrus.md +3 -3
  68. package/docs/migrations/sui-2.0/zksend.md +9 -8
  69. package/docs/sdk-building.md +7 -5
  70. package/docs/transactions/signing-and-execution.md +8 -28
  71. package/package.json +1 -1
  72. package/src/client/core.ts +5 -0
  73. package/src/client/mvr.ts +6 -0
  74. package/src/client/types.ts +11 -0
  75. package/src/graphql/client.ts +35 -2
  76. package/src/graphql/core.ts +57 -10
  77. package/src/graphql/generated/queries.ts +14 -0
  78. package/src/graphql/generated/schema.graphql +11 -1
  79. package/src/graphql/generated/tada-env.ts +20 -0
  80. package/src/graphql/queries/nameService.graphql +6 -0
  81. package/src/grpc/client.ts +34 -2
  82. package/src/grpc/core.ts +35 -0
  83. package/src/jsonRpc/client.ts +15 -0
  84. package/src/jsonRpc/core.ts +31 -6
  85. package/src/version.ts +1 -1
  86. package/docs/clients/json-rpc.md +0 -239
@@ -1,24 +1,28 @@
1
1
  # Migrating from JSON-RPC
2
2
 
3
- > Migrate from JSON-RPC to the new Core API using SuiGrpcClient or SuiGraphQLClient.
3
+ > Migrate deprecated JSON-RPC code to shared gRPC and GraphQL top-level methods
4
4
 
5
- This guide covers migrating from `SuiJsonRpcClient` to the new client APIs. The JSON-RPC API is
6
- being deprecated in favor of `SuiGrpcClient` and `SuiGraphQLClient`.
5
+ JSON-RPC APIs are deprecated in the Sui TypeScript SDK. For most application code, migrate from
6
+ `SuiJsonRpcClient` to [`SuiGrpcClient`](/sui/clients/grpc) and call the gRPC client's top-level
7
+ methods. Use [`SuiGraphQLClient`](/sui/clients/graphql) when the code needs custom indexed queries,
8
+ historical object versions, or custom GraphQL selection sets. Standard transaction and event queries
9
+ are available directly on both clients.
7
10
 
8
- > **Note:** Use `SuiGrpcClient` for most operations, including filtering transactions and events with the core
9
- > query API. `SuiGraphQLClient` remains useful for complex queries that aren't covered by the core
10
- > API.
11
+ > **Note:** Create one client for the transport you want to use. Application code should usually call
12
+ > top-level methods like `client.getObject()` and `client.listTransactions()`. SDKs and libraries
13
+ > should accept `ClientWithCoreApi` and call `client.core.<method>()`.
11
14
 
12
- ## Choosing a client
15
+ ## Choosing a target client
13
16
 
14
- | Client | Best For |
15
- | ------------------ | -------------------------------------------------------------------- |
16
- | `SuiGrpcClient` | Most operations, SDK integrations, queries, real-time data |
17
- | `SuiGraphQLClient` | Complex queries not covered by the core API, like historical objects |
17
+ | Client | Use For |
18
+ | ------------------ | ---------------------------------------------------------------------------------- |
19
+ | `SuiGrpcClient` | Standard reads, writes, simulations, transaction/event queries, and streams |
20
+ | `SuiGraphQLClient` | The same shared methods plus custom indexed queries and historical object versions |
21
+ | `SuiJsonRpcClient` | Maintaining legacy JSON-RPC code while migrating |
18
22
 
19
23
  ## Quick migration to gRPC
20
24
 
21
- For most use cases, migrate to `SuiGrpcClient`:
25
+ Replace `SuiJsonRpcClient` with `SuiGrpcClient`:
22
26
 
23
27
  ```diff
24
28
  - import { SuiJsonRpcClient, getJsonRpcFullnodeUrl } from '@mysten/sui/jsonRpc';
@@ -34,85 +38,300 @@ For most use cases, migrate to `SuiGrpcClient`:
34
38
  + });
35
39
  ```
36
40
 
37
- Both clients use the same full node URLs, so you can use the same endpoint when migrating.
41
+ Full node hosts commonly expose both JSON-RPC and gRPC. When migrating, pass the full node endpoint
42
+ as `baseUrl` instead of `url`, and verify the protocol and port used by your node provider.
38
43
 
39
- ## Core API methods
44
+ ## Use top-level methods in apps
40
45
 
41
- The gRPC client should work with almost all mysten SDKs as a drop in replacement for the JSON-RPC
42
- client. When using the client directly, the methods and data returned will not be exactly the same
43
- as what was available in JSON-RPC.
46
+ The gRPC and GraphQL clients expose top-level methods for the common client API. These methods use
47
+ the same options and response shapes as `client.core`, with transport-specific additions where the
48
+ transport can expose more data.
44
49
 
45
- ## Methods replaced by core API
50
+ This includes transaction and event methods. Use `getTransaction`, `waitForTransaction`,
51
+ `listTransactions`, and `listEvents` directly instead of dropping down to a raw gRPC service or a
52
+ custom GraphQL query for standard queries.
46
53
 
47
- These JSON-RPC methods have direct replacements in the core API:
54
+ ```typescript
55
+ const { object } = await client.getObject({
56
+ objectId: '0x...',
57
+ include: { content: true, display: true },
58
+ });
48
59
 
49
- | JSON-RPC Method | Core API Replacement |
50
- | ---------------------------- | ------------------------------------------------- |
51
- | `getCoins` | `listCoins` |
52
- | `getAllCoins` | `listOwnedObjects` with `type: '0x2::coin::Coin'` |
53
- | `getAllBalances` | `listBalances` |
54
- | `getOwnedObjects` | `listOwnedObjects` |
55
- | `multiGetObjects` | `getObjects` |
56
- | `getDynamicFields` | `listDynamicFields` |
57
- | `getDynamicFieldObject` | `getDynamicField` |
58
- | `devInspectTransactionBlock` | `simulateTransaction` with `checksEnabled: false` |
59
- | `dryRunTransactionBlock` | `simulateTransaction` |
60
- | `getNormalizedMoveFunction` | `getMoveFunction` |
61
- | `getMoveFunctionArgTypes` | `getMoveFunction` |
62
- | `queryTransactionBlocks` | `listTransactions` |
63
- | `queryEvents` | `listEvents` |
60
+ const { balance } = await client.getBalance({
61
+ owner: '0x...',
62
+ });
64
63
 
65
- > **Note:** The query methods (`listTransactions` and `listEvents`) behave identically on every client,
66
- > including `SuiJsonRpcClient`. See [Core API query methods](/sui/clients/core#query-methods) for
67
- > details.
64
+ const result = await client.signAndExecuteTransaction({
65
+ transaction,
66
+ signer,
67
+ include: { effects: true, balanceChanges: true },
68
+ });
69
+ ```
70
+
71
+ For SDKs and shared libraries, accept `ClientWithCoreApi` and use `client.core` so the caller can
72
+ provide `SuiGrpcClient`, `SuiGraphQLClient`, or a legacy `SuiJsonRpcClient` during migration:
68
73
 
69
- ### Example: Migrating queryTransactionBlocks
74
+ ```typescript
75
+
76
+ return client.core.getObject({
77
+ objectId,
78
+ include: { content: true },
79
+ });
80
+ }
81
+ ```
82
+
83
+ ## Method replacements
84
+
85
+ Replace legacy JSON-RPC method names with the gRPC top-level method when one exists. In SDK code,
86
+ use the same replacement under `client.core`.
87
+
88
+ | JSON-RPC Method | App Code Replacement |
89
+ | ---------------------------- | --------------------------------------------------------------------- |
90
+ | `getObject` | `getObject` |
91
+ | `multiGetObjects` | `getObjects` |
92
+ | `getOwnedObjects` | `listOwnedObjects` for an exact `StructType` filter |
93
+ | `getCoins` | `listCoins` |
94
+ | `getAllBalances` | Paginate `listBalances` and map its normalized response |
95
+ | `getBalance` | `getBalance` |
96
+ | `getCoinMetadata` | `getCoinMetadata` |
97
+ | `getDynamicFields` | `listDynamicFields` |
98
+ | `getDynamicFieldObject` | `getDynamicField` or `client.core.getDynamicObjectField` |
99
+ | `getTransactionBlock` | `getTransaction` |
100
+ | `multiGetTransactionBlocks` | Multiple `getTransaction` calls |
101
+ | `executeTransactionBlock` | `executeTransaction` |
102
+ | `waitForTransaction` | `waitForTransaction` |
103
+ | `dryRunTransactionBlock` | `simulateTransaction` |
104
+ | `devInspectTransactionBlock` | `simulateTransaction` with the sender set and `checksEnabled: false` |
105
+ | `queryTransactionBlocks` | `listTransactions` |
106
+ | `queryEvents` | `listEvents` |
107
+ | `getNormalizedMoveFunction` | `getMoveFunction` |
108
+ | `getMoveFunctionArgTypes` | No direct equivalent; `getMoveFunction` returns normalized signatures |
109
+ | `resolveNameServiceAddress` | `resolveNameServiceAddress` (returns `{ address }`) |
110
+ | `resolveNameServiceNames` | No direct equivalent for listing every name assigned to an address |
111
+
112
+ `getMoveFunction` exposes normalized parameter signatures, but it does not reproduce the legacy
113
+ `Pure`, `Object`, and object-access classifications from `getMoveFunctionArgTypes`. Likewise,
114
+ `defaultNameServiceName` and raw gRPC `nameService.reverseLookupName` return only the configured
115
+ default name; they do not replace the paginated list returned by `resolveNameServiceNames`. Keep a
116
+ legacy endpoint or use an application indexer when those exact results are required.
117
+
118
+ ```typescript
119
+ const { address } = await client.resolveNameServiceAddress({
120
+ name: 'example.sui',
121
+ });
122
+ ```
123
+
124
+ Some composed helpers are currently exposed through `client.core` rather than as top-level gRPC or
125
+ GraphQL methods:
126
+
127
+ ```typescript
128
+ const { protocolConfig } = await client.core.getProtocolConfig();
129
+ const { systemState } = await client.core.getCurrentSystemState();
130
+ const { chainIdentifier } = await client.core.getChainIdentifier();
131
+ ```
132
+
133
+ ## Object and coin reads
134
+
135
+ ### Migrating `getOwnedObjects`
70
136
 
71
137
  ```diff
72
- - const result = await jsonRpcClient.queryTransactionBlocks({
73
- - filter: { FromAddress: '0xabc...' },
74
- - options: { showEffects: true },
75
- - limit: 10,
138
+ - const { data } = await jsonRpcClient.getOwnedObjects({
139
+ - owner: '0xabc...',
140
+ - filter: { StructType: '0x2::coin::Coin<0x2::sui::SUI>' },
141
+ - options: { showContent: true },
76
142
  - });
77
- - const digests = result.data.map((tx) => tx.digest);
78
- + const result = await client.core.listTransactions({
79
- + filter: { sender: '0xabc...' },
80
- + include: { effects: true },
81
- + limit: 10,
143
+ + const { objects } = await client.listOwnedObjects({
144
+ + owner: '0xabc...',
145
+ + type: '0x2::coin::Coin<0x2::sui::SUI>',
146
+ + include: { content: true },
82
147
  + });
83
- + const digests = result.transactions.map(
84
- + (tx) => (tx.Transaction ?? tx.FailedTransaction).digest,
148
+ ```
149
+
150
+ Only the legacy `StructType` filter maps directly to `listOwnedObjects.type`. Legacy package,
151
+ module, owner, object ID, version, and boolean-composition filters do not have top-level Core
152
+ equivalents. A custom GraphQL `ObjectFilter` can cover package, module, and owner-kind cases. For
153
+ the remaining filters, use an indexer or paginate and filter the normalized results in application
154
+ code.
155
+
156
+ ### Migrating `getCoins`
157
+
158
+ ```diff
159
+ - const coins = await jsonRpcClient.getCoins({
160
+ - owner: '0xabc...',
161
+ - coinType: '0x2::sui::SUI',
162
+ - });
163
+ + const coins = await client.listCoins({
164
+ + owner: '0xabc...',
165
+ + coinType: '0x2::sui::SUI',
166
+ + });
167
+ ```
168
+
169
+ ### Migrating `getAllBalances`
170
+
171
+ Unlike `getAllBalances`, `listBalances` is paginated. It also returns normalized `balance`,
172
+ `coinBalance`, and `addressBalance` fields instead of the legacy `CoinBalance` shape with
173
+ `coinObjectCount`, `totalBalance`, and `lockedBalance`. Follow `cursor` while `hasNextPage` is true
174
+ and map the result explicitly if existing code depends on the legacy shape.
175
+
176
+ ### Migrating object include options
177
+
178
+ ```diff
179
+ - const object = await jsonRpcClient.getObject({
180
+ - id: objectId,
181
+ - options: {
182
+ - showBcs: true,
183
+ - showContent: true,
184
+ - showDisplay: true,
185
+ - },
186
+ - });
187
+ + const { object } = await client.getObject({
188
+ + objectId,
189
+ + include: {
190
+ + content: true,
191
+ + json: true,
192
+ + display: true,
193
+ + },
194
+ + });
195
+ ```
196
+
197
+ Use `include.content` for BCS-encoded Move struct bytes. It is the most stable cross-transport shape
198
+ for parsing application data.
199
+
200
+ ## Transaction execution and simulation
201
+
202
+ Legacy JSON-RPC methods accept serialized transaction blocks as base64 strings. `executeTransaction`
203
+ accepts bytes, while `simulateTransaction` accepts bytes or a `Transaction`, so decode existing
204
+ strings before passing them to the new client:
205
+
206
+ ```typescript
207
+
208
+ ```
209
+
210
+ ### Migrating `getTransactionBlock`
211
+
212
+ ```diff
213
+ - const result = await jsonRpcClient.getTransactionBlock({
214
+ - digest,
215
+ - options: {
216
+ - showEffects: true,
217
+ - showEvents: true,
218
+ - showInput: true,
219
+ - },
220
+ - });
221
+ + const result = await client.getTransaction({
222
+ + digest,
223
+ + include: {
224
+ + effects: true,
225
+ + events: true,
226
+ + transaction: true,
227
+ + },
228
+ + });
229
+ ```
230
+
231
+ Both `SuiGrpcClient` and `SuiGraphQLClient` expose `getTransaction`. SDKs can make the same request
232
+ through `client.core.getTransaction`.
233
+
234
+ For multiple digests, call the same top-level method for each transaction:
235
+
236
+ ```diff
237
+ - const results = await jsonRpcClient.multiGetTransactionBlocks({
238
+ - digests,
239
+ - options: { showEffects: true },
240
+ - });
241
+ + const results = await Promise.all(
242
+ + digests.map((digest) =>
243
+ + client.getTransaction({
244
+ + digest,
245
+ + include: { effects: true },
246
+ + }),
247
+ + ),
85
248
  + );
86
249
  ```
87
250
 
88
- The core API filters map to the JSON-RPC `TransactionFilter` variants:
251
+ The raw gRPC `ledgerService.batchGetTransactions` method remains available when an application
252
+ specifically needs the transport's batch wire API, but it is not required for ordinary client code.
89
253
 
90
- | JSON-RPC filter | Core API filter predicate |
91
- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
92
- | `FromAddress` | `sender` |
93
- | `MoveFunction` | `function` |
94
- | `ToAddress` / `FromOrToAddress` / `ChangedObject` / `AffectedObject` | No core equivalent. Use raw [gRPC](/sui/clients/grpc#ledger-service) or [GraphQL](/sui/clients/graphql) queries |
254
+ ### Migrating `executeTransactionBlock`
95
255
 
96
- ### Example: Migrating queryEvents
256
+ ```diff
257
+ - const result = await jsonRpcClient.executeTransactionBlock({
258
+ - transactionBlock: bytes,
259
+ - signature,
260
+ - options: {
261
+ - showEffects: true,
262
+ - showEvents: true,
263
+ - },
264
+ - });
265
+ + const result = await client.executeTransaction({
266
+ + transaction: fromBase64(bytes),
267
+ + signatures: [signature],
268
+ + include: {
269
+ + effects: true,
270
+ + events: true,
271
+ + },
272
+ + });
273
+
274
+ - const status = result.effects?.status.status;
275
+ + const tx = result.Transaction ?? result.FailedTransaction;
276
+ + const success = tx.status.success;
277
+ ```
278
+
279
+ ### Migrating `signAndExecuteTransaction`
97
280
 
98
281
  ```diff
99
- - const result = await jsonRpcClient.queryEvents({
100
- - query: { MoveEventType: '0x2::coin::CoinCreated' },
101
- - limit: 10,
102
- - order: 'descending',
282
+ - const result = await jsonRpcClient.signAndExecuteTransaction({
283
+ - transaction,
284
+ - signer,
285
+ - options: { showEffects: true },
103
286
  - });
104
- + const result = await client.core.listEvents({
105
- + filter: { eventType: '0x2::coin::CoinCreated' },
106
- + limit: 10,
107
- + order: 'descending',
287
+ + const result = await client.signAndExecuteTransaction({
288
+ + transaction,
289
+ + signer,
290
+ + include: { effects: true },
291
+ + });
292
+
293
+ + if (result.$kind === 'FailedTransaction') {
294
+ + throw new Error(result.FailedTransaction.status.error?.message ?? 'Transaction failed');
295
+ + }
296
+ ```
297
+
298
+ ### Migrating `waitForTransaction`
299
+
300
+ ```diff
301
+ - const result = await jsonRpcClient.waitForTransaction({
302
+ - digest,
303
+ - options: { showEffects: true },
304
+ - timeout: 60_000,
305
+ - pollInterval: 2_000,
306
+ - });
307
+ + const result = await client.waitForTransaction({
308
+ + digest,
309
+ + include: { effects: true },
310
+ + timeout: 60_000,
311
+ + pollSchedule: [0, 2_000],
108
312
  + });
109
313
  ```
110
314
 
111
- The JSON-RPC `EventFilter` variants map to `sender`, `emitModule` (replaces `MoveModule` /
112
- `MoveEventModule`), and `eventType` (replaces `MoveEventType`). Each returned event includes its
113
- ledger position (`checkpoint`, `transactionDigest`, and `eventIndex`).
315
+ `waitForTransaction` is a top-level method on both `SuiGrpcClient` and `SuiGraphQLClient`. It can
316
+ also accept the result of `executeTransaction` or `signAndExecuteTransaction` through its `result`
317
+ option.
114
318
 
115
- ### Example: Migrating devInspectTransactionBlock
319
+ ### Migrating `dryRunTransactionBlock`
320
+
321
+ ```diff
322
+ - const result = await jsonRpcClient.dryRunTransactionBlock({
323
+ - transactionBlock: tx,
324
+ - });
325
+ + const result = await client.simulateTransaction({
326
+ + transaction: fromBase64(tx),
327
+ + include: {
328
+ + effects: true,
329
+ + balanceChanges: true,
330
+ + },
331
+ + });
332
+ ```
333
+
334
+ ### Migrating `devInspectTransactionBlock`
116
335
 
117
336
  ```diff
118
337
  - const result = await jsonRpcClient.devInspectTransactionBlock({
@@ -120,54 +339,125 @@ ledger position (`checkpoint`, `transactionDigest`, and `eventIndex`).
120
339
  - transactionBlock: tx,
121
340
  - });
122
341
  - const returnValues = result.results?.[0]?.returnValues;
123
- + const result = await client.core.simulateTransaction({
124
- + transaction: tx,
342
+ + const transaction = Transaction.fromKind(tx);
343
+ + transaction.setSender('0xabc...');
344
+ + const result = await client.simulateTransaction({
345
+ + transaction,
125
346
  + checksEnabled: false,
126
347
  + include: { commandResults: true },
127
348
  + });
128
349
  + const returnValues = result.commandResults?.[0]?.returnValues;
129
350
  ```
130
351
 
131
- ### Example: Migrating getOwnedObjects
352
+ ## Transaction and event queries
353
+
354
+ `listTransactions` and `listEvents` are first-class methods on `SuiGrpcClient`, `SuiGraphQLClient`,
355
+ and the Core API. Application code should call the top-level method on its chosen client. Reusable
356
+ SDK code should call the same method through `client.core`.
357
+
358
+ ### Migrating `queryTransactionBlocks`
132
359
 
133
360
  ```diff
134
- - const { data } = await jsonRpcClient.getOwnedObjects({
135
- - owner: '0xabc...',
136
- - options: { showContent: true },
361
+ - const result = await jsonRpcClient.queryTransactionBlocks({
362
+ - filter: { FromAddress: '0xabc...' },
363
+ - options: { showEffects: true },
364
+ - limit: 10,
137
365
  - });
138
- + const { objects } = await grpcClient.listOwnedObjects({
139
- + owner: '0xabc...',
140
- + include: { content: true },
366
+ - const digests = result.data.map((tx) => tx.digest);
367
+ + const page = await client.listTransactions({
368
+ + filter: { sender: '0xabc...' },
369
+ + include: { effects: true },
370
+ + limit: 10,
371
+ + order: 'descending',
141
372
  + });
373
+ + const digests = page.transactions.map(
374
+ + (tx) => (tx.Transaction ?? tx.FailedTransaction).digest,
375
+ + );
142
376
  ```
143
377
 
144
- ## Methods replaced by gRPC services
145
-
146
- These JSON-RPC methods can be replaced by calling gRPC service clients directly:
147
-
148
- | JSON-RPC Method | gRPC Service Replacement |
149
- | ----------------------------------- | ---------------------------------------------------------------- |
150
- | `getCheckpoint` | `ledgerService.getCheckpoint` |
151
- | `getCheckpoints` | `ledgerService.listCheckpoints` |
152
- | `getLatestCheckpointSequenceNumber` | `ledgerService.getServiceInfo` (read `checkpointHeight`) |
153
- | `getCurrentEpoch` | `ledgerService.getEpoch` (omit `epoch` for the current) |
154
- | `getLatestSuiSystemState` | `ledgerService.getEpoch` with `system_state` in the read mask |
155
- | `getCommitteeInfo` | `ledgerService.getEpoch` with `committee` in the read mask |
156
- | `getProtocolConfig` | `ledgerService.getEpoch` with `protocol_config` in the read mask |
157
- | `getCoinMetadata` | `stateService.getCoinInfo` (response includes `metadata`) |
158
- | `getTotalSupply` | `stateService.getCoinInfo` (response includes `treasury`) |
159
- | `getNormalizedMoveModule` | `movePackageService.getPackage` (response includes all modules) |
160
- | `getNormalizedMoveModulesByPackage` | `movePackageService.getPackage` |
161
- | `getNormalizedMoveStruct` | `movePackageService.getDatatype` |
162
- | `resolveNameServiceAddress` | `nameService.lookupName` |
163
- | `resolveNameServiceNames` | `nameService.reverseLookupName` |
164
-
165
- > **Warning:** `getEpochs` (paginated listing) has no gRPC equivalent. Use the `epochs` GraphQL query instead.
166
- > `getValidatorsApy` also has no replacement: there is no canonical definition of validator APY, so
167
- > it must be computed client-side from validator exchange rates (see [Validator APY](#validator-apy)
168
- > below).
169
-
170
- ### Example: using gRPC service clients
378
+ Common transaction filter mappings:
379
+
380
+ | JSON-RPC Filter | gRPC/GraphQL Top-Level Filter |
381
+ | ----------------------------------------------------------------- | ----------------------------------------------------- |
382
+ | `FromAddress` | `sender` |
383
+ | `MoveFunction` | `function` |
384
+ | `ToAddress`, `FromOrToAddress`, `ChangedObject`, `AffectedObject` | Use raw gRPC ledger filters or a custom GraphQL query |
385
+
386
+ The response contains normalized transaction results plus ledger-position cursors:
387
+
388
+ ```typescript
389
+ for (const result of page.transactions) {
390
+ const transaction = result.Transaction ?? result.FailedTransaction;
391
+ console.log(transaction.digest, result.$kind);
392
+ }
393
+
394
+ const nextPage = page.hasNextPage
395
+ ? await client.listTransactions({
396
+ filter: { sender: '0xabc...' },
397
+ include: { effects: true },
398
+ before: page.endCursor,
399
+ limit: 10,
400
+ })
401
+ : null;
402
+ ```
403
+
404
+ The legacy JSON-RPC transaction and event queries default to descending order, while
405
+ `listTransactions` and `listEvents` default to ascending order. Pass `order: 'descending'` when
406
+ preserving the legacy default.
407
+
408
+ ### Migrating `queryEvents`
409
+
410
+ ```diff
411
+ - const result = await jsonRpcClient.queryEvents({
412
+ - query: { MoveEventType: '0x2::coin::CoinCreated' },
413
+ - limit: 10,
414
+ - order: 'descending',
415
+ - });
416
+ + const result = await client.listEvents({
417
+ + filter: { eventType: '0x2::coin::CoinCreated' },
418
+ + limit: 10,
419
+ + order: 'descending',
420
+ + });
421
+ + for (const event of result.events) {
422
+ + console.log(event.eventType, event.transactionDigest, event.json);
423
+ + }
424
+ ```
425
+
426
+ Common event filter mappings:
427
+
428
+ | JSON-RPC Filter | gRPC/GraphQL Top-Level Filter |
429
+ | ----------------- | ------------------------------------- |
430
+ | `Sender` | `sender` |
431
+ | `MoveModule` | `emitModule: 'package::module'` |
432
+ | `MoveEventModule` | `eventType: 'package::module'` |
433
+ | `MoveEventType` | `eventType: 'package::module::Event'` |
434
+
435
+ The top-level query methods handle pagination and cursor normalization. For richer filters, such as
436
+ combined predicates, affected addresses, affected objects, or checkpoint ranges, use the raw
437
+ [`ledgerService`](/sui/clients/grpc#using-service-clients) on `SuiGrpcClient` or a custom GraphQL
438
+ query.
439
+
440
+ Use `after: result.endCursor` to continue an ascending query and `before: result.endCursor` to
441
+ continue a descending query. `startCursor` identifies the first item in a page and can be used with
442
+ `after` to poll for newer transactions or events.
443
+
444
+ ## Native gRPC replacements
445
+
446
+ Some JSON-RPC methods map to gRPC service clients rather than top-level methods:
447
+
448
+ | JSON-RPC Method | gRPC Replacement |
449
+ | ----------------------------------- | --------------------------------------------------------------- |
450
+ | `getCheckpoint` | `ledgerService.getCheckpoint` |
451
+ | `getCheckpoints` | `ledgerService.listCheckpoints` |
452
+ | `getLatestCheckpointSequenceNumber` | `ledgerService.getServiceInfo` and read `checkpointHeight` |
453
+ | `getCurrentEpoch` | `ledgerService.getEpoch` with no `epoch` argument |
454
+ | `getCommitteeInfo` | `ledgerService.getEpoch` with `committee` in the read mask |
455
+ | `getLatestSuiSystemState` | `client.core.getCurrentSystemState` or `ledgerService.getEpoch` |
456
+ | `getProtocolConfig` | `client.core.getProtocolConfig` or `ledgerService.getEpoch` |
457
+ | `getTotalSupply` | `stateService.getCoinInfo` and read `treasury.totalSupply` |
458
+ | `getNormalizedMoveModule` | `movePackageService.getPackage` |
459
+ | `getNormalizedMoveModulesByPackage` | `movePackageService.getPackage` |
460
+ | `getNormalizedMoveStruct` | `movePackageService.getDatatype` |
171
461
 
172
462
  ```typescript
173
463
 
@@ -176,55 +466,24 @@ const client = new SuiGrpcClient({
176
466
  network: 'mainnet',
177
467
  });
178
468
 
179
- // Get the latest checkpoint sequence number (replaces getLatestCheckpointSequenceNumber)
180
469
  const { response: info } = await client.ledgerService.getServiceInfo({});
181
470
  const latestCheckpoint = info.checkpointHeight;
182
471
 
183
- // Get a specific checkpoint by sequence number. `checkpointId` is required (a oneof);
184
- // the read mask defaults to `sequence_number,digest`, so request more fields as needed.
472
+ if (latestCheckpoint == null) {
473
+ throw new Error('The server did not return a checkpoint height');
474
+ }
475
+
185
476
  const { response } = await client.ledgerService.getCheckpoint({
186
477
  checkpointId: { oneofKind: 'sequenceNumber', sequenceNumber: latestCheckpoint },
478
+ readMask: { paths: ['sequence_number', 'digest', 'summary.timestamp'] },
187
479
  });
188
480
 
189
- // Get the current epoch (omit `epoch` for the current one). The system state,
190
- // committee, and protocol config are NOT returned by default — you must request
191
- // them via the read mask (the default mask is just `epoch`).
192
- const { response: epoch } = await client.ledgerService.getEpoch({
193
- readMask: { paths: ['epoch', 'system_state', 'committee', 'protocol_config'] },
194
- });
195
- const systemState = epoch.epoch?.systemState;
196
- const committee = epoch.epoch?.committee;
197
- const protocolConfig = epoch.epoch?.protocolConfig;
198
-
199
- // Get coin metadata and total supply in one call
200
- const { response: coinInfo } = await client.stateService.getCoinInfo({
201
- coinType: '0x2::sui::SUI',
202
- });
203
- const metadata = coinInfo.metadata;
204
- const totalSupply = coinInfo.treasury?.totalSupply;
205
-
206
- // Get Move package information (includes all modules)
207
- const { response: pkg } = await client.movePackageService.getPackage({
208
- packageId: '0x2',
209
- });
210
-
211
- // Get a specific Move datatype (struct or enum)
212
- const { response: datatype } = await client.movePackageService.getDatatype({
213
- packageId: '0x2',
214
- moduleName: 'coin',
215
- name: 'Coin',
216
- });
217
-
218
- // Resolve SuiNS name
219
- const { response: address } = await client.nameService.lookupName({
220
- name: 'example.sui',
221
- });
481
+ console.log(response.checkpoint?.sequenceNumber, response.checkpoint?.summary?.timestamp);
222
482
  ```
223
483
 
224
- ## Methods replaced by gRPC subscriptions
484
+ ## Subscriptions
225
485
 
226
- Replace the deprecated JSON-RPC websocket subscriptions with the gRPC `subscriptionService`, which
227
- provides filtered, real-time streams:
486
+ Replace deprecated JSON-RPC websocket subscriptions with the gRPC `subscriptionService`:
228
487
 
229
488
  | JSON-RPC Method | gRPC Service Replacement |
230
489
  | ---------------------- | ------------------------------------------- |
@@ -258,27 +517,28 @@ for await (const frame of stream.responses) {
258
517
  }
259
518
  ```
260
519
 
261
- Subscriptions always begin at the current tip of the chain. To recover events you missed between
262
- subscriptions, replay the gap with `client.core.listEvents` using the same filter.
263
-
264
- ## Methods requiring GraphQL
265
-
266
- Some JSON-RPC methods don't have gRPC equivalents and require using `SuiGraphQLClient` instead:
267
-
268
- | JSON-RPC Method | GraphQL Alternative |
269
- | --------------------------- | --------------------------------------------------------- |
270
- | `multiGetTransactionBlocks` | `multiGetTransactionEffects` query |
271
- | `getEpochs` | `epochs` query |
272
- | `getCoinMetadata` | `coinMetadata` query (or gRPC `stateService.getCoinInfo`) |
273
- | `getTotalSupply` | `coinMetadata` query (or gRPC `stateService.getCoinInfo`) |
274
- | `getStakes` | `address.stakedSuis` query |
275
- | `getStakesByIds` | `multiGetObjects` query |
276
- | `tryGetPastObject` | Historical object queries |
277
- | `getNetworkMetrics` | Use indexer |
278
- | `getAddressMetrics` | Use indexer |
279
- | `getMoveCallMetrics` | Use indexer |
280
-
281
- ### Setting up GraphQL client
520
+ Subscriptions begin at the current tip of the chain and do not resume automatically. For gap
521
+ recovery, retain the last `frame.watermark.cursor`, open the new subscription to establish its
522
+ first-frame position, and replay with the paired raw `client.ledgerService.listEvents()` call using
523
+ the same protobuf filter and `options.after` cursor. Repeat the raw list call as the index advances
524
+ until it reaches the new subscription's start position. Do not pass the subscription filter or
525
+ cursor to top-level `client.listEvents()`: its Core filter and base64 cursor are different types.
526
+
527
+ ## When to use GraphQL
528
+
529
+ Use `SuiGraphQLClient` when the replacement needs a custom indexed query, a historical object
530
+ version, or a custom selection set. Standard transaction and event history does not require a custom
531
+ GraphQL query; call `graphqlClient.listTransactions()` or `graphqlClient.listEvents()` directly.
532
+
533
+ | JSON-RPC Method | Alternative |
534
+ | -------------------- | -------------------------------------------------------------- |
535
+ | `getEpochs` | GraphQL `epochs` query |
536
+ | `tryGetPastObject` | GraphQL `object(address:, version:)` query |
537
+ | `getStakes` | No current gRPC/Core/GraphQL equivalent; use a staking indexer |
538
+ | `getStakesByIds` | No current gRPC/Core/GraphQL equivalent; use a staking indexer |
539
+ | `getNetworkMetrics` | Use an indexer or analytics-specific GraphQL schema |
540
+ | `getAddressMetrics` | Use an indexer or analytics-specific GraphQL schema |
541
+ | `getMoveCallMetrics` | Use an indexer or analytics-specific GraphQL schema |
282
542
 
283
543
  ```typescript
284
544
 
@@ -286,53 +546,27 @@ const graphqlClient = new SuiGraphQLClient({
286
546
  url: 'https://sui-mainnet.mystenlabs.com/graphql',
287
547
  network: 'mainnet',
288
548
  });
289
- ```
290
-
291
- ### Fetching multiple transactions
292
-
293
- Replace `multiGetTransactionBlocks` with a GraphQL query:
294
-
295
- ```typescript
296
- const result = await graphqlClient.query({
297
- query: `
298
- query MultiGetTransactions($digests: [String!]!) {
299
- multiGetTransactionEffects(keys: $digests) {
300
- transaction {
301
- digest
302
- transactionBcs
303
- }
304
- status
305
- epoch { epochId }
306
- }
307
- }
308
- `,
309
- variables: {
310
- digests: ['digest1', 'digest2', 'digest3'],
311
- },
312
- });
313
- ```
314
-
315
- ### Querying historical objects
316
549
 
317
- Replace `tryGetPastObject` with a GraphQL query specifying a version:
550
+ const historicalObjectQuery = graphql(`
551
+ query GetObjectAtVersion($id: SuiAddress!, $version: UInt53!) {
552
+ object(address: $id, version: $version) {
553
+ address
554
+ version
555
+ digest
556
+ asMoveObject {
557
+ contents {
558
+ type {
559
+ repr
560
+ }
561
+ bcs
562
+ }
563
+ }
564
+ }
565
+ }
566
+ `);
318
567
 
319
- ```typescript
320
568
  const result = await graphqlClient.query({
321
- query: `
322
- query GetObjectAtVersion($id: SuiAddress!, $version: UInt53!) {
323
- object(address: $id, version: $version) {
324
- address
325
- version
326
- digest
327
- asMoveObject {
328
- contents {
329
- type { repr }
330
- bcs
331
- }
332
- }
333
- }
334
- }
335
- `,
569
+ query: historicalObjectQuery,
336
570
  variables: {
337
571
  id: '0x123...',
338
572
  version: 42,
@@ -340,98 +574,43 @@ const result = await graphqlClient.query({
340
574
  });
341
575
  ```
342
576
 
343
- ### Querying coin metadata
344
-
345
- Replace `getCoinMetadata` and `getTotalSupply` with a GraphQL query:
346
-
347
- ```typescript
348
- const result = await graphqlClient.query({
349
- query: `
350
- query GetCoinMetadata($coinType: String!) {
351
- coinMetadata(coinType: $coinType) {
352
- name
353
- symbol
354
- description
355
- decimals
356
- iconUrl
357
- supply
358
- }
359
- }
360
- `,
361
- variables: {
362
- coinType: '0x2::sui::SUI',
363
- },
364
- });
365
- ```
366
-
367
- ### Querying staked SUI
368
-
369
- Replace `getStakes` with a GraphQL query:
370
-
371
- ```typescript
372
- const result = await graphqlClient.query({
373
- query: `
374
- query GetStakes($owner: SuiAddress!) {
375
- address(address: $owner) {
376
- stakedSuis {
377
- nodes {
378
- principal
379
- stakeActivationEpoch
380
- estimatedReward
381
- contents {
382
- bcs
383
- }
384
- }
385
- }
386
- }
387
- }
388
- `,
389
- variables: {
390
- owner: '0xabc...',
391
- },
392
- });
393
- ```
394
-
395
577
  ## Validator APY
396
578
 
397
- There is **no replacement** for `getValidatorsApy`, and one will not be added. There is no canonical
398
- definition of validator APY no other chain's core/general-purpose RPCs offer this endpoint so it
399
- must be computed client-side from validator staking-pool exchange rates.
579
+ There is no direct SDK replacement for `getValidatorsApy`. There is no canonical definition of
580
+ validator APY, so compute the metric from validator staking-pool exchange rates or use an
581
+ application-specific indexer.
400
582
 
401
- The general approach: read each validator's `exchangeRates` (a table of `pool_token_amount` /
402
- `sui_amount` per epoch) and estimate the annualized rate from the change in exchange rate over the
403
- trailing epochs. Two concrete reference implementations:
583
+ Two reference implementations:
404
584
 
405
585
  - The `jsonrpc-alt` implementation in
406
- [`sui-indexer-alt-jsonrpc`](https://github.com/MystenLabs/sui/blob/31537d4d9235b9f61dc07a3a71b05ed61a2bda7b/crates/sui-indexer-alt-jsonrpc/src/api/governance.rs#L422-L440),
407
- which is compact and readable.
408
- - [This GraphQL approach](https://github.com/MystenLabs/sui/issues/23832#issuecomment-4437791087),
409
- which fetches the precursor exchange-rate information in a single query.
586
+ [`sui-indexer-alt-jsonrpc`](https://github.com/MystenLabs/sui/blob/31537d4d9235b9f61dc07a3a71b05ed61a2bda7b/crates/sui-indexer-alt-jsonrpc/src/api/governance.rs#L422-L440)
587
+ - [A GraphQL approach](https://github.com/MystenLabs/sui/issues/23832#issuecomment-4437791087)
410
588
 
411
589
  Treat whichever formula you adopt as _a_ definition of validator APY, not _the_ definition.
412
590
 
413
591
  ## Response format differences
414
592
 
415
- The gRPC client uses the core API response format, which differs from JSON-RPC responses. See the
416
- [`@mysten/sui` migration guide](/sui/migrations/sui-2.0/sui#transaction-executors-now-accept-any-client)
417
- for details on the new response format.
418
-
419
- Key differences:
593
+ gRPC and GraphQL top-level methods return the Core API response format, which differs from legacy
594
+ JSON-RPC response shapes.
420
595
 
421
596
  ```diff
422
597
  // Transaction result access
423
598
  - const status = result.effects?.status?.status;
424
599
  + const tx = result.Transaction ?? result.FailedTransaction;
425
- + const status = tx.effects.status.success;
600
+ + const success = tx.status.success;
426
601
 
427
602
  // Include options
428
603
  - { showEffects: true, showEvents: true }
429
604
  + { effects: true, events: true }
430
605
  ```
431
606
 
607
+ See the
608
+ [`@mysten/sui` migration guide](/sui/migrations/sui-2.0/sui#transaction-executors-now-accept-any-client)
609
+ for transaction executor response changes.
610
+
432
611
  ## Client extensions
433
612
 
434
- Client extensions work the same way with both clients:
613
+ Client extensions work with `SuiGrpcClient` and any client that implements `ClientWithCoreApi`:
435
614
 
436
615
  ```typescript
437
616
 
@@ -440,15 +619,14 @@ const client = new SuiGrpcClient({
440
619
  network: 'mainnet',
441
620
  }).$extend(deepbook({ address: myAddress }), suins());
442
621
 
443
- // Use extended functionality
444
622
  await client.deepbook.checkManagerBalance(manager, asset);
445
- await client.suins.getName('0xabc...');
623
+ await client.suins.getNameRecord('example.sui');
446
624
  ```
447
625
 
448
626
  ## See also
449
627
 
450
- - [SuiGrpcClient Documentation](/sui/clients/grpc) - Full gRPC client documentation
451
- - [SuiGraphQLClient Documentation](/sui/clients/graphql) - GraphQL client documentation
452
- - [Core API](/sui/clients/core) - Transport-agnostic API methods
453
- - [gRPC Overview](https://docs.sui.io/concepts/data-access/grpc-overview) - Sui gRPC API
454
- documentation
628
+ - [SuiGrpcClient](/sui/clients/grpc)
629
+ - [SuiGraphQLClient](/sui/clients/graphql)
630
+ - [Querying data](/sui/clients/querying)
631
+ - [Core API](/sui/clients/core)
632
+ - [Building SDKs](/sui/sdk-building)