@mysten/sui 2.24.0 → 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 (65) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/bcs/bcs.d.mts +6 -6
  3. package/dist/bcs/index.d.mts +36 -36
  4. package/dist/client/core.d.mts.map +1 -1
  5. package/dist/client/core.mjs +4 -1
  6. package/dist/client/core.mjs.map +1 -1
  7. package/dist/client/mvr.d.mts.map +1 -1
  8. package/dist/client/mvr.mjs +1 -0
  9. package/dist/client/mvr.mjs.map +1 -1
  10. package/dist/cryptography/signature.d.mts +6 -6
  11. package/dist/graphql/client.d.mts +5 -1
  12. package/dist/graphql/client.d.mts.map +1 -1
  13. package/dist/graphql/client.mjs +15 -2
  14. package/dist/graphql/client.mjs.map +1 -1
  15. package/dist/graphql/core.d.mts +4 -4
  16. package/dist/graphql/core.d.mts.map +1 -1
  17. package/dist/graphql/core.mjs +50 -13
  18. package/dist/graphql/core.mjs.map +1 -1
  19. package/dist/graphql/generated/tada-env.d.mts +16 -0
  20. package/dist/grpc/client.d.mts +5 -1
  21. package/dist/grpc/client.d.mts.map +1 -1
  22. package/dist/grpc/client.mjs +14 -2
  23. package/dist/grpc/client.mjs.map +1 -1
  24. package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
  25. package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
  26. package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
  27. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +4 -4
  28. package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.client.d.mts +4 -4
  29. package/dist/jsonRpc/client.d.mts.map +1 -1
  30. package/dist/jsonRpc/client.mjs +60 -15
  31. package/dist/jsonRpc/client.mjs.map +1 -1
  32. package/dist/jsonRpc/core.d.mts +1 -1
  33. package/dist/jsonRpc/core.d.mts.map +1 -1
  34. package/dist/jsonRpc/core.mjs +18 -7
  35. package/dist/jsonRpc/core.mjs.map +1 -1
  36. package/dist/transactions/Transaction.d.mts +3 -3
  37. package/dist/transactions/data/v1.d.mts +220 -220
  38. package/dist/transactions/data/v1.d.mts.map +1 -1
  39. package/dist/transactions/data/v2.d.mts +16 -16
  40. package/dist/transactions/data/v2.d.mts.map +1 -1
  41. package/dist/version.mjs +1 -1
  42. package/dist/version.mjs.map +1 -1
  43. package/dist/zklogin/bcs.d.mts +14 -14
  44. package/docs/bcs.md +2 -2
  45. package/docs/clients/core.md +150 -710
  46. package/docs/clients/executing.md +113 -0
  47. package/docs/clients/graphql.md +80 -70
  48. package/docs/clients/grpc.md +223 -208
  49. package/docs/clients/index.md +56 -67
  50. package/docs/clients/querying.md +539 -0
  51. package/docs/llms-index.md +6 -5
  52. package/docs/migrations/sui-2.0/json-rpc-migration.md +3 -1
  53. package/docs/transactions/signing-and-execution.md +8 -28
  54. package/package.json +1 -1
  55. package/src/client/core.ts +1 -0
  56. package/src/client/mvr.ts +6 -0
  57. package/src/graphql/client.ts +29 -2
  58. package/src/graphql/core.ts +42 -10
  59. package/src/graphql/generated/schema.graphql +11 -1
  60. package/src/graphql/generated/tada-env.ts +20 -0
  61. package/src/grpc/client.ts +28 -2
  62. package/src/jsonRpc/client.ts +15 -0
  63. package/src/jsonRpc/core.ts +19 -6
  64. package/src/version.ts +1 -1
  65. package/docs/clients/json-rpc.md +0 -243
@@ -1,115 +1,70 @@
1
1
  # SuiGrpcClient
2
2
 
3
- > Connect to Sui through gRPC with the recommended 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. It is the recommended default client
6
- for application code and SDK integrations.
7
-
8
- For more complete details on what is available through this API see the
9
- [gRPC API docs](https://docs.sui.io/develop/accessing-data/grpc/what-is-grpc).
10
-
11
- ## Creating a gRPC client
12
-
13
- 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.
14
8
 
15
9
  ```typescript
16
10
 
17
- const grpcClient = new SuiGrpcClient({
18
- network: 'testnet',
19
- baseUrl: 'https://fullnode.testnet.sui.io:443',
11
+ const client = new SuiGrpcClient({
12
+ network: 'mainnet',
13
+ baseUrl: 'https://fullnode.mainnet.sui.io:443',
20
14
  });
21
15
  ```
22
16
 
23
17
  For local development:
24
18
 
25
19
  ```typescript
26
- const grpcClient = new SuiGrpcClient({
20
+ const client = new SuiGrpcClient({
27
21
  network: 'localnet',
28
22
  baseUrl: 'http://127.0.0.1:9000',
29
23
  });
30
24
  ```
31
25
 
32
- ## Using top-level methods
33
-
34
- Use top-level methods for most application code. These methods match the shared
35
- [Core API](/sui/clients/core) option and response shapes, so the same calls can also be written as
36
- `grpcClient.core.<method>()` when SDK code needs the transport-agnostic `ClientWithCoreApi`
37
- contract.
38
-
39
- ```typescript
40
-
41
- const grpcClient = new SuiGrpcClient({
42
- network: 'testnet',
43
- baseUrl: 'https://fullnode.testnet.sui.io:443',
44
- });
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.
45
30
 
46
- const { balance } = await grpcClient.getBalance({
47
- owner: '<OWNER_ADDRESS>',
48
- });
31
+ ## gRPC-specific options
49
32
 
50
- const { object } = await grpcClient.getObject({
51
- objectId: '<OBJECT_ID>',
52
- include: { content: true, display: true },
53
- });
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:
54
35
 
55
- const coins = await grpcClient.listCoins({
56
- owner: '<OWNER_ADDRESS>',
57
- coinType: '0x2::sui::SUI',
58
- });
59
- ```
36
+ | Option | Available on |
37
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------- |
38
+ | `include.protoJson` | `getTransaction`, `executeTransaction`, `signAndExecuteTransaction`, `waitForTransaction`, `simulateTransaction` |
60
39
 
61
- Common top-level methods:
62
-
63
- | Category | Methods |
64
- | -------------- | ----------------------------------------------------------------------------------------------------------------- |
65
- | Objects | `getObject`, `getObjects`, `listOwnedObjects`, `listDynamicFields`, `getDynamicField` |
66
- | Coins | `listCoins`, `getBalance`, `listBalances`, `getCoinMetadata` |
67
- | Transactions | `getTransaction`, `executeTransaction`, `signAndExecuteTransaction`, `waitForTransaction` |
68
- | Simulation | `simulateTransaction` |
69
- | Queries | `listTransactions`, `listEvents` |
70
- | Move and names | `getMoveFunction`, `resolveNameServiceAddress`, `defaultNameServiceName`, `mvr.resolvePackage`, `mvr.resolveType` |
71
- | Verification | `verifyZkLoginSignature` |
72
-
73
- ### gRPC-specific top-level data
74
-
75
- Top-level gRPC methods are a superset of the shared Core API where the transport can expose useful
76
- gRPC data directly:
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:
77
42
 
78
43
  ```typescript
79
- const result = await grpcClient.getTransaction({
80
- digest: '<TRANSACTION_DIGEST>',
44
+ const result = await client.getTransaction({
45
+ digest: 'ABC123...',
81
46
  include: {
82
47
  effects: true,
83
48
  protoJson: true,
84
49
  },
85
50
  });
86
51
 
87
- const tx = result.Transaction ?? result.FailedTransaction;
88
- console.log(tx.digest, result.protoJson);
52
+ const transaction = result.Transaction ?? result.FailedTransaction;
53
+ console.log(transaction.digest, result.protoJson);
89
54
  ```
90
55
 
91
- gRPC-specific options include:
92
-
93
- | Option | Methods |
94
- | -------------------------- | ----------------------------------------------------------------------------------------- |
95
- | `include.protoJson` | `getTransaction`, `executeTransaction`, `signAndExecuteTransaction`, `waitForTransaction` |
96
- | `include.protoJson` | `simulateTransaction` |
97
- | `doGasSelection` | `simulateTransaction` |
98
- | `include: { value: true }` | `listDynamicFields` |
99
-
100
56
  ## Transport options
101
57
 
102
58
  By default, `SuiGrpcClient` uses `GrpcWebFetchTransport` from
103
59
  [protobuf-ts](https://github.com/timostamm/protobuf-ts), which works in browsers and Node.js through
104
- the Fetch API. You can also provide a custom transport for advanced use cases.
105
-
106
- The `GrpcWebFetchTransport` class, `GrpcWebOptions` type, and `RpcTransport` type are all
107
- 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.
108
63
 
109
64
  ### gRPC-web transport (default)
110
65
 
111
- The default transport uses the gRPC-web protocol over HTTP/1.1 or HTTP/2. You can customize it by
112
- 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:
113
68
 
114
69
  ```typescript
115
70
 
@@ -119,7 +74,7 @@ const transport = new GrpcWebFetchTransport({
119
74
  // Additional transport options like fetchInit
120
75
  });
121
76
 
122
- const grpcClient = new SuiGrpcClient({
77
+ const client = new SuiGrpcClient({
123
78
  network: 'testnet',
124
79
  transport,
125
80
  });
@@ -127,18 +82,14 @@ const grpcClient = new SuiGrpcClient({
127
82
 
128
83
  ### Native gRPC transport
129
84
 
130
- For server-side applications (Node.js, Bun, and others), you can use the native gRPC transport with
131
- `@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
132
87
  rather than the gRPC-web translation layer.
133
88
 
134
- Install the required packages:
135
-
136
- ```bash
89
+ ```npm
137
90
  npm install @protobuf-ts/grpc-transport @grpc/grpc-js
138
91
  ```
139
92
 
140
- Then create the client with a `GrpcTransport`:
141
-
142
93
  ```typescript
143
94
 
144
95
  const transport = new GrpcTransport({
@@ -146,97 +97,71 @@ const transport = new GrpcTransport({
146
97
  channelCredentials: ChannelCredentials.createSsl(),
147
98
  });
148
99
 
149
- const grpcClient = new SuiGrpcClient({
100
+ const client = new SuiGrpcClient({
150
101
  network: 'testnet',
151
102
  transport,
152
103
  });
153
104
  ```
154
105
 
155
- For local development without TLS:
106
+ For local development without TLS, use `ChannelCredentials.createInsecure()` and a plain
107
+ `host: '127.0.0.1:9000'`.
156
108
 
157
- ```typescript
109
+ ## Read masks
158
110
 
159
- const transport = new GrpcTransport({
160
- host: '127.0.0.1:9000',
161
- channelCredentials: ChannelCredentials.createInsecure(),
162
- });
163
-
164
- const grpcClient = new SuiGrpcClient({
165
- network: 'localnet',
166
- transport,
167
- });
168
- ```
169
-
170
- ## Using service clients
171
-
172
- The `SuiGrpcClient` exposes several service clients for lower-level access to the gRPC API. These
173
- service clients are generated using [protobuf-ts](https://github.com/timostamm/protobuf-ts), which
174
- provides type-safe gRPC clients for TypeScript. For more details on how to use gRPC with Sui, see
175
- the [gRPC overview](https://docs.sui.io/develop/accessing-data/grpc/what-is-grpc).
176
-
177
- ### Prefer top-level methods first
178
-
179
- For common operations, call the top-level method before reaching for raw service clients:
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:
180
113
 
181
114
  ```typescript
182
-
183
- const grpcClient = new SuiGrpcClient({
184
- network: 'testnet',
185
- baseUrl: 'https://fullnode.testnet.sui.io:443',
186
- });
187
-
188
- await grpcClient.listCoins({
189
- owner: '<OWNER_ADDRESS>',
115
+ const { response } = await client.ledgerService.getTransaction({
116
+ digest: 'ABC123...',
117
+ readMask: { paths: ['digest', 'effects.status', 'transaction.sender'] },
190
118
  });
191
119
  ```
192
120
 
193
- Use the generated service clients directly when you need gRPC methods, read masks, streaming
194
- behavior, or filters that are not exposed by the top-level API.
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.
195
124
 
196
- ### 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.
197
128
 
198
- ```typescript
199
- const { response } = await grpcClient.transactionExecutionService.executeTransaction({
200
- transaction: {
201
- bcs: {
202
- value: transactionBytes,
203
- },
204
- },
205
- signatures: signatures.map((sig) => ({
206
- bcs: { value: fromBase64(sig) },
207
- signature: { oneofKind: undefined },
208
- })),
209
- });
129
+ ## Using service clients
210
130
 
211
- // IMPORTANT: Always check the transaction status
212
- if (!response.finality?.effects?.status?.success) {
213
- const error = response.finality?.effects?.status?.error;
214
- throw new Error(`Transaction failed: ${error || 'Unknown error'}`);
215
- }
216
- ```
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 |
217
144
 
218
- ### 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`:
219
147
 
220
148
  ```typescript
221
- // Get transaction by digest
222
- const { response } = await grpcClient.ledgerService.getTransaction({
223
- digest: '0x123...',
224
- });
149
+ const controller = new AbortController();
225
150
 
226
- // Get current epoch information
227
- const { response: epochInfo } = await grpcClient.ledgerService.getEpoch({});
151
+ const { response } = await client.nameService.lookupName(
152
+ { name: 'example.sui' },
153
+ { abort: controller.signal },
154
+ );
228
155
  ```
229
156
 
230
- The ledger service also provides streaming `listCheckpoints`, `listTransactions`, and `listEvents`
231
- RPCs. For most use cases, prefer the corresponding
232
- [core API query methods](/sui/clients/core#query-methods) (`listTransactions` and `listEvents`),
233
- which handle pagination and filter construction for you. The raw RPCs additionally support DNF
234
- filters (combined, negated, and additional predicates like `affected_address` and `package_write`)
235
- 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.
236
161
 
237
162
  ```typescript
238
- // Transactions that affected an address but were not sent by it:
239
- const stream = grpcClient.ledgerService.listTransactions({
163
+ // Transactions that affected an address but were not sent by it
164
+ const stream = client.ledgerService.listTransactions({
240
165
  filter: {
241
166
  terms: [
242
167
  {
@@ -258,21 +183,116 @@ const stream = grpcClient.ledgerService.listTransactions({
258
183
  },
259
184
  readMask: { paths: ['digest'] },
260
185
  });
186
+ ```
261
187
 
262
- for await (const frame of stream.responses) {
263
- if (frame.transaction) {
264
- console.log(frame.transaction.digest);
265
- }
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;
266
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
+ });
267
221
  ```
268
222
 
269
- ### Subscription service
223
+ `parseGrpcSimulateTransactionResponse` does the same for `SimulateTransactionResponse`.
270
224
 
271
- Subscribe to filtered, real-time streams of checkpoints, transactions, or events. Streams begin at
272
- 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:
273
247
 
274
248
  ```typescript
275
- 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({
276
296
  filter: {
277
297
  terms: [
278
298
  {
@@ -290,71 +310,66 @@ const stream = grpcClient.subscriptionService.subscribeTransactions({
290
310
 
291
311
  for await (const frame of stream.responses) {
292
312
  if (frame.transaction) {
293
- console.log(frame.transaction.digest);
313
+ console.log(frame.transaction.digest, frame.transaction.effects?.status?.success);
294
314
  }
295
315
  }
296
316
  ```
297
317
 
298
- ### State service
318
+ Omit `filter` to receive everything.
299
319
 
300
- ```typescript
301
- // List owned objects
302
- const { response } = await grpcClient.stateService.listOwnedObjects({
303
- owner: '0xabc...',
304
- objectType: '0x2::coin::Coin<0x2::sui::SUI>',
305
- });
320
+ ### Frames and watermarks
306
321
 
307
- // Get dynamic fields
308
- const { response: fields } = await grpcClient.stateService.listDynamicFields({
309
- parent: '0x123...',
310
- });
311
- ```
312
-
313
- ### Move package service
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:
314
328
 
315
329
  ```typescript
316
- // Get function information
317
- const { response } = await grpcClient.movePackageService.getFunction({
318
- packageId: '0x2',
319
- moduleName: 'coin',
320
- name: 'value',
321
- });
322
- ```
330
+ let lastCursor: Uint8Array | undefined;
323
331
 
324
- ### Name service
332
+ for await (const frame of stream.responses) {
333
+ lastCursor = frame.watermark?.cursor ?? lastCursor;
325
334
 
326
- ```typescript
327
- const { address } = await grpcClient.resolveNameServiceAddress({
328
- name: 'example.sui',
329
- });
335
+ if (frame.transaction) {
336
+ await handleTransaction(frame.transaction);
337
+ }
338
+ }
330
339
  ```
331
340
 
332
- Use the raw name service when you need the complete gRPC `NameRecord` instead of only its target
333
- address:
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`:
334
345
 
335
346
  ```typescript
336
- const { response } = await grpcClient.nameService.lookupName({
337
- name: 'example.sui',
338
- });
347
+ const controller = new AbortController();
339
348
 
340
- const { response: reverseResponse } = await grpcClient.nameService.reverseLookupName({
341
- address: '0xabc...',
342
- });
349
+ const stream = client.subscriptionService.subscribeEvents(
350
+ { readMask: { paths: ['event_type', 'transaction_digest'] } },
351
+ { abort: controller.signal },
352
+ );
353
+
354
+ // Later, to tear the stream down
355
+ controller.abort();
343
356
  ```
344
357
 
345
- ### Signature verification service
358
+ ### Recovering missed data
346
359
 
347
- ```typescript
348
- // Verify a signature
349
- const { response } = await grpcClient.signatureVerificationService.verifySignature({
350
- message: {
351
- name: 'TransactionData',
352
- value: messageBytes,
353
- },
354
- signature: {
355
- bcs: { value: signatureBytes },
356
- signature: { oneofKind: undefined },
357
- },
358
- jwks: [],
359
- });
360
- ```
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.