@mysten/sui 2.25.0 → 2.26.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 (35) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/bcs/bcs.d.mts +6 -6
  3. package/dist/bcs/index.d.mts +36 -36
  4. package/dist/client/errors.d.mts +31 -2
  5. package/dist/client/errors.d.mts.map +1 -1
  6. package/dist/client/errors.mjs +15 -13
  7. package/dist/client/errors.mjs.map +1 -1
  8. package/dist/client/index.d.mts +2 -2
  9. package/dist/client/index.mjs +2 -2
  10. package/dist/graphql/core.d.mts.map +1 -1
  11. package/dist/graphql/core.mjs +11 -5
  12. package/dist/graphql/core.mjs.map +1 -1
  13. package/dist/grpc/core.d.mts.map +1 -1
  14. package/dist/grpc/core.mjs +29 -12
  15. package/dist/grpc/core.mjs.map +1 -1
  16. package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.d.mts +4 -4
  17. package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
  18. package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
  19. package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
  20. package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
  21. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +4 -4
  22. package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.client.d.mts +4 -4
  23. package/dist/jsonRpc/core.d.mts.map +1 -1
  24. package/dist/jsonRpc/core.mjs +55 -15
  25. package/dist/jsonRpc/core.mjs.map +1 -1
  26. package/dist/transactions/Transaction.d.mts +9 -9
  27. package/dist/version.mjs +1 -1
  28. package/dist/version.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/src/client/errors.ts +39 -23
  31. package/src/client/index.ts +9 -1
  32. package/src/graphql/core.ts +11 -6
  33. package/src/grpc/core.ts +90 -65
  34. package/src/jsonRpc/core.ts +83 -18
  35. package/src/version.ts +1 -1
@@ -1,7 +1,6 @@
1
1
  // Copyright (c) Mysten Labs, Inc.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import type { ObjectResponseError } from '../jsonRpc/index.js';
5
4
  import type { SuiClientTypes } from './types.js';
6
5
 
7
6
  export class SuiClientError extends Error {}
@@ -18,33 +17,50 @@ export class SimulationError extends SuiClientError {
18
17
  }
19
18
  }
20
19
 
20
+ export type ObjectErrorReason = 'notFound' | 'deleted' | 'unknown';
21
+
22
+ export interface ObjectErrorOptions {
23
+ /** A transport-neutral reason shared by all Core API clients. */
24
+ reason: ObjectErrorReason;
25
+ /** The requested object ID, when the lookup identifies one. */
26
+ objectId?: string;
27
+ /** The original transport error or response. */
28
+ cause?: unknown;
29
+ }
30
+
31
+ /** An error returned for an individual object lookup. */
21
32
  export class ObjectError extends SuiClientError {
33
+ /** The transport's error code. Use `reason` for transport-neutral handling. */
22
34
  code: string;
35
+ /** A transport-neutral reason shared by all Core API clients. */
36
+ readonly reason: ObjectErrorReason;
37
+ /** The requested object ID, when the lookup identifies one. */
38
+ readonly objectId?: string;
23
39
 
24
- constructor(code: string, message: string) {
25
- super(message);
40
+ constructor(code: string, message: string, options?: ObjectErrorOptions) {
41
+ super(message, { cause: options?.cause });
26
42
  this.code = code;
43
+ this.reason = options?.reason ?? 'unknown';
44
+ this.objectId = options?.objectId;
27
45
  }
46
+ }
47
+
48
+ export type TransactionErrorReason = 'notFound';
49
+
50
+ const TRANSACTION_ERROR_MESSAGES: Record<TransactionErrorReason, (digest: string) => string> = {
51
+ notFound: (digest) => `Transaction ${digest} not found`,
52
+ };
53
+
54
+ /** An error returned by a transaction lookup. */
55
+ export class TransactionError extends SuiClientError {
56
+ /** A transport-neutral reason shared by all Core API clients. */
57
+ readonly reason: TransactionErrorReason;
58
+ /** The requested transaction digest. */
59
+ readonly digest: string;
28
60
 
29
- static fromResponse(response: ObjectResponseError, objectId?: string): ObjectError {
30
- switch (response.code) {
31
- case 'notExists':
32
- return new ObjectError(response.code, `Object ${response.object_id} does not exist`);
33
- case 'dynamicFieldNotFound':
34
- return new ObjectError(
35
- response.code,
36
- `Dynamic field not found for object ${response.parent_object_id}`,
37
- );
38
- case 'deleted':
39
- return new ObjectError(response.code, `Object ${response.object_id} has been deleted`);
40
- case 'displayError':
41
- return new ObjectError(response.code, `Display error: ${response.error}`);
42
- case 'unknown':
43
- default:
44
- return new ObjectError(
45
- response.code,
46
- `Unknown error while loading object${objectId ? ` ${objectId}` : ''}`,
47
- );
48
- }
61
+ constructor(reason: TransactionErrorReason, digest: string, options?: { cause?: unknown }) {
62
+ super(TRANSACTION_ERROR_MESSAGES[reason](digest), options);
63
+ this.reason = reason;
64
+ this.digest = digest;
49
65
  }
50
66
  }
@@ -22,7 +22,15 @@ export {
22
22
  type ClientWithCoreApi,
23
23
  };
24
24
 
25
- export { SimulationError } from './errors.js';
25
+ export {
26
+ ObjectError,
27
+ SimulationError,
28
+ SuiClientError,
29
+ TransactionError,
30
+ type ObjectErrorOptions,
31
+ type ObjectErrorReason,
32
+ type TransactionErrorReason,
33
+ } from './errors.js';
26
34
 
27
35
  export { ClientCache, type ClientCacheOptions } from './cache.js';
28
36
  export { type NamedPackagesOverrides } from './mvr.js';
@@ -38,7 +38,7 @@ import {
38
38
  VerifyZkLoginSignatureDocument,
39
39
  ZkLoginIntentScope,
40
40
  } from './generated/queries.js';
41
- import { ObjectError, SimulationError } from '../client/errors.js';
41
+ import { ObjectError, SimulationError, TransactionError } from '../client/errors.js';
42
42
  import { chunk, fromBase64, toBase64 } from '@mysten/utils';
43
43
  import { normalizeStructTag, normalizeSuiAddress } from '../utils/sui-types.js';
44
44
  import {
@@ -86,6 +86,7 @@ export class GraphQLCoreClient extends CoreClient {
86
86
  >(
87
87
  options: GraphQLQueryOptions<Result, Variables>,
88
88
  getData?: (result: Result) => Data,
89
+ createMissingDataError?: () => Error,
89
90
  ): Promise<NonNullable<Data>> {
90
91
  const { data, errors } = await this.#graphqlClient.query(options);
91
92
 
@@ -94,7 +95,7 @@ export class GraphQLCoreClient extends CoreClient {
94
95
  const extractedData = data && (getData ? getData(data) : data);
95
96
 
96
97
  if (extractedData == null) {
97
- throw new Error('Missing response data');
98
+ throw createMissingDataError?.() ?? new Error('Missing response data');
98
99
  }
99
100
 
100
101
  return extractedData as NonNullable<Data>;
@@ -124,11 +125,14 @@ export class GraphQLCoreClient extends CoreClient {
124
125
  );
125
126
  results.push(
126
127
  ...batch
127
- .map((id) => normalizeSuiAddress(id))
128
+ .map((objectId) => ({ objectId, normalized: normalizeSuiAddress(objectId) }))
128
129
  .map(
129
- (id) =>
130
- page.find((obj) => obj?.address === id) ??
131
- new ObjectError('notFound', `Object ${id} not found`),
130
+ ({ objectId, normalized }) =>
131
+ page.find((obj) => obj?.address === normalized) ??
132
+ new ObjectError('notFound', `Object ${normalized} not found`, {
133
+ reason: 'notFound',
134
+ objectId,
135
+ }),
132
136
  )
133
137
  .map((obj) => {
134
138
  if (obj instanceof ObjectError) {
@@ -385,6 +389,7 @@ export class GraphQLCoreClient extends CoreClient {
385
389
  },
386
390
  },
387
391
  (result) => result.transaction,
392
+ () => new TransactionError('notFound', options.digest),
388
393
  );
389
394
 
390
395
  return parseTransaction(result, options.include);
package/src/grpc/core.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  // Copyright (c) Mysten Labs, Inc.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import type { CoreClientOptions, SuiClientTypes } from '../client/index.js';
5
- import { CoreClient, formatMoveAbortMessage, SimulationError } from '../client/index.js';
4
+ import type { CoreClientOptions, ObjectErrorReason, SuiClientTypes } from '../client/index.js';
5
+ import {
6
+ CoreClient,
7
+ formatMoveAbortMessage,
8
+ ObjectError,
9
+ SimulationError,
10
+ TransactionError,
11
+ } from '../client/index.js';
6
12
  import { raceSignal } from '../client/mvr.js';
7
13
  import type { SuiGrpcClient } from './client.js';
8
14
  import type { Owner } from './proto/sui/rpc/v2/owner.js';
@@ -55,6 +61,7 @@ import type { QueryEnd, QueryOptions } from './proto/sui/rpc/v2/query_options.js
55
61
  import { Ordering, QueryEndReason } from './proto/sui/rpc/v2/query_options.js';
56
62
  import type { ResolvedPagination } from '../client/query-filters.js';
57
63
  import { RpcError } from '@protobuf-ts/runtime-rpc';
64
+ import { GrpcStatusCode } from '@protobuf-ts/grpcweb-transport';
58
65
  import {
59
66
  resolveEventFilter,
60
67
  resolvePagination,
@@ -69,8 +76,8 @@ export interface GrpcCoreClientOptions extends CoreClientOptions {
69
76
 
70
77
  function isNameServiceResolutionMiss(error: unknown): boolean {
71
78
  if (!(error instanceof RpcError)) return false;
72
- if (error.code === 'NOT_FOUND') return true;
73
- if (error.code !== 'RESOURCE_EXHAUSTED') return false;
79
+ if (error.code === GrpcStatusCode[GrpcStatusCode.NOT_FOUND]) return true;
80
+ if (error.code !== GrpcStatusCode[GrpcStatusCode.RESOURCE_EXHAUSTED]) return false;
74
81
 
75
82
  try {
76
83
  // The gRPC service currently reports expired names as RESOURCE_EXHAUSTED without a
@@ -124,51 +131,62 @@ export class GrpcCoreClient extends CoreClient {
124
131
  );
125
132
 
126
133
  results.push(
127
- ...response.response.objects.map((object): SuiClientTypes.Object<Include> | Error => {
128
- if (object.result.oneofKind === 'error') {
129
- // TODO: improve error handling
130
- return new Error(object.result.error.message);
131
- }
132
-
133
- if (object.result.oneofKind !== 'object') {
134
- return new Error('Unexpected result type');
135
- }
136
-
137
- const bcsContent = object.result.object.contents?.value ?? undefined;
138
- const objectBcs = object.result.object.bcs?.value ?? undefined;
139
-
140
- // Package objects have type "package" which is not a struct tag, so don't normalize it
141
- const objectType = object.result.object.objectType;
142
- const type =
143
- objectType && objectType.includes('::')
144
- ? normalizeStructTag(objectType)
145
- : (objectType ?? '');
146
-
147
- const jsonContent = options.include?.json
148
- ? object.result.object.json
149
- ? (Value.toJson(object.result.object.json) as Record<string, unknown>)
150
- : null
151
- : undefined;
152
-
153
- const displayData = mapDisplayProto(
154
- options.include?.display,
155
- object.result.object.display,
156
- );
157
-
158
- return {
159
- objectId: object.result.object.objectId!,
160
- version: object.result.object.version?.toString()!,
161
- digest: object.result.object.digest!,
162
- content: bcsContent as SuiClientTypes.Object<Include>['content'],
163
- owner: mapOwner(object.result.object.owner)!,
164
- type,
165
- previousTransaction: (object.result.object.previousTransaction ??
166
- undefined) as SuiClientTypes.Object<Include>['previousTransaction'],
167
- objectBcs: objectBcs as SuiClientTypes.Object<Include>['objectBcs'],
168
- json: jsonContent as SuiClientTypes.Object<Include>['json'],
169
- display: displayData as SuiClientTypes.Object<Include>['display'],
170
- };
171
- }),
134
+ ...response.response.objects.map(
135
+ (object, index): SuiClientTypes.Object<Include> | ObjectError => {
136
+ if (object.result.oneofKind === 'error') {
137
+ const error = object.result.error;
138
+ const reason: ObjectErrorReason =
139
+ error.code === GrpcStatusCode.NOT_FOUND ? 'notFound' : 'unknown';
140
+ return new ObjectError(String(error.code), error.message, {
141
+ cause: error,
142
+ reason,
143
+ objectId: batch[index],
144
+ });
145
+ }
146
+
147
+ if (object.result.oneofKind !== 'object') {
148
+ return new ObjectError('unknown', 'Unexpected result type', {
149
+ reason: 'unknown',
150
+ objectId: batch[index],
151
+ });
152
+ }
153
+
154
+ const bcsContent = object.result.object.contents?.value ?? undefined;
155
+ const objectBcs = object.result.object.bcs?.value ?? undefined;
156
+
157
+ // Package objects have type "package" which is not a struct tag, so don't normalize it
158
+ const objectType = object.result.object.objectType;
159
+ const type =
160
+ objectType && objectType.includes('::')
161
+ ? normalizeStructTag(objectType)
162
+ : (objectType ?? '');
163
+
164
+ const jsonContent = options.include?.json
165
+ ? object.result.object.json
166
+ ? (Value.toJson(object.result.object.json) as Record<string, unknown>)
167
+ : null
168
+ : undefined;
169
+
170
+ const displayData = mapDisplayProto(
171
+ options.include?.display,
172
+ object.result.object.display,
173
+ );
174
+
175
+ return {
176
+ objectId: object.result.object.objectId!,
177
+ version: object.result.object.version?.toString()!,
178
+ digest: object.result.object.digest!,
179
+ content: bcsContent as SuiClientTypes.Object<Include>['content'],
180
+ owner: mapOwner(object.result.object.owner)!,
181
+ type,
182
+ previousTransaction: (object.result.object.previousTransaction ??
183
+ undefined) as SuiClientTypes.Object<Include>['previousTransaction'],
184
+ objectBcs: objectBcs as SuiClientTypes.Object<Include>['objectBcs'],
185
+ json: jsonContent as SuiClientTypes.Object<Include>['json'],
186
+ display: displayData as SuiClientTypes.Object<Include>['display'],
187
+ };
188
+ },
189
+ ),
172
190
  );
173
191
  }
174
192
 
@@ -362,25 +380,32 @@ export class GrpcCoreClient extends CoreClient {
362
380
  async getTransaction<Include extends SuiClientTypes.TransactionInclude = {}>(
363
381
  options: SuiClientTypes.GetTransactionOptions<Include>,
364
382
  ): Promise<SuiClientTypes.TransactionResult<Include>> {
365
- const { response } = await this.#client.ledgerService.getTransaction(
366
- {
367
- digest: options.digest,
368
- readMask: {
369
- paths: transactionReadMaskPaths(options.include),
383
+ try {
384
+ const { response } = await this.#client.ledgerService.getTransaction(
385
+ {
386
+ digest: options.digest,
387
+ readMask: {
388
+ paths: transactionReadMaskPaths(options.include),
389
+ },
370
390
  },
371
- },
372
- { abort: options.signal },
373
- );
391
+ { abort: options.signal },
392
+ );
374
393
 
375
- if (!response.transaction) {
376
- throw new Error(`Transaction ${options.digest} not found`);
377
- }
394
+ if (!response.transaction) {
395
+ throw new TransactionError('notFound', options.digest);
396
+ }
378
397
 
379
- return withProtoJson(
380
- parseGrpcTransactionResponse(response.transaction, { include: options.include }),
381
- options.include,
382
- () => ExecutedTransaction.toJson(response.transaction!),
383
- );
398
+ return withProtoJson(
399
+ parseGrpcTransactionResponse(response.transaction, { include: options.include }),
400
+ options.include,
401
+ () => ExecutedTransaction.toJson(response.transaction!),
402
+ );
403
+ } catch (error) {
404
+ if (error instanceof RpcError && error.code === GrpcStatusCode[GrpcStatusCode.NOT_FOUND]) {
405
+ throw new TransactionError('notFound', options.digest, { cause: error });
406
+ }
407
+ throw error;
408
+ }
384
409
  }
385
410
  async executeTransaction<Include extends SuiClientTypes.TransactionInclude = {}>(
386
411
  options: SuiClientTypes.ExecuteTransactionOptions<Include>,
@@ -10,6 +10,7 @@ import type {
10
10
  EventId,
11
11
  ExecutionStatus as JsonRpcExecutionStatus,
12
12
  ObjectOwner,
13
+ ObjectResponseError,
13
14
  SuiMoveAbilitySet,
14
15
  SuiMoveAbort,
15
16
  SuiMoveNormalizedType,
@@ -36,16 +37,73 @@ import { deriveDynamicFieldID } from '../utils/dynamic-fields.js';
36
37
  import { SUI_FRAMEWORK_ADDRESS, SUI_SYSTEM_ADDRESS } from '../utils/constants.js';
37
38
  import { CoreClient } from '../client/core.js';
38
39
  import type { SuiClientTypes } from '../client/types.js';
39
- import { ObjectError } from '../client/errors.js';
40
+ import { ObjectError, TransactionError } from '../client/errors.js';
40
41
  import {
41
42
  formatMoveAbortMessage,
42
43
  parseTransactionBcs,
43
44
  parseTransactionEffectsBcs,
44
45
  } from '../client/index.js';
45
46
  import type { SuiJsonRpcClient } from './client.js';
47
+ import { JsonRpcError } from './errors.js';
46
48
 
47
49
  const MAX_GAS = 50_000_000_000;
48
50
 
51
+ function mapJsonRpcObjectError(
52
+ response: ObjectResponseError,
53
+ requestedObjectId?: string,
54
+ ): ObjectError {
55
+ switch (response.code) {
56
+ case 'notExists':
57
+ return new ObjectError(response.code, `Object ${response.object_id} does not exist`, {
58
+ cause: response,
59
+ reason: 'notFound',
60
+ objectId: requestedObjectId ?? response.object_id,
61
+ });
62
+ case 'dynamicFieldNotFound':
63
+ return new ObjectError(
64
+ response.code,
65
+ `Dynamic field not found for object ${response.parent_object_id}`,
66
+ {
67
+ cause: response,
68
+ reason: 'notFound',
69
+ objectId: requestedObjectId ?? response.parent_object_id,
70
+ },
71
+ );
72
+ case 'deleted':
73
+ return new ObjectError(response.code, `Object ${response.object_id} has been deleted`, {
74
+ cause: response,
75
+ reason: 'deleted',
76
+ objectId: requestedObjectId ?? response.object_id,
77
+ });
78
+ case 'displayError':
79
+ return new ObjectError(response.code, `Display error: ${response.error}`, {
80
+ cause: response,
81
+ reason: 'unknown',
82
+ objectId: requestedObjectId,
83
+ });
84
+ case 'unknown':
85
+ default:
86
+ return new ObjectError(
87
+ response.code,
88
+ `Unknown error while loading object${requestedObjectId ? ` ${requestedObjectId}` : ''}`,
89
+ {
90
+ cause: response,
91
+ reason: 'unknown',
92
+ objectId: requestedObjectId,
93
+ },
94
+ );
95
+ }
96
+ }
97
+
98
+ function isJsonRpcTransactionNotFound(error: unknown, digest: string): boolean {
99
+ if (!(error instanceof JsonRpcError) || error.code !== -32602) return false;
100
+
101
+ return (
102
+ error.message === `Invalid Params: Transaction ${digest} not found` ||
103
+ error.message === `Could not find the referenced transaction [TransactionDigest(${digest})].`
104
+ );
105
+ }
106
+
49
107
  function parseJsonRpcExecutionStatus(
50
108
  status: JsonRpcExecutionStatus,
51
109
  abortError?: SuiMoveAbort | null,
@@ -160,7 +218,7 @@ export class JSONRpcCoreClient extends CoreClient {
160
218
 
161
219
  for (const [idx, object] of objects.entries()) {
162
220
  if (object.error) {
163
- results.push(ObjectError.fromResponse(object.error, batch[idx]));
221
+ results.push(mapJsonRpcObjectError(object.error, batch[idx]));
164
222
  } else {
165
223
  results.push(parseObject(object.data!, options.include));
166
224
  }
@@ -211,7 +269,7 @@ export class JSONRpcCoreClient extends CoreClient {
211
269
  return {
212
270
  objects: objects.data.map((result) => {
213
271
  if (result.error) {
214
- throw ObjectError.fromResponse(result.error);
272
+ throw mapJsonRpcObjectError(result.error);
215
273
  }
216
274
 
217
275
  return parseObject(result.data!, options.include);
@@ -340,22 +398,29 @@ export class JSONRpcCoreClient extends CoreClient {
340
398
  async getTransaction<Include extends SuiClientTypes.TransactionInclude = {}>(
341
399
  options: SuiClientTypes.GetTransactionOptions<Include>,
342
400
  ): Promise<SuiClientTypes.TransactionResult<Include>> {
343
- const transaction = await this.#jsonRpcClient.getTransactionBlock({
344
- digest: options.digest,
345
- options: {
346
- // showRawInput is always needed to extract signatures from SenderSignedData
347
- showRawInput: true,
348
- // showEffects is always needed to get status
349
- showEffects: true,
350
- showObjectChanges: options.include?.objectTypes ?? false,
351
- showRawEffects: options.include?.effects ?? false,
352
- showEvents: options.include?.events ?? false,
353
- showBalanceChanges: options.include?.balanceChanges ?? false,
354
- },
355
- signal: options.signal,
356
- });
401
+ try {
402
+ const transaction = await this.#jsonRpcClient.getTransactionBlock({
403
+ digest: options.digest,
404
+ options: {
405
+ // showRawInput is always needed to extract signatures from SenderSignedData
406
+ showRawInput: true,
407
+ // showEffects is always needed to get status
408
+ showEffects: true,
409
+ showObjectChanges: options.include?.objectTypes ?? false,
410
+ showRawEffects: options.include?.effects ?? false,
411
+ showEvents: options.include?.events ?? false,
412
+ showBalanceChanges: options.include?.balanceChanges ?? false,
413
+ },
414
+ signal: options.signal,
415
+ });
357
416
 
358
- return parseTransaction(transaction, options.include);
417
+ return parseTransaction(transaction, options.include);
418
+ } catch (error) {
419
+ if (isJsonRpcTransactionNotFound(error, options.digest)) {
420
+ throw new TransactionError('notFound', options.digest, { cause: error });
421
+ }
422
+ throw error;
423
+ }
359
424
  }
360
425
  /**
361
426
  * @deprecated JSON-RPC APIs are deprecated in the Sui TypeScript SDK. Use `SuiGrpcClient`
package/src/version.ts CHANGED
@@ -3,4 +3,4 @@
3
3
 
4
4
  // This file is generated by genversion.mjs. Do not edit it directly.
5
5
 
6
- export const PACKAGE_VERSION = '2.25.0';
6
+ export const PACKAGE_VERSION = '2.26.0';