@0xobelisk/sui-client 1.0.6 → 1.0.8

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.
@@ -0,0 +1,28 @@
1
+ export declare class BaseError extends Error {
2
+ code: number;
3
+ type: string;
4
+ constructor(message: string, code: number, type: string);
5
+ }
6
+ export declare class HttpError extends BaseError {
7
+ constructor(message: string, code: number);
8
+ }
9
+ export declare class GraphQLError extends BaseError {
10
+ constructor(message: string);
11
+ }
12
+ export declare class ValidationError extends BaseError {
13
+ constructor(message: string);
14
+ }
15
+ export declare class NotFoundError extends BaseError {
16
+ constructor(message: string);
17
+ }
18
+ export declare class ParseError extends BaseError {
19
+ constructor(message: string);
20
+ }
21
+ export type ErrorType = BaseError | Error | HttpError | NotFoundError | ValidationError | ParseError | GraphQLError;
22
+ export declare function handleError(error: ErrorType): {
23
+ code: number;
24
+ error: {
25
+ message: string;
26
+ type: string;
27
+ };
28
+ };
@@ -0,0 +1,21 @@
1
+ import { WebSocketInstance } from './ws-adapter';
2
+ export type FetchOptions = RequestInit & {
3
+ next?: {
4
+ revalidate?: boolean | number;
5
+ };
6
+ };
7
+ export declare class Http {
8
+ private customFetch?;
9
+ private apiEndpoint;
10
+ private graphqlEndpoint;
11
+ private wsEndpoint;
12
+ private defaultOptions?;
13
+ constructor(apiEndpoint: string, wsEndpoint: string, customFetch?: typeof fetch | undefined, defaultOptions?: FetchOptions);
14
+ private getFetch;
15
+ fetch(url: string): Promise<Response>;
16
+ fetchGraphql<T>({ query, variables, }: {
17
+ query: string;
18
+ variables?: any;
19
+ }): Promise<T>;
20
+ subscribe(names: string[], handleData: (data: any) => void): Promise<WebSocketInstance>;
21
+ }
@@ -0,0 +1,4 @@
1
+ export { Http } from './http';
2
+ export type { FetchOptions } from './http';
3
+ export * from './errors';
4
+ export * from './types';
@@ -0,0 +1 @@
1
+ export declare const ERROR: {};
@@ -0,0 +1,6 @@
1
+ export type WebSocketInstance = WebSocket;
2
+ export interface WebSocketConstructor {
3
+ new (url: string): WebSocket;
4
+ }
5
+ export declare function createWebSocketClient(url: string): WebSocketInstance;
6
+ export declare function isWebSocketSupported(): boolean;
@@ -0,0 +1,95 @@
1
+ import { Http } from '../http';
2
+ export interface OrderDirection {
3
+ ASC: 'ASC';
4
+ DESC: 'DESC';
5
+ }
6
+ export interface OrderBy {
7
+ field: string;
8
+ direction: OrderDirection['ASC'] | OrderDirection['DESC'];
9
+ }
10
+ export interface PageInfo {
11
+ hasNextPage: boolean;
12
+ hasPreviousPage: boolean;
13
+ startCursor?: string;
14
+ endCursor?: string;
15
+ }
16
+ export interface Transaction {
17
+ id: number;
18
+ checkpoint: number;
19
+ digest: string;
20
+ created_at: string;
21
+ }
22
+ export interface Schema {
23
+ id: number;
24
+ name: string;
25
+ key1?: string;
26
+ key2?: string;
27
+ value: string;
28
+ last_update_checkpoint: string;
29
+ last_update_digest: string;
30
+ is_removed: boolean;
31
+ created_at: string;
32
+ updated_at: string;
33
+ }
34
+ export interface Event {
35
+ id: number;
36
+ checkpoint: string;
37
+ digest: string;
38
+ name: string;
39
+ value: string;
40
+ created_at: string;
41
+ }
42
+ export interface ConnectionResponse<T> {
43
+ edges: Array<{
44
+ cursor: string;
45
+ node: T;
46
+ }>;
47
+ pageInfo: PageInfo;
48
+ }
49
+ export declare class SuiIndexerClient {
50
+ private http;
51
+ constructor(http: Http);
52
+ private fetchGraphql;
53
+ getTransactions(params?: {
54
+ first?: number;
55
+ after?: string;
56
+ last?: number;
57
+ before?: string;
58
+ checkpoint?: number;
59
+ orderBy?: OrderBy;
60
+ distinct?: boolean;
61
+ }): Promise<ConnectionResponse<Transaction>>;
62
+ getSchemas(params?: {
63
+ first?: number;
64
+ after?: string;
65
+ last?: number;
66
+ before?: string;
67
+ name?: string;
68
+ key1?: string;
69
+ key2?: string;
70
+ orderBy?: OrderBy;
71
+ distinct?: boolean;
72
+ }): Promise<ConnectionResponse<Schema>>;
73
+ getEvents(params?: {
74
+ first?: number;
75
+ after?: string;
76
+ last?: number;
77
+ before?: string;
78
+ name?: string;
79
+ checkpoint?: string;
80
+ orderBy?: OrderBy;
81
+ distinct?: boolean;
82
+ }): Promise<ConnectionResponse<Event>>;
83
+ getStorage({ name, key1, key2, first, after, last, before, orderBy, distinct, }: {
84
+ name: string;
85
+ key1?: string;
86
+ key2?: string;
87
+ first?: number;
88
+ after?: string;
89
+ last?: number;
90
+ before?: string;
91
+ orderBy?: OrderBy;
92
+ distinct?: boolean;
93
+ }): Promise<ConnectionResponse<Schema>>;
94
+ subscribe(names: string[], handleData: (data: any) => void): Promise<WebSocket>;
95
+ }
File without changes
@@ -6,5 +6,6 @@ export interface NetworkConfig {
6
6
  txExplorer: string;
7
7
  accountExplorer: string;
8
8
  explorer: string;
9
+ indexerUrl: string;
9
10
  }
10
11
  export declare const getDefaultURL: (networkType?: NetworkType) => NetworkConfig;
@@ -5,6 +5,7 @@ import type { Transaction, TransactionObjectArgument, TransactionResult, Argumen
5
5
  import type { SuiMoveNormalizedModules, DevInspectResults, SuiTransactionBlockResponse, DisplayFieldsResponse, MoveStruct, SuiMoveNormalizedEnum, SuiMoveNormalizedStruct } from '@mysten/sui/client';
6
6
  import { SuiTx } from '../libs/suiTxBuilder';
7
7
  import { SuiMoveMoudleFuncType } from '../libs/suiContractFactory/types';
8
+ import { FetchOptions } from '../libs/http';
8
9
  export declare const ObjectContentFields: import("superstruct").Struct<Record<string, any>, null>;
9
10
  export type ObjectContentFields = Infer<typeof ObjectContentFields>;
10
11
  export type DubheObjectData = {
@@ -28,6 +29,10 @@ export type DubheParams = {
28
29
  networkType?: NetworkType;
29
30
  packageId?: string;
30
31
  metadata?: SuiMoveNormalizedModules;
32
+ customFetch?: typeof fetch;
33
+ defaultOptions?: FetchOptions;
34
+ indexerUrl?: string;
35
+ indexerWsUrl?: string;
31
36
  };
32
37
  export type SchemaFieldType = {
33
38
  schemas: {
@@ -2,3 +2,4 @@ export declare function capitalizeFirstLetter(input: string): string;
2
2
  export declare function normalizeHexAddress(input: string): string | null;
3
3
  export declare function numberToAddressHex(num: number): string;
4
4
  export declare function normalizePackageId(input: string): string;
5
+ export declare function convertHttpToWebSocket(url: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xobelisk/sui-client",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Tookit for interacting with move eps framework",
5
5
  "keywords": [
6
6
  "sui",
@@ -37,32 +37,34 @@
37
37
  "src"
38
38
  ],
39
39
  "dependencies": {
40
+ "@graphql-typed-document-node/core": "^3.2.0",
40
41
  "@mysten/bcs": "^1.2.1",
41
42
  "@mysten/sui": "^1.19.0",
43
+ "@mysten/zklogin": "^0.7.8",
42
44
  "@noble/curves": "^1.4.2",
43
45
  "@noble/hashes": "^1.4.0",
44
46
  "@scure/bip39": "^1.3.0",
45
- "@mysten/zklogin": "^0.7.8",
46
- "@graphql-typed-document-node/core": "^3.2.0",
47
47
  "@suchipi/femver": "^1.0.0",
48
48
  "assert": "^2.1.0",
49
49
  "bech32": "^2.0.0",
50
- "superstruct": "^1.0.3",
51
- "ts-retry-promise": "^0.7.1",
52
- "tweetnacl": "^1.0.3",
53
50
  "colorts": "^0.1.63",
51
+ "gql.tada": "^1.7.0",
52
+ "graphql": "^16.8.1",
54
53
  "husky": "^8.0.3",
55
54
  "keccak256": "^1.0.6",
56
55
  "process": "^0.11.10",
57
- "gql.tada": "^1.7.0",
58
- "graphql": "^16.8.1",
56
+ "superstruct": "^1.0.3",
59
57
  "tmp": "^0.2.1",
60
- "valibot": "0.36.0"
58
+ "ts-retry-promise": "^0.7.1",
59
+ "tweetnacl": "^1.0.3",
60
+ "valibot": "0.36.0",
61
+ "ws": "^8.18.0"
61
62
  },
62
63
  "devDependencies": {
63
64
  "@commitlint/cli": "^18.0.0",
64
65
  "@commitlint/config-conventional": "^18.0.0",
65
66
  "@commitlint/prompt-cli": "^18.0.0",
67
+ "@types/jest": "^29.5.14",
66
68
  "@types/node": "^20.8.7",
67
69
  "@types/tmp": "^0.2.5",
68
70
  "@typescript-eslint/eslint-plugin": "^6.8.0",
@@ -71,13 +73,16 @@
71
73
  "eslint": "^8.52.0",
72
74
  "eslint-config-prettier": "^8.8.0",
73
75
  "eslint-plugin-prettier": "^5.0.1",
76
+ "graphql-ws": "^6.0.2",
77
+ "jest": "^29.7.0",
74
78
  "lint-staged": "^15.0.2",
75
79
  "prettier": "^2.8.8",
76
80
  "ts-node": "^10.9.1",
77
81
  "tsconfig-paths": "^4.2.0",
78
82
  "tsup": "^7.1.0",
79
83
  "typedoc": "^0.25.2",
80
- "typescript": "^5.2.2"
84
+ "typescript": "^5.2.2",
85
+ "@types/ws": "^8.5.14"
81
86
  },
82
87
  "lint-staged": {
83
88
  "**/*.ts": [
package/src/dubhe.ts CHANGED
@@ -41,12 +41,20 @@ import {
41
41
  SuiVecTxArg,
42
42
  } from './types';
43
43
  import {
44
+ convertHttpToWebSocket,
44
45
  normalizeHexAddress,
45
46
  normalizePackageId,
46
47
  numberToAddressHex,
47
48
  } from './utils';
48
49
  import { bcs, fromHEX, toHEX } from '@mysten/bcs';
49
50
  import { ContractDataParsingError } from './errors';
51
+ import {
52
+ ConnectionResponse,
53
+ OrderBy,
54
+ Schema,
55
+ SuiIndexerClient,
56
+ } from './libs/suiIndexerClient';
57
+ import { Http } from './libs/http';
50
58
 
51
59
  export function isUndefined(value?: unknown): value is undefined {
52
60
  return value === undefined;
@@ -121,8 +129,10 @@ function createTx(
121
129
  * @description This class is used to aggregate the tools that used to interact with SUI network.
122
130
  */
123
131
  export class Dubhe {
132
+ public http: Http;
124
133
  public accountManager: SuiAccountManager;
125
134
  public suiInteractor: SuiInteractor;
135
+ public suiIndexerClient: SuiIndexerClient;
126
136
  public contractFactory: SuiContractFactory;
127
137
  public packageId: string | undefined;
128
138
  public metadata: SuiMoveNormalizedModules | undefined;
@@ -237,13 +247,27 @@ export class Dubhe {
237
247
  fullnodeUrls,
238
248
  packageId,
239
249
  metadata,
250
+ customFetch,
251
+ defaultOptions,
252
+ indexerUrl,
253
+ indexerWsUrl,
240
254
  }: DubheParams = {}) {
255
+ networkType = networkType ?? 'mainnet';
256
+
257
+ const defaultParams = getDefaultURL(networkType);
258
+
241
259
  // Init the account manager
242
260
  this.accountManager = new SuiAccountManager({ mnemonics, secretKey });
243
261
  // Init the rpc provider
244
- fullnodeUrls = fullnodeUrls || [getFullnodeUrl(networkType ?? 'mainnet')];
262
+ fullnodeUrls = fullnodeUrls || [defaultParams.fullNode];
245
263
  this.suiInteractor = new SuiInteractor(fullnodeUrls, networkType);
246
264
 
265
+ indexerUrl = indexerUrl || defaultParams.indexerUrl;
266
+ indexerWsUrl = indexerWsUrl || convertHttpToWebSocket(indexerUrl);
267
+ this.http = new Http(indexerUrl, indexerWsUrl, customFetch, defaultOptions);
268
+
269
+ this.suiIndexerClient = new SuiIndexerClient(this.http);
270
+
247
271
  this.packageId = packageId ? normalizePackageId(packageId) : undefined;
248
272
  if (metadata !== undefined) {
249
273
  this.metadata = metadata as SuiMoveNormalizedModules;
@@ -1085,6 +1109,47 @@ export class Dubhe {
1085
1109
  });
1086
1110
  }
1087
1111
 
1112
+ async getStorage({
1113
+ name,
1114
+ key1,
1115
+ key2,
1116
+ first,
1117
+ after,
1118
+ last,
1119
+ before,
1120
+ orderBy,
1121
+ distinct,
1122
+ }: {
1123
+ name: string;
1124
+ key1?: string;
1125
+ key2?: string;
1126
+ first?: number;
1127
+ after?: string;
1128
+ last?: number;
1129
+ before?: string;
1130
+ orderBy?: OrderBy;
1131
+ distinct?: boolean;
1132
+ }): Promise<ConnectionResponse<Schema>> {
1133
+ return await this.suiIndexerClient.getStorage({
1134
+ name,
1135
+ key1,
1136
+ key2,
1137
+ first,
1138
+ after,
1139
+ last,
1140
+ before,
1141
+ orderBy,
1142
+ distinct,
1143
+ });
1144
+ }
1145
+
1146
+ async subscribe(
1147
+ names: string[],
1148
+ handleData: (data: any) => void
1149
+ ): Promise<WebSocket> {
1150
+ return this.suiIndexerClient.subscribe(names, handleData);
1151
+ }
1152
+
1088
1153
  #processKeyParameter(tx: Transaction, keyType: string, value: any) {
1089
1154
  // Handle basic types
1090
1155
  switch (keyType.toLowerCase()) {
@@ -1228,6 +1293,10 @@ export class Dubhe {
1228
1293
  return this.suiInteractor.currentClient;
1229
1294
  }
1230
1295
 
1296
+ indexerClient() {
1297
+ return this.suiIndexerClient;
1298
+ }
1299
+
1231
1300
  async getObject(objectId: string) {
1232
1301
  return this.suiInteractor.getObject(objectId);
1233
1302
  }
@@ -0,0 +1,122 @@
1
+ export class BaseError extends Error {
2
+ constructor(message: string, public code: number, public type: string) {
3
+ super(message);
4
+ this.name = this.constructor.name;
5
+ }
6
+ }
7
+
8
+ export class HttpError extends BaseError {
9
+ constructor(message: string, code: number) {
10
+ super(message, code, 'ERROR_HTTP');
11
+ }
12
+ }
13
+
14
+ export class GraphQLError extends BaseError {
15
+ constructor(message: string) {
16
+ super(message, 400, 'ERROR_GRAPHQL');
17
+ }
18
+ }
19
+
20
+ export class ValidationError extends BaseError {
21
+ constructor(message: string) {
22
+ super(message, 400, 'ERROR_VALIDATION');
23
+ }
24
+ }
25
+
26
+ export class NotFoundError extends BaseError {
27
+ constructor(message: string) {
28
+ super(message, 404, 'ERROR_NOT_FOUND');
29
+ }
30
+ }
31
+
32
+ export class ParseError extends BaseError {
33
+ constructor(message: string) {
34
+ super(message, 500, 'ERROR_PARSE');
35
+ }
36
+ }
37
+
38
+ export type ErrorType =
39
+ | BaseError
40
+ | Error
41
+ | HttpError
42
+ | NotFoundError
43
+ | ValidationError
44
+ | ParseError
45
+ | GraphQLError;
46
+
47
+ export function handleError(error: ErrorType) {
48
+ if (!(error instanceof Error)) {
49
+ return {
50
+ code: 500,
51
+ error: {
52
+ message: 'Unknown error occurred',
53
+ type: `ERROR_UNKNOWN`,
54
+ },
55
+ };
56
+ }
57
+
58
+ switch (true) {
59
+ case error instanceof HttpError:
60
+ return {
61
+ code: (error as HttpError).code,
62
+ error: {
63
+ message: error.message,
64
+ type: 'ERROR_HTTP',
65
+ },
66
+ };
67
+
68
+ case error instanceof GraphQLError:
69
+ return {
70
+ code: 400,
71
+ error: {
72
+ message: error.message,
73
+ type: 'ERROR_GRAPHQL',
74
+ },
75
+ };
76
+
77
+ case error instanceof ValidationError:
78
+ return {
79
+ code: 400,
80
+ error: {
81
+ message: error.message,
82
+ type: 'ERROR_VALIDATION',
83
+ },
84
+ };
85
+
86
+ case error instanceof NotFoundError:
87
+ return {
88
+ code: 404,
89
+ error: {
90
+ message: error.message,
91
+ type: 'ERROR_NOT_FOUND',
92
+ },
93
+ };
94
+
95
+ case error instanceof ParseError:
96
+ return {
97
+ code: 500,
98
+ error: {
99
+ message: error.message,
100
+ type: 'ERROR_PARSE',
101
+ },
102
+ };
103
+
104
+ case error instanceof BaseError:
105
+ return {
106
+ code: (error as BaseError).code,
107
+ error: {
108
+ message: error.message,
109
+ type: (error as BaseError).type,
110
+ },
111
+ };
112
+
113
+ default:
114
+ return {
115
+ code: 500,
116
+ error: {
117
+ message: error.message || 'Internal server error',
118
+ type: 'ERROR_UNKNOWN',
119
+ },
120
+ };
121
+ }
122
+ }
@@ -0,0 +1,153 @@
1
+ import { BaseError, HttpError, GraphQLError, ParseError } from './errors';
2
+ import { createWebSocketClient, WebSocketInstance } from './ws-adapter';
3
+
4
+ export type FetchOptions = RequestInit & {
5
+ next?: {
6
+ revalidate?: boolean | number;
7
+ };
8
+ };
9
+
10
+ export class Http {
11
+ private apiEndpoint: string;
12
+ private graphqlEndpoint: string;
13
+ private wsEndpoint: string;
14
+ private defaultOptions?: FetchOptions;
15
+
16
+ constructor(
17
+ apiEndpoint: string,
18
+ wsEndpoint: string,
19
+ private customFetch?: typeof fetch,
20
+ defaultOptions?: FetchOptions
21
+ ) {
22
+ this.apiEndpoint = apiEndpoint;
23
+ this.graphqlEndpoint = apiEndpoint + '/graphql';
24
+ this.wsEndpoint = wsEndpoint;
25
+ this.defaultOptions = defaultOptions;
26
+ }
27
+
28
+ private getFetch() {
29
+ return this.customFetch || fetch;
30
+ }
31
+
32
+ async fetch(url: string): Promise<Response> {
33
+ try {
34
+ const fetchFn = this.getFetch();
35
+ const response = await fetchFn(url, {
36
+ ...this.defaultOptions,
37
+ });
38
+
39
+ if (!response.ok) {
40
+ throw new HttpError(
41
+ `HTTP error! status: ${response.status}`,
42
+ response.status
43
+ );
44
+ }
45
+
46
+ return response;
47
+ } catch (error) {
48
+ if (error instanceof HttpError) {
49
+ throw error;
50
+ }
51
+ throw new HttpError(`Failed to fetch: ${(error as Error).message}`, 500);
52
+ }
53
+ }
54
+
55
+ async fetchGraphql<T>({
56
+ query,
57
+ variables,
58
+ }: {
59
+ query: string;
60
+ variables?: any;
61
+ }): Promise<T> {
62
+ try {
63
+ const isFirstPage = variables?.after === 'first';
64
+ const fetchFn = this.getFetch();
65
+ const response = await fetchFn(this.graphqlEndpoint, {
66
+ method: 'POST',
67
+ headers: {
68
+ 'Content-Type': 'application/json',
69
+ Accept: 'application/json',
70
+ },
71
+ body: JSON.stringify({
72
+ query,
73
+ variables: {
74
+ ...variables,
75
+ after: isFirstPage ? undefined : variables?.after,
76
+ },
77
+ }),
78
+ ...this.defaultOptions,
79
+ });
80
+
81
+ if (!response.ok) {
82
+ const errorData = await response.json();
83
+
84
+ if (errorData.errors?.[0]?.message?.includes('Syntax Error')) {
85
+ throw new GraphQLError(
86
+ `GraphQL syntax error: ${errorData.errors[0].message}`
87
+ );
88
+ }
89
+
90
+ if (errorData.errors?.length > 0) {
91
+ throw new GraphQLError(
92
+ errorData.errors[0].message || 'Unknown GraphQL error'
93
+ );
94
+ }
95
+
96
+ throw new HttpError(
97
+ `HTTP error: ${JSON.stringify(errorData)}`,
98
+ response.status
99
+ );
100
+ }
101
+ const data = await response.json();
102
+
103
+ if (data.errors) {
104
+ throw new GraphQLError(
105
+ data.errors[0]?.message || 'GraphQL query failed'
106
+ );
107
+ }
108
+
109
+ return data.data;
110
+ } catch (error) {
111
+ if (error instanceof BaseError) {
112
+ throw error;
113
+ }
114
+ if (error instanceof SyntaxError) {
115
+ throw new ParseError('Failed to parse JSON response');
116
+ }
117
+ throw new HttpError(
118
+ `Failed to fetch GraphQL: ${(error as Error).message}`,
119
+ 500
120
+ );
121
+ }
122
+ }
123
+
124
+ async subscribe(
125
+ names: string[],
126
+ handleData: (data: any) => void
127
+ ): Promise<WebSocketInstance> {
128
+ const ws = createWebSocketClient(this.wsEndpoint);
129
+
130
+ ws.onopen = () => {
131
+ console.log('Connected to the WebSocket server');
132
+ const subscribeMessage = JSON.stringify({
133
+ type: 'subscribe',
134
+ names: names,
135
+ });
136
+ ws.send(subscribeMessage);
137
+ };
138
+
139
+ ws.onmessage = (event) => {
140
+ handleData(JSON.parse(event.data.toString()));
141
+ };
142
+
143
+ ws.onclose = () => {
144
+ console.log('Disconnected from the WebSocket server');
145
+ };
146
+
147
+ ws.onerror = (error) => {
148
+ console.error(`WebSocket error:`, error);
149
+ };
150
+
151
+ return ws;
152
+ }
153
+ }
@@ -0,0 +1,4 @@
1
+ export { Http } from './http';
2
+ export type { FetchOptions } from './http';
3
+ export * from './errors';
4
+ export * from './types';
@@ -0,0 +1 @@
1
+ export const ERROR = {};