@exodus/sui-api 0.1.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 (44) hide show
  1. package/README.md +20 -0
  2. package/package.json +53 -0
  3. package/src/api.js +198 -0
  4. package/src/balances.js +22 -0
  5. package/src/basic-account-state.js +11 -0
  6. package/src/create-unsigned-tx.js +60 -0
  7. package/src/get-fee-async.js +62 -0
  8. package/src/get-fee.js +10 -0
  9. package/src/index.js +9 -0
  10. package/src/sui-no-history-monitor.js +277 -0
  11. package/src/transform-tx.js +100 -0
  12. package/src/tx-send.js +249 -0
  13. package/src/vendors/client/client.js +891 -0
  14. package/src/vendors/client/errors.js +38 -0
  15. package/src/vendors/client/http-transport.js +108 -0
  16. package/src/vendors/client/index.js +1 -0
  17. package/src/vendors/client/rpc-websocket-client.js +178 -0
  18. package/src/vendors/experimental/cache.js +89 -0
  19. package/src/vendors/experimental/client.js +27 -0
  20. package/src/vendors/experimental/core.js +105 -0
  21. package/src/vendors/experimental/errors.js +32 -0
  22. package/src/vendors/experimental/index.js +5 -0
  23. package/src/vendors/experimental/mvr.js +443 -0
  24. package/src/vendors/experimental/transports/json-rpc-resolver.js +359 -0
  25. package/src/vendors/experimental/transports/jsonRPC.js +504 -0
  26. package/src/vendors/experimental/transports/utils.js +90 -0
  27. package/src/vendors/transactions/Arguments.js +22 -0
  28. package/src/vendors/transactions/Commands.js +108 -0
  29. package/src/vendors/transactions/Inputs.js +59 -0
  30. package/src/vendors/transactions/ObjectCache.js +272 -0
  31. package/src/vendors/transactions/Transaction.js +843 -0
  32. package/src/vendors/transactions/TransactionData.js +293 -0
  33. package/src/vendors/transactions/data/internal.js +218 -0
  34. package/src/vendors/transactions/data/v1.js +538 -0
  35. package/src/vendors/transactions/data/v2.js +119 -0
  36. package/src/vendors/transactions/hash.js +12 -0
  37. package/src/vendors/transactions/index.js +1 -0
  38. package/src/vendors/transactions/object.js +22 -0
  39. package/src/vendors/transactions/plugins/NamedPackagesPlugin.js +49 -0
  40. package/src/vendors/transactions/pure.js +40 -0
  41. package/src/vendors/transactions/resolve.js +95 -0
  42. package/src/vendors/transactions/serializer.js +203 -0
  43. package/src/vendors/transactions/utils.js +67 -0
  44. package/src/vendors/valibot.js +7920 -0
@@ -0,0 +1,38 @@
1
+ const CODE_TO_ERROR_TYPE = {
2
+ '-32700': 'ParseError',
3
+ '-32701': 'OversizedRequest',
4
+ '-32702': 'OversizedResponse',
5
+ '-32600': 'InvalidRequest',
6
+ '-32601': 'MethodNotFound',
7
+ '-32602': 'InvalidParams',
8
+ '-32603': 'InternalError',
9
+ '-32604': 'ServerBusy',
10
+ '-32000': 'CallExecutionFailed',
11
+ '-32001': 'UnknownError',
12
+ '-32003': 'SubscriptionClosed',
13
+ '-32004': 'SubscriptionClosedWithError',
14
+ '-32005': 'BatchesNotSupported',
15
+ '-32006': 'TooManySubscriptions',
16
+ '-32050': 'TransientError',
17
+ '-32002': 'TransactionExecutionClientError',
18
+ }
19
+
20
+ class SuiHTTPTransportError extends Error {}
21
+
22
+ class JsonRpcError extends SuiHTTPTransportError {
23
+ constructor(message, code) {
24
+ super(message)
25
+ this.code = code
26
+ this.type = CODE_TO_ERROR_TYPE[code] ?? 'ServerError'
27
+ }
28
+ }
29
+
30
+ class SuiHTTPStatusError extends SuiHTTPTransportError {
31
+ constructor(message, status, statusText) {
32
+ super(message)
33
+ this.status = status
34
+ this.statusText = statusText
35
+ }
36
+ }
37
+ export { JsonRpcError, SuiHTTPStatusError, SuiHTTPTransportError }
38
+ // # sourceMappingURL=errors.js.map
@@ -0,0 +1,108 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { JsonRpcError, SuiHTTPStatusError } from './errors.js'
5
+ import { WebsocketClient } from './rpc-websocket-client.js'
6
+
7
+ export const PACKAGE_VERSION = '1.36.1'
8
+ export const TARGETED_RPC_VERSION = '1.55.0'
9
+
10
+ /**
11
+ * An object defining headers to be passed to the RPC server
12
+ */
13
+
14
+ export class SuiHTTPTransport {
15
+ #requestId = 0
16
+ #options
17
+ #websocketClient
18
+
19
+ constructor(options) {
20
+ this.#options = options
21
+ }
22
+
23
+ fetch(input, init) {
24
+ const fetchFn = this.#options.fetch ?? fetch
25
+
26
+ if (!fetchFn) {
27
+ throw new Error(
28
+ 'The current environment does not support fetch, you can provide a fetch implementation in the options for SuiHTTPTransport.'
29
+ )
30
+ }
31
+
32
+ return fetchFn(input, init)
33
+ }
34
+
35
+ #getWebsocketClient() {
36
+ if (!this.#websocketClient) {
37
+ // eslint-disable-next-line no-undef
38
+ const WebSocketConstructor = this.#options.WebSocketConstructor ?? WebSocket
39
+ if (!WebSocketConstructor) {
40
+ throw new Error(
41
+ 'The current environment does not support WebSocket, you can provide a WebSocketConstructor in the options for SuiHTTPTransport.'
42
+ )
43
+ }
44
+
45
+ this.#websocketClient = new WebsocketClient(
46
+ this.#options.websocket?.url ?? this.#options.url,
47
+ {
48
+ WebSocketConstructor,
49
+ ...this.#options.websocket,
50
+ }
51
+ )
52
+ }
53
+
54
+ return this.#websocketClient
55
+ }
56
+
57
+ async request(input) {
58
+ this.#requestId += 1
59
+
60
+ const res = await this.fetch(this.#options.rpc?.url ?? this.#options.url, {
61
+ method: 'POST',
62
+ signal: input.signal,
63
+ headers: {
64
+ 'Content-Type': 'application/json',
65
+ 'Client-Sdk-Type': 'typescript',
66
+ 'Client-Sdk-Version': PACKAGE_VERSION,
67
+ 'Client-Target-Api-Version': TARGETED_RPC_VERSION,
68
+ 'Client-Request-Method': input.method,
69
+ ...this.#options.rpc?.headers,
70
+ },
71
+ body: JSON.stringify({
72
+ jsonrpc: '2.0',
73
+ id: this.#requestId,
74
+ method: input.method,
75
+ params: input.params,
76
+ }),
77
+ })
78
+
79
+ if (!res.ok) {
80
+ throw new SuiHTTPStatusError(
81
+ `Unexpected status code: ${res.status}`,
82
+ res.status,
83
+ res.statusText
84
+ )
85
+ }
86
+
87
+ const data = await res.json()
88
+
89
+ if ('error' in data && data.error != null) {
90
+ throw new JsonRpcError(data.error.message, data.error.code)
91
+ }
92
+
93
+ return data.result
94
+ }
95
+
96
+ async subscribe(input) {
97
+ const unsubscribe = await this.#getWebsocketClient().subscribe(input)
98
+
99
+ if (input.signal) {
100
+ input.signal.throwIfAborted()
101
+ input.signal.addEventListener('abort', () => {
102
+ unsubscribe()
103
+ })
104
+ }
105
+
106
+ return async () => !!(await unsubscribe())
107
+ }
108
+ }
@@ -0,0 +1 @@
1
+ export { SuiClient } from './client.js'
@@ -0,0 +1,178 @@
1
+ import { JsonRpcError } from './errors.js'
2
+
3
+ function getWebsocketUrl(httpUrl) {
4
+ const url = new URL(httpUrl)
5
+ url.protocol = url.protocol.replace('http', 'ws')
6
+ return url.toString()
7
+ }
8
+
9
+ export const DEFAULT_CLIENT_OPTIONS = {
10
+ // We fudge the typing because we also check for undefined in the constructor:
11
+ // eslint-disable-next-line no-undef
12
+ WebSocketConstructor: typeof WebSocket === 'undefined' ? undefined : WebSocket, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
13
+ callTimeout: 30_000,
14
+ reconnectTimeout: 3000,
15
+ maxReconnects: 5,
16
+ }
17
+
18
+ export class WebsocketClient {
19
+ endpoint
20
+ options
21
+ #requestId = 0
22
+ #disconnects = 0
23
+ #webSocket = null
24
+ #connectionPromise = null
25
+ #subscriptions = new Set()
26
+ #pendingRequests = new Map()
27
+
28
+ constructor(endpoint, options = Object.create(null)) {
29
+ this.endpoint = endpoint
30
+ this.options = { ...DEFAULT_CLIENT_OPTIONS, ...options }
31
+
32
+ if (!this.options.WebSocketConstructor) {
33
+ throw new Error('Missing WebSocket constructor')
34
+ }
35
+
36
+ if (this.endpoint.startsWith('http')) {
37
+ this.endpoint = getWebsocketUrl(this.endpoint)
38
+ }
39
+ }
40
+
41
+ async makeRequest(method, params, signal) {
42
+ const webSocket = await this.#setupWebSocket()
43
+
44
+ return new Promise((resolve, reject) => {
45
+ this.#requestId += 1
46
+ this.#pendingRequests.set(this.#requestId, {
47
+ resolve,
48
+ reject,
49
+ timeout: setTimeout(() => {
50
+ this.#pendingRequests.delete(this.#requestId)
51
+ reject(new Error(`Request timeout: ${method}`))
52
+ }, this.options.callTimeout),
53
+ })
54
+
55
+ signal?.addEventListener('abort', () => {
56
+ this.#pendingRequests.delete(this.#requestId)
57
+ reject(signal.reason)
58
+ })
59
+
60
+ webSocket.send(JSON.stringify({ jsonrpc: '2.0', id: this.#requestId, method, params }))
61
+ }).then(({ error, result }) => {
62
+ if (error) {
63
+ throw new JsonRpcError(error.message, error.code)
64
+ }
65
+
66
+ return result
67
+ })
68
+ }
69
+
70
+ #setupWebSocket() {
71
+ if (this.#connectionPromise) {
72
+ return this.#connectionPromise
73
+ }
74
+
75
+ this.#connectionPromise = new Promise((resolve) => {
76
+ this.#webSocket?.close()
77
+ this.#webSocket = new this.options.WebSocketConstructor(this.endpoint)
78
+
79
+ this.#webSocket.addEventListener('open', () => {
80
+ this.#disconnects = 0
81
+ resolve(this.#webSocket)
82
+ })
83
+
84
+ this.#webSocket.addEventListener('close', () => {
85
+ this.#disconnects++
86
+ if (this.#disconnects <= this.options.maxReconnects) {
87
+ setTimeout(() => {
88
+ this.#reconnect()
89
+ }, this.options.reconnectTimeout)
90
+ }
91
+ })
92
+
93
+ this.#webSocket.addEventListener('message', ({ data }) => {
94
+ let json
95
+ try {
96
+ json = JSON.parse(data)
97
+ } catch (error) {
98
+ console.error(new Error(`Failed to parse RPC message: ${data}`, { cause: error }))
99
+ return
100
+ }
101
+
102
+ if ('id' in json && json.id != null && this.#pendingRequests.has(json.id)) {
103
+ const { resolve, timeout } = this.#pendingRequests.get(json.id)
104
+
105
+ clearTimeout(timeout)
106
+ resolve(json)
107
+ } else if ('params' in json) {
108
+ const { params } = json
109
+ this.#subscriptions.forEach((subscription) => {
110
+ if (
111
+ subscription.subscriptionId === params.subscription &&
112
+ params.subscription === subscription.subscriptionId
113
+ ) {
114
+ subscription.onMessage(params.result)
115
+ }
116
+ })
117
+ }
118
+ })
119
+ })
120
+
121
+ return this.#connectionPromise
122
+ }
123
+
124
+ async #reconnect() {
125
+ this.#webSocket?.close()
126
+ this.#connectionPromise = null
127
+
128
+ return Promise.allSettled(
129
+ [...this.#subscriptions].map((subscription) => subscription.subscribe(this))
130
+ )
131
+ }
132
+
133
+ async subscribe(input) {
134
+ const subscription = new RpcSubscription(input)
135
+ this.#subscriptions.add(subscription)
136
+ await subscription.subscribe(this)
137
+ return () => subscription.unsubscribe(this)
138
+ }
139
+ }
140
+
141
+ class RpcSubscription {
142
+ subscriptionId = null
143
+ input
144
+ subscribed = false
145
+
146
+ constructor(input) {
147
+ this.input = input
148
+ }
149
+
150
+ onMessage(message) {
151
+ if (this.subscribed) {
152
+ this.input.onMessage(message)
153
+ }
154
+ }
155
+
156
+ async unsubscribe(client) {
157
+ const { subscriptionId } = this
158
+ this.subscribed = false
159
+ if (subscriptionId == null) return false
160
+ this.subscriptionId = null
161
+
162
+ return client.makeRequest(this.input.unsubscribe, [subscriptionId])
163
+ }
164
+
165
+ async subscribe(client) {
166
+ this.subscriptionId = null
167
+ this.subscribed = true
168
+ const newSubscriptionId = await client.makeRequest(
169
+ this.input.method,
170
+ this.input.params,
171
+ this.input.signal
172
+ )
173
+
174
+ if (this.subscribed) {
175
+ this.subscriptionId = newSubscriptionId
176
+ }
177
+ }
178
+ }
@@ -0,0 +1,89 @@
1
+ /* eslint-disable no-sequences */
2
+ var __typeError = (msg) => {
3
+ throw new TypeError(msg)
4
+ }
5
+
6
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError('Cannot ' + msg)
7
+ var __privateGet = (obj, member, getter) => (
8
+ __accessCheck(obj, member, 'read from private field'), getter ? getter.call(obj) : member.get(obj)
9
+ )
10
+ var __privateAdd = (obj, member, value) =>
11
+ member.has(obj)
12
+ ? __typeError('Cannot add the same private member more than once')
13
+ : member instanceof WeakSet
14
+ ? member.add(obj)
15
+ : member.set(obj, value)
16
+ var __privateSet = (obj, member, value, setter) => (
17
+ __accessCheck(obj, member, 'write to private field'),
18
+ setter ? setter.call(obj, value) : member.set(obj, value),
19
+ value
20
+ )
21
+ var _prefix, _cache
22
+ const _ClientCache = class _ClientCache {
23
+ constructor({ prefix, cache } = Object.create(null)) {
24
+ __privateAdd(this, _prefix)
25
+ __privateAdd(this, _cache)
26
+ __privateSet(this, _prefix, prefix ?? [])
27
+ __privateSet(this, _cache, cache ?? /* @__PURE__ */ new Map())
28
+ }
29
+
30
+ read(key, load) {
31
+ const cacheKey = [__privateGet(this, _prefix), ...key].join(':')
32
+ if (__privateGet(this, _cache).has(cacheKey)) {
33
+ return __privateGet(this, _cache).get(cacheKey)
34
+ }
35
+
36
+ const result = load()
37
+ __privateGet(this, _cache).set(cacheKey, result)
38
+ if (typeof result === 'object' && result !== null && result.hasOwnProperty('then')) {
39
+ return Promise.resolve(result)
40
+ .then((v) => {
41
+ __privateGet(this, _cache).set(cacheKey, v)
42
+ return v
43
+ })
44
+ .catch((err) => {
45
+ __privateGet(this, _cache).delete(cacheKey)
46
+ throw err
47
+ })
48
+ }
49
+
50
+ return result
51
+ }
52
+
53
+ readSync(key, load) {
54
+ const cacheKey = [__privateGet(this, _prefix), ...key].join(':')
55
+ if (__privateGet(this, _cache).has(cacheKey)) {
56
+ return __privateGet(this, _cache).get(cacheKey)
57
+ }
58
+
59
+ const result = load()
60
+ __privateGet(this, _cache).set(cacheKey, result)
61
+ return result
62
+ }
63
+
64
+ clear(prefix) {
65
+ const prefixKey = [...__privateGet(this, _prefix), ...(prefix ?? [])].join(':')
66
+ if (!prefixKey) {
67
+ __privateGet(this, _cache).clear()
68
+ return
69
+ }
70
+
71
+ for (const key of __privateGet(this, _cache).keys()) {
72
+ if (key.startsWith(prefixKey)) {
73
+ __privateGet(this, _cache).delete(key)
74
+ }
75
+ }
76
+ }
77
+
78
+ scope(prefix) {
79
+ return new _ClientCache({
80
+ prefix: [...__privateGet(this, _prefix), ...(Array.isArray(prefix) ? prefix : [prefix])],
81
+ cache: __privateGet(this, _cache),
82
+ })
83
+ }
84
+ }
85
+ _prefix = new WeakMap()
86
+ _cache = new WeakMap()
87
+ const ClientCache = _ClientCache
88
+ export { ClientCache }
89
+ // # sourceMappingURL=cache.js.map
@@ -0,0 +1,27 @@
1
+ /* eslint-disable camelcase */
2
+ import { ClientCache } from './cache.js'
3
+
4
+ class Experimental_BaseClient {
5
+ constructor({ network, base }) {
6
+ this.network = network
7
+ this.base = base ?? this
8
+ this.cache = base?.cache ?? new ClientCache()
9
+ }
10
+
11
+ $extend(...registrations) {
12
+ return Object.create(
13
+ this,
14
+ Object.fromEntries(
15
+ registrations.map((registration) => {
16
+ if ('experimental_asClientExtension' in registration) {
17
+ const { name, register } = registration.experimental_asClientExtension()
18
+ return [name, { value: register(this) }]
19
+ }
20
+
21
+ return [registration.name, { value: registration.register(this) }]
22
+ })
23
+ )
24
+ )
25
+ }
26
+ }
27
+ export { Experimental_BaseClient }
@@ -0,0 +1,105 @@
1
+ /* eslint-disable camelcase */
2
+
3
+ import { vendorLib } from '@exodus/sui-lib'
4
+
5
+ import { Experimental_BaseClient } from './client.js'
6
+ import { MvrClient } from './mvr.js'
7
+
8
+ const { TypeTagSerializer } = vendorLib.bcs
9
+ const { deriveDynamicFieldID, normalizeStructTag, parseStructTag, SUI_ADDRESS_LENGTH } =
10
+ vendorLib.utils
11
+
12
+ const DEFAULT_MVR_URLS = {
13
+ mainnet: 'https://mainnet.mvr.mystenlabs.com',
14
+ testnet: 'https://testnet.mvr.mystenlabs.com',
15
+ }
16
+
17
+ class Experimental_CoreClient extends Experimental_BaseClient {
18
+ constructor(options) {
19
+ super(options)
20
+ this.core = this
21
+ this.mvr = new MvrClient({
22
+ cache: this.cache.scope('core.mvr'),
23
+ url: options.mvr?.url ?? DEFAULT_MVR_URLS[this.network],
24
+ pageSize: options.mvr?.pageSize,
25
+ overrides: options.mvr?.overrides,
26
+ })
27
+ }
28
+
29
+ async getObject(options) {
30
+ const { objectId } = options
31
+ const {
32
+ objects: [result],
33
+ } = await this.getObjects({ objectIds: [objectId], signal: options.signal })
34
+ if (result instanceof Error) {
35
+ throw result
36
+ }
37
+
38
+ return { object: result }
39
+ }
40
+
41
+ async getDynamicField(options) {
42
+ const fieldId = deriveDynamicFieldID(
43
+ options.parentId,
44
+ TypeTagSerializer.parseFromStr(options.name.type),
45
+ options.name.bcs
46
+ )
47
+ const {
48
+ objects: [fieldObject],
49
+ } = await this.getObjects({
50
+ objectIds: [fieldId],
51
+ signal: options.signal,
52
+ })
53
+ if (fieldObject instanceof Error) {
54
+ throw fieldObject
55
+ }
56
+
57
+ const fieldType = parseStructTag(fieldObject.type)
58
+ const content = await fieldObject.content
59
+ return {
60
+ dynamicField: {
61
+ id: fieldObject.id,
62
+ digest: fieldObject.digest,
63
+ version: fieldObject.version,
64
+ type: fieldObject.type,
65
+ name: {
66
+ type:
67
+ typeof fieldType.typeParams[0] === 'string'
68
+ ? fieldType.typeParams[0]
69
+ : normalizeStructTag(fieldType.typeParams[0]),
70
+ bcs: options.name.bcs,
71
+ },
72
+ value: {
73
+ type:
74
+ typeof fieldType.typeParams[1] === 'string'
75
+ ? fieldType.typeParams[1]
76
+ : normalizeStructTag(fieldType.typeParams[1]),
77
+ bcs: content.slice(SUI_ADDRESS_LENGTH + options.name.bcs.length),
78
+ },
79
+ },
80
+ }
81
+ }
82
+
83
+ async waitForTransaction({ signal, timeout = 60 * 1e3, ...input }) {
84
+ const abortSignal = signal
85
+ ? AbortSignal.any([AbortSignal.timeout(timeout), signal])
86
+ : AbortSignal.timeout(timeout)
87
+ // eslint-disable-next-line promise/param-names
88
+ const abortPromise = new Promise((_, reject) => {
89
+ abortSignal.addEventListener('abort', () => reject(abortSignal.reason))
90
+ })
91
+ abortPromise.catch(() => {})
92
+ while (true) {
93
+ abortSignal.throwIfAborted()
94
+ try {
95
+ return await this.getTransaction({
96
+ ...input,
97
+ signal: abortSignal,
98
+ })
99
+ } catch {
100
+ await Promise.race([new Promise((resolve) => setTimeout(resolve, 2e3)), abortPromise])
101
+ }
102
+ }
103
+ }
104
+ }
105
+ export { Experimental_CoreClient }
@@ -0,0 +1,32 @@
1
+ /* eslint-disable sonarjs/no-nested-template-literals */
2
+ class SuiClientError extends Error {}
3
+
4
+ class ObjectError extends SuiClientError {
5
+ constructor(code, message) {
6
+ super(message)
7
+ this.code = code
8
+ }
9
+
10
+ static fromResponse(response, objectId) {
11
+ switch (response.code) {
12
+ case 'notExists':
13
+ return new ObjectError(response.code, `Object ${response.object_id} does not exist`)
14
+ case 'dynamicFieldNotFound':
15
+ return new ObjectError(
16
+ response.code,
17
+ `Dynamic field not found for object ${response.parent_object_id}`
18
+ )
19
+ case 'deleted':
20
+ return new ObjectError(response.code, `Object ${response.object_id} has been deleted`)
21
+ case 'displayError':
22
+ return new ObjectError(response.code, `Display error: ${response.error}`)
23
+ default:
24
+ return new ObjectError(
25
+ response.code,
26
+ `Unknown error while loading object${objectId ? ` ${objectId}` : ''}`
27
+ )
28
+ }
29
+ }
30
+ }
31
+ export { ObjectError, SuiClientError }
32
+ // # sourceMappingURL=errors.js.map
@@ -0,0 +1,5 @@
1
+ /* eslint-disable camelcase */
2
+ export { ClientCache } from './cache.js'
3
+ export { Experimental_CoreClient } from './core.js'
4
+ export { Experimental_BaseClient } from './client.js'
5
+ // # sourceMappingURL=index.js.map