@0xobelisk/sui-client 1.0.6 → 1.0.7

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 { WebSocket } from 'ws';
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<WebSocket>;
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,92 @@
1
+ import { Http } from '../http';
2
+ import { WebSocket } from 'ws';
3
+ export interface OrderDirection {
4
+ ASC: 'ASC';
5
+ DESC: 'DESC';
6
+ }
7
+ export interface OrderBy {
8
+ field: string;
9
+ direction: OrderDirection['ASC'] | OrderDirection['DESC'];
10
+ }
11
+ export interface PageInfo {
12
+ hasNextPage: boolean;
13
+ hasPreviousPage: boolean;
14
+ startCursor?: string;
15
+ endCursor?: string;
16
+ }
17
+ export interface Transaction {
18
+ id: number;
19
+ checkpoint: number;
20
+ digest: 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
+ }
32
+ export interface Event {
33
+ id: number;
34
+ checkpoint: string;
35
+ digest: string;
36
+ name: string;
37
+ value: string;
38
+ }
39
+ export interface ConnectionResponse<T> {
40
+ edges: Array<{
41
+ cursor: string;
42
+ node: T;
43
+ }>;
44
+ pageInfo: PageInfo;
45
+ }
46
+ export declare class SuiIndexerClient {
47
+ private http;
48
+ constructor(http: Http);
49
+ private fetchGraphql;
50
+ getTransactions(params?: {
51
+ first?: number;
52
+ after?: string;
53
+ last?: number;
54
+ before?: string;
55
+ checkpoint?: number;
56
+ orderBy?: OrderBy;
57
+ distinct?: boolean;
58
+ }): Promise<ConnectionResponse<Transaction>>;
59
+ getSchemas(params?: {
60
+ first?: number;
61
+ after?: string;
62
+ last?: number;
63
+ before?: string;
64
+ name?: string;
65
+ key1?: string;
66
+ key2?: string;
67
+ orderBy?: OrderBy;
68
+ distinct?: boolean;
69
+ }): Promise<ConnectionResponse<Schema>>;
70
+ getEvents(params?: {
71
+ first?: number;
72
+ after?: string;
73
+ last?: number;
74
+ before?: string;
75
+ name?: string;
76
+ checkpoint?: string;
77
+ orderBy?: OrderBy;
78
+ distinct?: boolean;
79
+ }): Promise<ConnectionResponse<Event>>;
80
+ getStorage({ name, key1, key2, first, after, last, before, orderBy, distinct, }: {
81
+ name: string;
82
+ key1?: string;
83
+ key2?: string;
84
+ first?: number;
85
+ after?: string;
86
+ last?: number;
87
+ before?: string;
88
+ orderBy?: OrderBy;
89
+ distinct?: boolean;
90
+ }): Promise<ConnectionResponse<Schema>>;
91
+ subscribe(names: string[], handleData: (data: any) => void): Promise<WebSocket>;
92
+ }
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.7",
4
4
  "description": "Tookit for interacting with move eps framework",
5
5
  "keywords": [
6
6
  "sui",
@@ -37,47 +37,52 @@
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",
58
+ "ts-retry-promise": "^0.7.1",
59
+ "tweetnacl": "^1.0.3",
60
60
  "valibot": "0.36.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@commitlint/cli": "^18.0.0",
64
64
  "@commitlint/config-conventional": "^18.0.0",
65
65
  "@commitlint/prompt-cli": "^18.0.0",
66
+ "@types/jest": "^29.5.14",
66
67
  "@types/node": "^20.8.7",
67
68
  "@types/tmp": "^0.2.5",
69
+ "@types/ws": "^8.5.14",
68
70
  "@typescript-eslint/eslint-plugin": "^6.8.0",
69
71
  "@typescript-eslint/parser": "^6.8.0",
70
72
  "dotenv": "^16.3.1",
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
+ "ws": "^8.18.0"
81
86
  },
82
87
  "lint-staged": {
83
88
  "**/*.ts": [
package/src/dubhe.ts CHANGED
@@ -41,12 +41,21 @@ 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';
58
+ import { WebSocket } from 'ws';
50
59
 
51
60
  export function isUndefined(value?: unknown): value is undefined {
52
61
  return value === undefined;
@@ -121,8 +130,10 @@ function createTx(
121
130
  * @description This class is used to aggregate the tools that used to interact with SUI network.
122
131
  */
123
132
  export class Dubhe {
133
+ public http: Http;
124
134
  public accountManager: SuiAccountManager;
125
135
  public suiInteractor: SuiInteractor;
136
+ public suiIndexerClient: SuiIndexerClient;
126
137
  public contractFactory: SuiContractFactory;
127
138
  public packageId: string | undefined;
128
139
  public metadata: SuiMoveNormalizedModules | undefined;
@@ -237,13 +248,27 @@ export class Dubhe {
237
248
  fullnodeUrls,
238
249
  packageId,
239
250
  metadata,
251
+ customFetch,
252
+ defaultOptions,
253
+ indexerUrl,
254
+ indexerWsUrl,
240
255
  }: DubheParams = {}) {
256
+ networkType = networkType ?? 'mainnet';
257
+
258
+ const defaultParams = getDefaultURL(networkType);
259
+
241
260
  // Init the account manager
242
261
  this.accountManager = new SuiAccountManager({ mnemonics, secretKey });
243
262
  // Init the rpc provider
244
- fullnodeUrls = fullnodeUrls || [getFullnodeUrl(networkType ?? 'mainnet')];
263
+ fullnodeUrls = fullnodeUrls || [defaultParams.fullNode];
245
264
  this.suiInteractor = new SuiInteractor(fullnodeUrls, networkType);
246
265
 
266
+ indexerUrl = indexerUrl || defaultParams.indexerUrl;
267
+ indexerWsUrl = indexerWsUrl || convertHttpToWebSocket(indexerUrl);
268
+ this.http = new Http(indexerUrl, indexerWsUrl, customFetch, defaultOptions);
269
+
270
+ this.suiIndexerClient = new SuiIndexerClient(this.http);
271
+
247
272
  this.packageId = packageId ? normalizePackageId(packageId) : undefined;
248
273
  if (metadata !== undefined) {
249
274
  this.metadata = metadata as SuiMoveNormalizedModules;
@@ -1085,6 +1110,47 @@ export class Dubhe {
1085
1110
  });
1086
1111
  }
1087
1112
 
1113
+ async getStorage({
1114
+ name,
1115
+ key1,
1116
+ key2,
1117
+ first,
1118
+ after,
1119
+ last,
1120
+ before,
1121
+ orderBy,
1122
+ distinct,
1123
+ }: {
1124
+ name: string;
1125
+ key1?: string;
1126
+ key2?: string;
1127
+ first?: number;
1128
+ after?: string;
1129
+ last?: number;
1130
+ before?: string;
1131
+ orderBy?: OrderBy;
1132
+ distinct?: boolean;
1133
+ }): Promise<ConnectionResponse<Schema>> {
1134
+ return await this.suiIndexerClient.getStorage({
1135
+ name,
1136
+ key1,
1137
+ key2,
1138
+ first,
1139
+ after,
1140
+ last,
1141
+ before,
1142
+ orderBy,
1143
+ distinct,
1144
+ });
1145
+ }
1146
+
1147
+ async subscribe(
1148
+ names: string[],
1149
+ handleData: (data: any) => void
1150
+ ): Promise<WebSocket> {
1151
+ return this.suiIndexerClient.subscribe(names, handleData);
1152
+ }
1153
+
1088
1154
  #processKeyParameter(tx: Transaction, keyType: string, value: any) {
1089
1155
  // Handle basic types
1090
1156
  switch (keyType.toLowerCase()) {
@@ -1228,6 +1294,10 @@ export class Dubhe {
1228
1294
  return this.suiInteractor.currentClient;
1229
1295
  }
1230
1296
 
1297
+ indexerClient() {
1298
+ return this.suiIndexerClient;
1299
+ }
1300
+
1231
1301
  async getObject(objectId: string) {
1232
1302
  return this.suiInteractor.getObject(objectId);
1233
1303
  }
@@ -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 { WebSocket } from 'ws';
2
+ import { BaseError, HttpError, GraphQLError, ParseError } from './errors';
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
+ console.log(query);
66
+ const response = await fetchFn(this.graphqlEndpoint, {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ Accept: 'application/json',
71
+ },
72
+ body: JSON.stringify({
73
+ query,
74
+ variables: {
75
+ ...variables,
76
+ after: isFirstPage ? undefined : variables?.after,
77
+ },
78
+ }),
79
+ ...this.defaultOptions,
80
+ });
81
+
82
+ if (!response.ok) {
83
+ const errorData = await response.json();
84
+
85
+ if (errorData.errors?.[0]?.message?.includes('Syntax Error')) {
86
+ throw new GraphQLError(
87
+ `GraphQL syntax error: ${errorData.errors[0].message}`
88
+ );
89
+ }
90
+
91
+ if (errorData.errors?.length > 0) {
92
+ throw new GraphQLError(
93
+ errorData.errors[0].message || 'Unknown GraphQL error'
94
+ );
95
+ }
96
+
97
+ throw new HttpError(
98
+ `HTTP error: ${JSON.stringify(errorData)}`,
99
+ response.status
100
+ );
101
+ }
102
+ const data = await response.json();
103
+
104
+ if (data.errors) {
105
+ throw new GraphQLError(
106
+ data.errors[0]?.message || 'GraphQL query failed'
107
+ );
108
+ }
109
+
110
+ return data.data;
111
+ } catch (error) {
112
+ console.log(error);
113
+ if (error instanceof BaseError) {
114
+ throw error;
115
+ }
116
+ if (error instanceof SyntaxError) {
117
+ throw new ParseError('Failed to parse JSON response');
118
+ }
119
+ throw new HttpError(
120
+ `Failed to fetch GraphQL: ${(error as Error).message}`,
121
+ 500
122
+ );
123
+ }
124
+ }
125
+
126
+ async subscribe(names: string[], handleData: (data: any) => void): Promise<WebSocket> {
127
+ const ws = new WebSocket(this.wsEndpoint);
128
+
129
+ ws.on('open', () => {
130
+ console.log('Connected to the WebSocket server');
131
+ // Subscribe to specific event names
132
+ const subscribeMessage = JSON.stringify({
133
+ type: 'subscribe',
134
+ names: names,
135
+ });
136
+ ws.send(subscribeMessage);
137
+ });
138
+
139
+ ws.on('message', (data) => {
140
+ handleData(JSON.parse(data.toString()));
141
+ });
142
+
143
+ ws.on('close', () => {
144
+ console.log('Disconnected from the WebSocket server');
145
+ });
146
+
147
+ ws.on('error', (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 = {};