@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,46 +1,70 @@
1
1
  # SuiGrpcClient
2
2
 
3
- > Connect to Sui through gRPC with SuiGrpcClient.
3
+ > Connect to Sui over gRPC, with native service clients and real-time subscriptions
4
4
 
5
- The `SuiGrpcClient` provides access to the Full Node gRPC API.
6
-
7
- For more complete details on what is available through this API see the
8
- [gRPC API docs](https://docs.sui.io/develop/accessing-data/grpc/what-is-grpc).
9
-
10
- ## Creating a gRPC client
11
-
12
- To get started, create a `SuiGrpcClient` instance by specifying a network and base URL:
5
+ `SuiGrpcClient` talks to the full node gRPC API. It is the recommended default for application code
6
+ and SDK integrations: it reads directly from a full node, and it is the only client with real-time
7
+ subscriptions.
13
8
 
14
9
  ```typescript
15
10
 
16
- const grpcClient = new SuiGrpcClient({
17
- network: 'testnet',
18
- baseUrl: 'https://fullnode.testnet.sui.io:443',
11
+ const client = new SuiGrpcClient({
12
+ network: 'mainnet',
13
+ baseUrl: 'https://fullnode.mainnet.sui.io:443',
19
14
  });
20
15
  ```
21
16
 
22
17
  For local development:
23
18
 
24
19
  ```typescript
25
- const grpcClient = new SuiGrpcClient({
20
+ const client = new SuiGrpcClient({
26
21
  network: 'localnet',
27
22
  baseUrl: 'http://127.0.0.1:9000',
28
23
  });
29
24
  ```
30
25
 
26
+ Reading data, executing transactions, and querying history all work the same way here as on any
27
+ client. See [Querying data](/sui/clients/querying) and
28
+ [Signing and execution](/sui/transactions/signing-and-execution). The rest of this page covers what
29
+ is specific to gRPC.
30
+
31
+ ## gRPC-specific options
32
+
33
+ Top-level gRPC methods are a superset of the shared API, adding fields where the transport exposes
34
+ more data than the common shape can carry:
35
+
36
+ | Option | Available on |
37
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------- |
38
+ | `include.protoJson` | `getTransaction`, `executeTransaction`, `signAndExecuteTransaction`, `waitForTransaction`, `simulateTransaction` |
39
+
40
+ `protoJson` returns the raw protobuf response alongside the parsed result, which is useful when you
41
+ need a field the unified shape does not expose yet:
42
+
43
+ ```typescript
44
+ const result = await client.getTransaction({
45
+ digest: 'ABC123...',
46
+ include: {
47
+ effects: true,
48
+ protoJson: true,
49
+ },
50
+ });
51
+
52
+ const transaction = result.Transaction ?? result.FailedTransaction;
53
+ console.log(transaction.digest, result.protoJson);
54
+ ```
55
+
31
56
  ## Transport options
32
57
 
33
58
  By default, `SuiGrpcClient` uses `GrpcWebFetchTransport` from
34
59
  [protobuf-ts](https://github.com/timostamm/protobuf-ts), which works in browsers and Node.js through
35
- the Fetch API. You can also provide a custom transport for advanced use cases.
36
-
37
- The `GrpcWebFetchTransport` class, `GrpcWebOptions` type, and `RpcTransport` type are all
38
- re-exported from `@mysten/sui/grpc` for convenience.
60
+ the Fetch API. The `GrpcWebFetchTransport` class, `GrpcWebOptions` type, and `RpcTransport` type are
61
+ re-exported from `@mysten/sui/grpc`, so you can configure a transport without adding
62
+ `@protobuf-ts/*` as a direct dependency.
39
63
 
40
64
  ### gRPC-web transport (default)
41
65
 
42
- The default transport uses the gRPC-web protocol over HTTP/1.1 or HTTP/2. You can customize it by
43
- passing `GrpcWebFetchTransport` options directly:
66
+ The default transport uses the gRPC-web protocol over HTTP/1.1 or HTTP/2. Pass
67
+ `GrpcWebFetchTransport` options to customize it:
44
68
 
45
69
  ```typescript
46
70
 
@@ -50,7 +74,7 @@ const transport = new GrpcWebFetchTransport({
50
74
  // Additional transport options like fetchInit
51
75
  });
52
76
 
53
- const grpcClient = new SuiGrpcClient({
77
+ const client = new SuiGrpcClient({
54
78
  network: 'testnet',
55
79
  transport,
56
80
  });
@@ -58,18 +82,14 @@ const grpcClient = new SuiGrpcClient({
58
82
 
59
83
  ### Native gRPC transport
60
84
 
61
- For server-side applications (Node.js, Bun, and others), you can use the native gRPC transport with
62
- `@protobuf-ts/grpc-transport` and `@grpc/grpc-js`. This uses HTTP/2 with the native gRPC protocol
85
+ For server-side applications (Node.js, Bun, and others), use the native gRPC transport with
86
+ `@protobuf-ts/grpc-transport` and `@grpc/grpc-js`. This speaks HTTP/2 and the native gRPC protocol
63
87
  rather than the gRPC-web translation layer.
64
88
 
65
- Install the required packages:
66
-
67
- ```bash
89
+ ```npm
68
90
  npm install @protobuf-ts/grpc-transport @grpc/grpc-js
69
91
  ```
70
92
 
71
- Then create the client with a `GrpcTransport`:
72
-
73
93
  ```typescript
74
94
 
75
95
  const transport = new GrpcTransport({
@@ -77,96 +97,71 @@ const transport = new GrpcTransport({
77
97
  channelCredentials: ChannelCredentials.createSsl(),
78
98
  });
79
99
 
80
- const grpcClient = new SuiGrpcClient({
100
+ const client = new SuiGrpcClient({
81
101
  network: 'testnet',
82
102
  transport,
83
103
  });
84
104
  ```
85
105
 
86
- For local development without TLS:
106
+ For local development without TLS, use `ChannelCredentials.createInsecure()` and a plain
107
+ `host: '127.0.0.1:9000'`.
87
108
 
88
- ```typescript
109
+ ## Read masks
89
110
 
90
- const transport = new GrpcTransport({
91
- host: '127.0.0.1:9000',
92
- channelCredentials: ChannelCredentials.createInsecure(),
93
- });
94
-
95
- const grpcClient = new SuiGrpcClient({
96
- network: 'localnet',
97
- transport,
98
- });
99
- ```
100
-
101
- ## Using service clients
102
-
103
- The `SuiGrpcClient` exposes several service clients for lower-level access to the gRPC API. These
104
- service clients are generated using [protobuf-ts](https://github.com/timostamm/protobuf-ts), which
105
- provides type-safe gRPC clients for TypeScript. For more details on how to use gRPC with Sui, see
106
- the [gRPC overview](https://docs.sui.io/develop/accessing-data/grpc/what-is-grpc).
107
-
108
- ### With the core API
109
-
110
- The gRPC client implements all the [`core`](./core) API methods:
111
+ gRPC responses are opt-in. A request names the fields it wants in a `readMask`, and the server
112
+ returns only those. Paths are proto field names in `snake_case`, and nested fields are dotted:
111
113
 
112
114
  ```typescript
113
-
114
- const grpcClient = new SuiGrpcClient({
115
- network: 'testnet',
116
- baseUrl: 'https://fullnode.testnet.sui.io:443',
117
- });
118
- // Get coins owned by an address
119
- await grpcClient.getCoins({
120
- owner: '<OWNER_ADDRESS>',
115
+ const { response } = await client.ledgerService.getTransaction({
116
+ digest: 'ABC123...',
117
+ readMask: { paths: ['digest', 'effects.status', 'transaction.sender'] },
121
118
  });
122
119
  ```
123
120
 
124
- To query additional data not available in the core API, you can use the service clients directly:
121
+ A field you did not ask for comes back unset, which is the most common surprise when moving from a
122
+ JSON-RPC mindset: an empty field usually means it was not requested rather than that it has no
123
+ value.
125
124
 
126
- ### Transaction execution service
125
+ The top-level methods build masks for you from their `include` options, adding the paths each option
126
+ needs on top of the handful every result carries. That is the main reason to prefer them: field
127
+ selection and the mapping back into the unified shape are handled for you.
127
128
 
128
- ```typescript
129
- const { response } = await grpcClient.transactionExecutionService.executeTransaction({
130
- transaction: {
131
- bcs: {
132
- value: transactionBytes,
133
- },
134
- },
135
- signatures: signatures.map((sig) => ({
136
- bcs: { value: fromBase64(sig) },
137
- signature: { oneofKind: undefined },
138
- })),
139
- });
129
+ ## Using service clients
140
130
 
141
- // IMPORTANT: Always check the transaction status
142
- if (!response.finality?.effects?.status?.success) {
143
- const error = response.finality?.effects?.status?.error;
144
- throw new Error(`Transaction failed: ${error || 'Unknown error'}`);
145
- }
146
- ```
131
+ `SuiGrpcClient` exposes the generated service clients as properties, so any RPC the node serves is
132
+ reachable even where the shared API has no method for it:
133
+
134
+ | Property | Service |
135
+ | ------------------------------ | ----------------------------------------- |
136
+ | `ledgerService` | Ledger reads, and the streaming list RPCs |
137
+ | `stateService` | Live object and balance state |
138
+ | `transactionExecutionService` | Transaction execution |
139
+ | `subscriptionService` | Real-time streams |
140
+ | `movePackageService` | Move package metadata |
141
+ | `nameService` | SuiNS lookup and reverse lookup |
142
+ | `signatureVerificationService` | Signature verification |
143
+ | `forkingService` | Admin APIs, for `sui-fork` instances only |
147
144
 
148
- ### Ledger service
145
+ The clients are generated with [protobuf-ts](https://github.com/timostamm/protobuf-ts). Each call
146
+ takes the request message and an optional `RpcOptions`, where `abort` carries an `AbortSignal`:
149
147
 
150
148
  ```typescript
151
- // Get transaction by digest
152
- const { response } = await grpcClient.ledgerService.getTransaction({
153
- digest: '0x123...',
154
- });
149
+ const controller = new AbortController();
155
150
 
156
- // Get current epoch information
157
- const { response: epochInfo } = await grpcClient.ledgerService.getEpoch({});
151
+ const { response } = await client.nameService.lookupName(
152
+ { name: 'example.sui' },
153
+ { abort: controller.signal },
154
+ );
158
155
  ```
159
156
 
160
- The ledger service also provides streaming `listCheckpoints`, `listTransactions`, and `listEvents`
161
- RPCs. For most use cases, prefer the corresponding
162
- [core API query methods](/sui/clients/core#query-methods) (`listTransactions` and `listEvents`),
163
- which handle pagination and filter construction for you. The raw RPCs additionally support DNF
164
- filters (combined, negated, and additional predicates like `affected_address` and `package_write`)
165
- and checkpoint range bounds that the core API does not expose:
157
+ Request and response shapes come from the proto definitions, so the generated types are the
158
+ reference for what each RPC accepts. Filters on the list and subscribe RPCs are one place worth
159
+ knowing the shape: a filter is a list of `terms` ORed together, each term a list of `literals` ANDed
160
+ together, and `negated: true` inverts a literal.
166
161
 
167
162
  ```typescript
168
- // Transactions that affected an address but were not sent by it:
169
- const stream = grpcClient.ledgerService.listTransactions({
163
+ // Transactions that affected an address but were not sent by it
164
+ const stream = client.ledgerService.listTransactions({
170
165
  filter: {
171
166
  terms: [
172
167
  {
@@ -188,21 +183,116 @@ const stream = grpcClient.ledgerService.listTransactions({
188
183
  },
189
184
  readMask: { paths: ['digest'] },
190
185
  });
186
+ ```
191
187
 
192
- for await (const frame of stream.responses) {
193
- if (frame.transaction) {
194
- console.log(frame.transaction.digest);
195
- }
188
+ An absent filter matches everything; a present filter needs at least one term.
189
+
190
+ ## Generated types and helpers
191
+
192
+ `@mysten/sui/grpc` re-exports everything you need to work with the generated API, so nothing has to
193
+ depend on `@protobuf-ts/*` or the proto files directly.
194
+
195
+ `GrpcTypes` is a namespace holding every generated message interface and enum. Use it to type values
196
+ you pass around, and to reference enums by name rather than by number:
197
+
198
+ ```typescript
199
+
200
+ function describe(status: GrpcTypes.ExecutionStatus) {
201
+ return status.success ? 'succeeded' : status.error?.description;
196
202
  }
203
+
204
+ const ordering = GrpcTypes.Ordering.DESCENDING;
205
+ ```
206
+
207
+ Two helpers map a raw protobuf response into the same shape the top-level methods return, which is
208
+ useful when you drop to a service client for the request but still want the unified result:
209
+
210
+ ```typescript
211
+
212
+ const { response } = await client.ledgerService.getTransaction({
213
+ digest: 'ABC123...',
214
+ readMask: { paths: ['digest', 'effects'] },
215
+ });
216
+
217
+ // Same discriminated union that client.getTransaction() returns
218
+ const result = parseGrpcTransactionResponse(response.transaction!, {
219
+ include: { effects: true },
220
+ });
197
221
  ```
198
222
 
199
- ### Subscription service
223
+ `parseGrpcSimulateTransactionResponse` does the same for `SimulateTransactionResponse`.
200
224
 
201
- Subscribe to filtered, real-time streams of checkpoints, transactions, or events. Streams begin at
202
- the current tip of the chain:
225
+ | Export | Use |
226
+ | --------------------------------------------------------- | ----------------------------------------------------- |
227
+ | `GrpcTypes` | Generated message interfaces and enums |
228
+ | `parseGrpcTransactionResponse` | Raw `ExecutedTransaction` to the unified result shape |
229
+ | `parseGrpcSimulateTransactionResponse` | Raw simulation response to the unified result shape |
230
+ | `GrpcWebFetchTransport`, `GrpcWebOptions`, `RpcTransport` | Configuring a [transport](#transport-options) |
231
+ | `SuiGrpcClientOptions`, `GrpcTransactionInclude`, … | Typing your own wrappers around the client |
232
+ | `isSuiGrpcClient` | Type guard for narrowing an unknown client |
233
+
234
+ ## Streaming responses
235
+
236
+ The list and subscribe RPCs return server streams rather than a single response, consumed with
237
+ `for await` over `stream.responses`. Both use the same frame shape.
238
+
239
+ A list RPC is a stream of frames, not a single response. Each frame either delivers one item or just
240
+ reports progress, and every frame carries a `watermark` whose `cursor` is a safe resume point.
241
+ Exactly one frame of a successful stream carries `end`, reporting why the scan stopped.
242
+
243
+ This matters because a single request does not necessarily reach the end of the range you asked for:
244
+ the server bounds how much ledger a filtered scan reads, so a stream can stop early and report
245
+ `SCAN_LIMIT`. Reissue from the last watermark cursor until the reason says the scan is genuinely
246
+ finished:
203
247
 
204
248
  ```typescript
205
- const stream = grpcClient.subscriptionService.subscribeTransactions({
249
+
250
+ let resumeFrom: Uint8Array | undefined;
251
+ let reason: GrpcTypes.QueryEndReason | undefined;
252
+
253
+ // One request can stop before the range is exhausted, so scan until the range bound is reached
254
+ do {
255
+ const stream = client.ledgerService.listEvents({
256
+ readMask: { paths: ['event_type', 'transaction_digest', 'event_index'] },
257
+ // Bound the scan. Without an end, this walks the whole ledger to the current tip
258
+ startCheckpoint: 1_000_000n,
259
+ endCheckpoint: 1_000_100n,
260
+ options: {
261
+ after: resumeFrom,
262
+ limit: 100,
263
+ ordering: GrpcTypes.Ordering.ASCENDING,
264
+ },
265
+ });
266
+
267
+ for await (const frame of stream.responses) {
268
+ // The latest watermark is always the safe place to resume from
269
+ resumeFrom = frame.watermark?.cursor ?? resumeFrom;
270
+ reason = frame.end?.reason ?? reason;
271
+
272
+ if (frame.event) {
273
+ console.log(frame.event.eventType, frame.event.transactionDigest);
274
+ }
275
+ }
276
+ } while (reason === GrpcTypes.QueryEndReason.SCAN_LIMIT);
277
+ ```
278
+
279
+ `options.after` and `options.before` are ledger-position bounds that mean the same thing in both
280
+ directions (ordering only controls the order of items within the interval), and they intersect with
281
+ the checkpoint range when both are given.
282
+
283
+ ## Subscriptions
284
+
285
+ `subscriptionService` provides filtered, real-time streams. Each subscription pairs with the list
286
+ RPC of the same name: same filter message, same item and watermark shapes, same cursor semantics.
287
+
288
+ | Method | Yields |
289
+ | ----------------------- | ----------------------------------------------- |
290
+ | `subscribeCheckpoints` | Checkpoints, and progress-only cursor frames |
291
+ | `subscribeTransactions` | Executed transactions, and progress-only frames |
292
+ | `subscribeEvents` | Emitted events, and progress-only frames |
293
+
294
+ ```typescript
295
+ const stream = client.subscriptionService.subscribeTransactions({
206
296
  filter: {
207
297
  terms: [
208
298
  {
@@ -220,59 +310,66 @@ const stream = grpcClient.subscriptionService.subscribeTransactions({
220
310
 
221
311
  for await (const frame of stream.responses) {
222
312
  if (frame.transaction) {
223
- console.log(frame.transaction.digest);
313
+ console.log(frame.transaction.digest, frame.transaction.effects?.status?.success);
224
314
  }
225
315
  }
226
316
  ```
227
317
 
228
- ### State service
318
+ Omit `filter` to receive everything.
319
+
320
+ ### Frames and watermarks
321
+
322
+ A subscription behaves like an unbounded ascending scan, so its frames work the same way as a
323
+ [list RPC's](#streaming-responses), with two differences: checkpoint frames are checkpoint-granular
324
+ and carry a `cursor` sequence number instead of a watermark, and the first frame of a filtered
325
+ subscription is always progress-only, establishing the start position. Progress also keeps advancing
326
+ with bounded staleness while nothing matches, which is what keeps a sparse filter alive. Track the
327
+ cursor on every frame, not just the ones with items:
229
328
 
230
329
  ```typescript
231
- // List owned objects
232
- const { response } = await grpcClient.stateService.listOwnedObjects({
233
- owner: '0xabc...',
234
- objectType: '0x2::coin::Coin<0x2::sui::SUI>',
235
- });
330
+ let lastCursor: Uint8Array | undefined;
236
331
 
237
- // Get dynamic fields
238
- const { response: fields } = await grpcClient.stateService.listDynamicFields({
239
- parent: '0x123...',
240
- });
332
+ for await (const frame of stream.responses) {
333
+ lastCursor = frame.watermark?.cursor ?? lastCursor;
334
+
335
+ if (frame.transaction) {
336
+ await handleTransaction(frame.transaction);
337
+ }
338
+ }
241
339
  ```
242
340
 
243
- ### Move package service
341
+ ### Cancelling a subscription
342
+
343
+ Subscription streams have no successful end. They run until the client cancels them, or until the
344
+ server terminates them with a gRPC status. Pass an `AbortSignal` through `RpcOptions`:
244
345
 
245
346
  ```typescript
246
- // Get function information
247
- const { response } = await grpcClient.movePackageService.getFunction({
248
- packageId: '0x2',
249
- moduleName: 'coin',
250
- name: 'value',
251
- });
252
- ```
347
+ const controller = new AbortController();
253
348
 
254
- ### Name service
349
+ const stream = client.subscriptionService.subscribeEvents(
350
+ { readMask: { paths: ['event_type', 'transaction_digest'] } },
351
+ { abort: controller.signal },
352
+ );
255
353
 
256
- ```typescript
257
- // Reverse lookup address to get name
258
- const { response } = await grpcClient.nameService.reverseLookupName({
259
- address: '0xabc...',
260
- });
354
+ // Later, to tear the stream down
355
+ controller.abort();
261
356
  ```
262
357
 
263
- ### Signature verification service
358
+ ### Recovering missed data
264
359
 
265
- ```typescript
266
- // Verify a signature
267
- const { response } = await grpcClient.signatureVerificationService.verifySignature({
268
- message: {
269
- name: 'TransactionData',
270
- value: messageBytes,
271
- },
272
- signature: {
273
- bcs: { value: signatureBytes },
274
- signature: { oneofKind: undefined },
275
- },
276
- jwks: [],
277
- });
278
- ```
360
+ Subscriptions do not resume: a new subscription starts at the current tip, so anything that happened
361
+ while you were disconnected is skipped. Close the gap with the paired list RPC, scanning between the
362
+ last cursor you processed and the cursor the new subscription reported in its first frame. Pass them
363
+ as `options.after` and `options.before`. The indexed tip the list RPC reads from can trail the
364
+ subscription's start position, so repeat the scan until a terminal frame reports `CURSOR_BOUND`
365
+ rather than `LEDGER_TIP`.
366
+
367
+ A durable consumer therefore keeps two pieces of state: the last cursor it processed, and the start
368
+ cursor of each new subscription. On reconnect, open the subscription first, buffer its frames,
369
+ replay the gap, then drain the buffer.
370
+
371
+ Checkpoint subscriptions recover differently. `subscribeCheckpoints` reports its position as a
372
+ checkpoint sequence number rather than a watermark cursor, and `listCheckpoints` bounds a scan with
373
+ `startCheckpoint` and `endCheckpoint` rather than `options.after` and `options.before`. Replay that
374
+ gap by listing from the sequence number after the last one you processed, up to the sequence number
375
+ the new subscription started at.
@@ -1,28 +1,22 @@
1
1
  # Sui Clients
2
2
 
3
- > Choose and configure gRPC, GraphQL, or JSON-RPC clients for the Sui network.
3
+ > Choose between SuiGrpcClient and SuiGraphQLClient and understand their shared API
4
4
 
5
- The Sui TypeScript SDK provides multiple client implementations for interacting with the Sui
6
- network. Each client connects to a different API but provides two levels of access:
5
+ A client is how your code reads from and writes to the Sui network. There are two to choose from,
6
+ and they implement the same API (the same method names, options, and response shapes), so the choice
7
+ is about which transport suits your application, not about which features you get.
7
8
 
8
- - Native API: Full access to everything the underlying API offers
9
- - [Core API](/sui/clients/core): A consistent interface across all clients for common operations
9
+ | Client | Choose it for |
10
+ | ------------------------------------------ | ------------------------------------------------------------------------------------- |
11
+ | [`SuiGrpcClient`](/sui/clients/grpc) | The default. Reads from a full node, and the only client with real-time subscriptions |
12
+ | [`SuiGraphQLClient`](/sui/clients/graphql) | Reads from the indexer, and custom queries written against the GraphQL schema |
10
13
 
11
- ## Available clients
14
+ Both are fully supported primary choices. Pick `SuiGrpcClient` if you have no particular reason to
15
+ prefer one, and `SuiGraphQLClient` when your application wants to query the GraphQL schema directly.
16
+ Because they share an API, moving between them is mostly a matter of changing the constructor.
12
17
 
13
- | Client | API |
14
- | -------------------------------------------------------- | ------------------------------------------------------------------ |
15
- | [`SuiGrpcClient`](/sui/clients/grpc) (recommended) | [Full Node gRPC](https://docs.sui.io/references/fullnode-protocol) |
16
- | [`SuiGraphQLClient`](/sui/clients/graphql) | [GraphQL](https://docs.sui.io/references/sui-graphql) |
17
- | [`SuiJsonRpcClient`](/sui/clients/json-rpc) (deprecated) | [JSON-RPC (deprecated)](https://docs.sui.io/sui-api-ref) |
18
-
19
- All clients are compatible with Mysten SDKs like `@mysten/walrus`, `@mysten/seal` and
20
- `@mysten/suins`.
21
-
22
- For most application gRPC is a good default. The JSON RPC API has been deprecated and will be
23
- decommissioned soon. The GraphQL can be used for more advanced query patterns that can not be
24
- supported directly on full nodes (for example, querying for transactions or events with various
25
- filters).
18
+ > **Note:** JSON-RPC is deprecated. See the [migration guide](/sui/migrations/sui-2.0/json-rpc-migration) if
19
+ > you maintain existing JSON-RPC code.
26
20
 
27
21
  ## Quick start
28
22
 
@@ -33,26 +27,76 @@ const client = new SuiGrpcClient({
33
27
  baseUrl: 'https://fullnode.mainnet.sui.io:443',
34
28
  });
35
29
 
36
- // Use the native API for full access to transport-specific features
37
- const { response } = await client.ledgerService.getTransaction({ digest: '0x...' });
30
+ const { balance } = await client.getBalance({ owner: '0x...' });
38
31
 
39
- // Use the Core API for transport-agnostic operations
40
- const { object } = await client.core.getObject({ objectId: '0x...' });
32
+ const { object } = await client.getObject({
33
+ objectId: '0x...',
34
+ include: { content: true },
35
+ });
41
36
  ```
42
37
 
43
- ## Native vs core API
38
+ The same code against GraphQL differs only in how the client is created:
39
+
40
+ ```typescript
41
+
42
+ const client = new SuiGraphQLClient({
43
+ network: 'mainnet',
44
+ url: 'https://graphql.mainnet.sui.io/graphql',
45
+ });
46
+ ```
44
47
 
45
- ### Native API
48
+ ## Three ways to call a client
46
49
 
47
- Each client exposes the full capabilities of its underlying transport. Use the native API when you
48
- need transport-specific features or want maximum control:
50
+ Each client exposes the same functionality through three surfaces, aimed at different callers.
51
+
52
+ ### Top-level methods
53
+
54
+ The main API for application code. Read data, execute transactions, and query history by calling
55
+ methods directly on the client:
49
56
 
50
57
  ```typescript
58
+ const { objects } = await client.listOwnedObjects({
59
+ owner: '0x...',
60
+ include: { display: true },
61
+ });
62
+
63
+ const result = await client.signAndExecuteTransaction({
64
+ transaction,
65
+ signer,
66
+ include: { effects: true, balanceChanges: true },
67
+ });
68
+ ```
69
+
70
+ These are documented in [Querying data](/sui/clients/querying) and
71
+ [Signing and execution](/sui/transactions/signing-and-execution). Where a transport can return more
72
+ than the shared shape carries, it adds options here, such as `include: { protoJson: true }` on gRPC.
73
+
74
+ ### `client.core`
51
75
 
52
- // gRPC - access various service clients to call any gRPC method
53
- const { response } = await grpcClient.stateService.listOwnedObjects({ owner: '0x...' });
76
+ The transport-agnostic contract, for libraries that must work with whichever client their caller
77
+ supplies:
54
78
 
55
- // GraphQL - write type-safe custom queries using the graphql function
79
+ ```typescript
80
+
81
+ return client.core.getObject({
82
+ objectId,
83
+ include: { content: true },
84
+ });
85
+ }
86
+ ```
87
+
88
+ See the [Core API](/sui/clients/core) for the contract and the cross-transport differences to
89
+ account for. Application code does not need this. Call the top-level methods instead.
90
+
91
+ ### Native APIs
92
+
93
+ Each client also exposes its transport in full, for the cases the shared API does not cover:
94
+
95
+ ```typescript
96
+ // gRPC: generated service clients, read masks, and streams
97
+ const { response } = await grpcClient.ledgerService.getServiceInfo({});
98
+
99
+ // GraphQL: type-safe custom queries
56
100
  const result = await graphqlClient.query({
57
101
  query: graphql(`
58
102
  query {
@@ -60,36 +104,28 @@ const result = await graphqlClient.query({
60
104
  }
61
105
  `),
62
106
  });
63
-
64
- // JSON-RPC - call any JSON-RPC method
65
- const coins = await jsonRpcClient.getCoins({ owner: '0x...' });
66
- ```
67
-
68
- ### Core API
69
-
70
- All clients also implement the [Core API](/sui/clients/core) through `client.core`. This provides a
71
- consistent interface for common operations that works identically across all transports:
72
-
73
- ```typescript
74
- // These methods work the same on any client
75
- const { object } = await client.core.getObject({ objectId: '0x...' });
76
- const balance = await client.core.getBalance({ owner: '0x...' });
77
- await client.core.executeTransaction({ transaction, signatures });
78
107
  ```
79
108
 
80
- The Core API is essential for [building SDKs](/sui/sdk-building) that work with any client the user
81
- chooses.
109
+ gRPC adds [raw list RPCs](/sui/clients/grpc#using-service-clients) and
110
+ [subscriptions](/sui/clients/grpc#subscriptions); GraphQL adds
111
+ [custom queries](/sui/clients/graphql#writing-queries) against the schema.
82
112
 
83
113
  ## Client extensions
84
114
 
85
- All clients support extensions through the `$extend` method, enabling SDKs like
86
- [@mysten/walrus](https://www.npmjs.com/package/@mysten/walrus) to add functionality:
115
+ Both clients support extensions through `$extend`, which lets SDKs such as
116
+ [@mysten/walrus](https://www.npmjs.com/package/@mysten/walrus) add functionality to a client your
117
+ application already configured:
87
118
 
88
119
  ```typescript
89
120
 
90
121
  const client = new SuiGrpcClient({ network: 'mainnet', baseUrl: '...' }).$extend(walrus());
91
122
 
92
- await client.walrus.writeBlob({ ... });
123
+ const { blobId } = await client.walrus.writeBlob({
124
+ blob: file,
125
+ deletable: false,
126
+ epochs: 3,
127
+ signer: keypair,
128
+ });
93
129
  ```
94
130
 
95
- See [Building SDKs](/sui/sdk-building) for more on creating client extensions.
131
+ See [Building SDKs](/sui/sdk-building) for creating your own.