@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,504 @@
1
+ /* eslint-disable camelcase */
2
+
3
+ import { vendorLib } from '@exodus/sui-lib'
4
+
5
+ import { Transaction } from '../../transactions/Transaction.js'
6
+ import { TransactionDataBuilder } from '../../transactions/TransactionData.js'
7
+ import { Experimental_CoreClient } from '../core.js'
8
+ import { ObjectError } from '../errors.js'
9
+ import { suiClientResolveTransactionPlugin } from './json-rpc-resolver.js'
10
+ import { parseTransactionBcs, parseTransactionEffectsBcs } from './utils.js'
11
+
12
+ const { bcs } = vendorLib
13
+ const { chunk } = vendorLib.utils
14
+ const { fromBase64 } = vendorLib.bcs
15
+
16
+ export class JSONRpcTransport extends Experimental_CoreClient {
17
+ #jsonRpcClient
18
+
19
+ constructor({ jsonRpcClient, mvr }) {
20
+ super({ network: jsonRpcClient.network, base: jsonRpcClient, mvr })
21
+ this.#jsonRpcClient = jsonRpcClient
22
+ }
23
+
24
+ async getObjects(options) {
25
+ const batches = chunk(options.objectIds, 50)
26
+ const results = []
27
+
28
+ for (const batch of batches) {
29
+ const objects = await this.#jsonRpcClient.multiGetObjects({
30
+ ids: batch,
31
+ options: {
32
+ showOwner: true,
33
+ showType: true,
34
+ showBcs: true,
35
+ },
36
+ signal: options.signal,
37
+ })
38
+
39
+ for (const [idx, object] of objects.entries()) {
40
+ if (object.error) {
41
+ results.push(ObjectError.fromResponse(object.error, batch[idx]))
42
+ } else {
43
+ results.push(parseObject(object.data))
44
+ }
45
+ }
46
+ }
47
+
48
+ return {
49
+ objects: results,
50
+ }
51
+ }
52
+
53
+ async getOwnedObjects(options) {
54
+ const objects = await this.#jsonRpcClient.getOwnedObjects({
55
+ owner: options.address,
56
+ limit: options.limit,
57
+ cursor: options.cursor,
58
+ options: {
59
+ showOwner: true,
60
+ showType: true,
61
+ showBcs: true,
62
+ },
63
+ filter: options.type ? { StructType: options.type } : null,
64
+ signal: options.signal,
65
+ })
66
+
67
+ return {
68
+ objects: objects.data.map((result) => {
69
+ if (result.error) {
70
+ throw ObjectError.fromResponse(result.error, options.address)
71
+ }
72
+
73
+ return parseObject(result.data)
74
+ }),
75
+ hasNextPage: objects.hasNextPage,
76
+ cursor: objects.nextCursor ?? null,
77
+ }
78
+ }
79
+
80
+ async getCoins(options) {
81
+ const coins = await this.#jsonRpcClient.getCoins({
82
+ owner: options.address,
83
+ coinType: options.coinType,
84
+ limit: options.limit,
85
+ cursor: options.cursor,
86
+ signal: options.signal,
87
+ })
88
+
89
+ return {
90
+ objects: coins.data.map((coin) => {
91
+ return {
92
+ id: coin.coinObjectId,
93
+ version: coin.version,
94
+ digest: coin.digest,
95
+ balance: coin.balance,
96
+ type: `0x2::coin::Coin<${coin.coinType}>`,
97
+ content: Promise.resolve(
98
+ Coin.serialize({
99
+ id: coin.coinObjectId,
100
+ balance: {
101
+ value: coin.balance,
102
+ },
103
+ }).toBytes()
104
+ ),
105
+ owner: {
106
+ $kind: 'ObjectOwner',
107
+ ObjectOwner: options.address,
108
+ },
109
+ }
110
+ }),
111
+ hasNextPage: coins.hasNextPage,
112
+ cursor: coins.nextCursor ?? null,
113
+ }
114
+ }
115
+
116
+ async getBalance(options) {
117
+ const balance = await this.#jsonRpcClient.getBalance({
118
+ owner: options.address,
119
+ coinType: options.coinType,
120
+ signal: options.signal,
121
+ })
122
+
123
+ return {
124
+ balance: {
125
+ coinType: balance.coinType,
126
+ balance: balance.totalBalance,
127
+ },
128
+ }
129
+ }
130
+
131
+ async getAllBalances(options) {
132
+ const balances = await this.#jsonRpcClient.getAllBalances({
133
+ owner: options.address,
134
+ signal: options.signal,
135
+ })
136
+
137
+ return {
138
+ balances: balances.map((balance) => ({
139
+ coinType: balance.coinType,
140
+ balance: balance.totalBalance,
141
+ })),
142
+ hasNextPage: false,
143
+ cursor: null,
144
+ }
145
+ }
146
+
147
+ async getTransaction(options) {
148
+ const transaction = await this.#jsonRpcClient.getTransactionBlock({
149
+ digest: options.digest,
150
+ options: {
151
+ showRawInput: true,
152
+ showObjectChanges: true,
153
+ showRawEffects: true,
154
+ showEvents: true,
155
+ showEffects: true,
156
+ },
157
+ signal: options.signal,
158
+ })
159
+
160
+ return {
161
+ transaction: parseTransaction(transaction),
162
+ }
163
+ }
164
+
165
+ async executeTransaction(options) {
166
+ const transaction = await this.#jsonRpcClient.executeTransactionBlock({
167
+ transactionBlock: options.transaction,
168
+ signature: options.signatures,
169
+ options: {
170
+ showRawEffects: true,
171
+ showEvents: true,
172
+ showObjectChanges: true,
173
+ showRawInput: true,
174
+ showEffects: true,
175
+ },
176
+ signal: options.signal,
177
+ })
178
+
179
+ return {
180
+ transaction: parseTransaction(transaction),
181
+ }
182
+ }
183
+
184
+ async dryRunTransaction(options) {
185
+ const tx = Transaction.from(options.transaction)
186
+ const result = await this.#jsonRpcClient.dryRunTransactionBlock({
187
+ transactionBlock: options.transaction,
188
+ signal: options.signal,
189
+ })
190
+
191
+ const { effects, objectTypes } = parseTransactionEffectsJson({
192
+ effects: result.effects,
193
+ objectChanges: result.objectChanges,
194
+ })
195
+
196
+ return {
197
+ transaction: {
198
+ digest: await tx.getDigest(),
199
+ epoch: null,
200
+ effects,
201
+ objectTypes: Promise.resolve(objectTypes),
202
+ signatures: [],
203
+ transaction: parseTransactionBcs(options.transaction),
204
+ },
205
+ }
206
+ }
207
+
208
+ async getReferenceGasPrice(options) {
209
+ const referenceGasPrice = await this.#jsonRpcClient.getReferenceGasPrice({
210
+ signal: options?.signal,
211
+ })
212
+ return {
213
+ referenceGasPrice: String(referenceGasPrice),
214
+ }
215
+ }
216
+
217
+ async getDynamicFields(options) {
218
+ const dynamicFields = await this.#jsonRpcClient.getDynamicFields({
219
+ parentId: options.parentId,
220
+ limit: options.limit,
221
+ cursor: options.cursor,
222
+ })
223
+
224
+ return {
225
+ dynamicFields: dynamicFields.data.map((dynamicField) => {
226
+ return {
227
+ id: dynamicField.objectId,
228
+ type: dynamicField.objectType,
229
+ name: {
230
+ type: dynamicField.name.type,
231
+ bcs: fromBase64(dynamicField.bcsName),
232
+ },
233
+ }
234
+ }),
235
+ hasNextPage: dynamicFields.hasNextPage,
236
+ cursor: dynamicFields.nextCursor,
237
+ }
238
+ }
239
+
240
+ async verifyZkLoginSignature(options) {
241
+ const result = await this.#jsonRpcClient.verifyZkLoginSignature({
242
+ bytes: options.bytes,
243
+ signature: options.signature,
244
+ intentScope: options.intentScope,
245
+ author: options.author,
246
+ })
247
+
248
+ return {
249
+ success: result.success,
250
+ errors: result.errors,
251
+ }
252
+ }
253
+
254
+ resolveNameServiceNames(options) {
255
+ return this.#jsonRpcClient.resolveNameServiceNames(options)
256
+ }
257
+
258
+ resolveTransactionPlugin() {
259
+ return suiClientResolveTransactionPlugin(this.#jsonRpcClient)
260
+ }
261
+ }
262
+
263
+ function parseObject(object) {
264
+ return {
265
+ id: object.objectId,
266
+ version: object.version,
267
+ digest: object.digest,
268
+ type: object.type,
269
+ content: Promise.resolve(
270
+ object.bcs?.dataType === 'moveObject' ? fromBase64(object.bcs.bcsBytes) : new Uint8Array()
271
+ ),
272
+ owner: parseOwner(object.owner),
273
+ }
274
+ }
275
+
276
+ function parseOwner(owner) {
277
+ if (owner === 'Immutable') {
278
+ return {
279
+ $kind: 'Immutable',
280
+ Immutable: true,
281
+ }
282
+ }
283
+
284
+ if ('ConsensusAddressOwner' in owner) {
285
+ return {
286
+ $kind: 'ConsensusAddressOwner',
287
+ ConsensusAddressOwner: {
288
+ owner: owner.ConsensusAddressOwner.owner,
289
+ startVersion: owner.ConsensusAddressOwner.start_version,
290
+ },
291
+ }
292
+ }
293
+
294
+ if ('AddressOwner' in owner) {
295
+ return {
296
+ $kind: 'AddressOwner',
297
+ AddressOwner: owner.AddressOwner,
298
+ }
299
+ }
300
+
301
+ if ('ObjectOwner' in owner) {
302
+ return {
303
+ $kind: 'ObjectOwner',
304
+ ObjectOwner: owner.ObjectOwner,
305
+ }
306
+ }
307
+
308
+ if ('Shared' in owner) {
309
+ return {
310
+ $kind: 'Shared',
311
+ Shared: {
312
+ initialSharedVersion: owner.Shared.initial_shared_version,
313
+ },
314
+ }
315
+ }
316
+
317
+ throw new Error(`Unknown owner type: ${JSON.stringify(owner)}`)
318
+ }
319
+
320
+ function parseTransaction(transaction) {
321
+ const parsedTx = bcs.SenderSignedData.parse(fromBase64(transaction.rawTransaction))[0]
322
+ const objectTypes = Object.create(null)
323
+
324
+ transaction.objectChanges?.forEach((change) => {
325
+ if (change.type !== 'published') {
326
+ objectTypes[change.objectId] = change.objectType
327
+ }
328
+ })
329
+
330
+ const bytes = bcs.TransactionData.serialize(parsedTx.intentMessage.value).toBytes()
331
+
332
+ const data = TransactionDataBuilder.restore({
333
+ version: 2,
334
+ sender: parsedTx.intentMessage.value.V1.sender,
335
+ expiration: parsedTx.intentMessage.value.V1.expiration,
336
+ gasData: parsedTx.intentMessage.value.V1.gasData,
337
+ inputs: parsedTx.intentMessage.value.V1.kind.ProgrammableTransaction.inputs,
338
+ commands: parsedTx.intentMessage.value.V1.kind.ProgrammableTransaction.commands,
339
+ })
340
+
341
+ return {
342
+ digest: transaction.digest,
343
+ epoch: transaction.effects?.executedEpoch ?? null,
344
+ effects: parseTransactionEffectsBcs(new Uint8Array(transaction.rawEffects)),
345
+ objectTypes: Promise.resolve(objectTypes),
346
+ transaction: {
347
+ ...data,
348
+ bcs: bytes,
349
+ },
350
+ signatures: parsedTx.txSignatures,
351
+ }
352
+ }
353
+
354
+ function parseTransactionEffectsJson({ bytes, effects, objectChanges }) {
355
+ const changedObjects = []
356
+ const unchangedSharedObjects = []
357
+ const objectTypes = Object.create(null)
358
+
359
+ objectChanges?.forEach((change) => {
360
+ switch (change.type) {
361
+ case 'published':
362
+ changedObjects.push({
363
+ id: change.packageId,
364
+ inputState: 'DoesNotExist',
365
+ inputVersion: null,
366
+ inputDigest: null,
367
+ inputOwner: null,
368
+ outputState: 'PackageWrite',
369
+ outputVersion: change.version,
370
+ outputDigest: change.digest,
371
+ outputOwner: null,
372
+ idOperation: 'Created',
373
+ })
374
+ break
375
+ case 'transferred':
376
+ changedObjects.push({
377
+ id: change.objectId,
378
+ inputState: 'Exists',
379
+ inputVersion: change.version,
380
+ inputDigest: change.digest,
381
+ inputOwner: {
382
+ $kind: 'AddressOwner',
383
+ AddressOwner: change.sender,
384
+ },
385
+ outputState: 'ObjectWrite',
386
+ outputVersion: change.version,
387
+ outputDigest: change.digest,
388
+ outputOwner: parseOwner(change.recipient),
389
+ idOperation: 'None',
390
+ })
391
+ objectTypes[change.objectId] = change.objectType
392
+ break
393
+ case 'mutated':
394
+ changedObjects.push({
395
+ id: change.objectId,
396
+ inputState: 'Exists',
397
+ inputVersion: change.previousVersion,
398
+ inputDigest: null,
399
+ inputOwner: parseOwner(change.owner),
400
+ outputState: 'ObjectWrite',
401
+ outputVersion: change.version,
402
+ outputDigest: change.digest,
403
+ outputOwner: parseOwner(change.owner),
404
+ idOperation: 'None',
405
+ })
406
+ objectTypes[change.objectId] = change.objectType
407
+ break
408
+ case 'deleted':
409
+ changedObjects.push({
410
+ id: change.objectId,
411
+ inputState: 'Exists',
412
+ inputVersion: change.version,
413
+ inputDigest: effects.deleted?.find((d) => d.objectId === change.objectId)?.digest ?? null,
414
+ inputOwner: null,
415
+ outputState: 'DoesNotExist',
416
+ outputVersion: null,
417
+ outputDigest: null,
418
+ outputOwner: null,
419
+ idOperation: 'Deleted',
420
+ })
421
+ objectTypes[change.objectId] = change.objectType
422
+ break
423
+ case 'wrapped':
424
+ changedObjects.push({
425
+ id: change.objectId,
426
+ inputState: 'Exists',
427
+ inputVersion: change.version,
428
+ inputDigest: null,
429
+ inputOwner: {
430
+ $kind: 'AddressOwner',
431
+ AddressOwner: change.sender,
432
+ },
433
+ outputState: 'ObjectWrite',
434
+ outputVersion: change.version,
435
+ outputDigest:
436
+ effects.wrapped?.find((w) => w.objectId === change.objectId)?.digest ?? null,
437
+ outputOwner: {
438
+ $kind: 'ObjectOwner',
439
+ ObjectOwner: change.sender,
440
+ },
441
+ idOperation: 'None',
442
+ })
443
+ objectTypes[change.objectId] = change.objectType
444
+ break
445
+ case 'created':
446
+ changedObjects.push({
447
+ id: change.objectId,
448
+ inputState: 'DoesNotExist',
449
+ inputVersion: null,
450
+ inputDigest: null,
451
+ inputOwner: null,
452
+ outputState: 'ObjectWrite',
453
+ outputVersion: change.version,
454
+ outputDigest: change.digest,
455
+ outputOwner: parseOwner(change.owner),
456
+ idOperation: 'Created',
457
+ })
458
+ objectTypes[change.objectId] = change.objectType
459
+ break
460
+ }
461
+ })
462
+
463
+ return {
464
+ objectTypes,
465
+ effects: {
466
+ bcs: bytes ?? null,
467
+ digest: effects.transactionDigest,
468
+ version: 2,
469
+ status:
470
+ effects.status.status === 'success'
471
+ ? { success: true, error: null }
472
+ : { success: false, error: effects.status.error },
473
+ gasUsed: effects.gasUsed,
474
+ transactionDigest: effects.transactionDigest,
475
+ gasObject: {
476
+ id: effects.gasObject?.reference.objectId,
477
+ inputState: 'Exists',
478
+ inputVersion: null,
479
+ inputDigest: null,
480
+ inputOwner: null,
481
+ outputState: 'ObjectWrite',
482
+ outputVersion: effects.gasObject.reference.version,
483
+ outputDigest: effects.gasObject.reference.digest,
484
+ outputOwner: parseOwner(effects.gasObject.owner),
485
+ idOperation: 'None',
486
+ },
487
+ eventsDigest: effects.eventsDigest ?? null,
488
+ dependencies: effects.dependencies ?? [],
489
+ lamportVersion: effects.gasObject.reference.version,
490
+ changedObjects,
491
+ unchangedSharedObjects,
492
+ auxiliaryDataDigest: null,
493
+ },
494
+ }
495
+ }
496
+
497
+ const Balance = bcs.struct('Balance', {
498
+ value: bcs.u64(),
499
+ })
500
+
501
+ const Coin = bcs.struct('Coin', {
502
+ id: bcs.Address,
503
+ balance: Balance,
504
+ })
@@ -0,0 +1,90 @@
1
+ import { vendorLib } from '@exodus/sui-lib'
2
+
3
+ import { TransactionDataBuilder } from '../../transactions/TransactionData.js'
4
+
5
+ const { bcs } = vendorLib
6
+
7
+ function parseTransactionBcs(bytes) {
8
+ return {
9
+ ...TransactionDataBuilder.fromBytes(bytes).snapshot(),
10
+ bcs: bytes,
11
+ }
12
+ }
13
+
14
+ function parseTransactionEffectsBcs(effects) {
15
+ const parsed = bcs.TransactionEffects.parse(effects)
16
+ switch (parsed.$kind) {
17
+ case 'V1':
18
+ return parseTransactionEffectsV1({ bytes: effects, effects: parsed.V1 })
19
+ case 'V2':
20
+ return parseTransactionEffectsV2({ bytes: effects, effects: parsed.V2 })
21
+ default:
22
+ throw new Error(`Unknown transaction effects version: ${parsed.$kind}`)
23
+ }
24
+ }
25
+
26
+ function parseTransactionEffectsV1(_) {
27
+ throw new Error('V1 effects are not supported yet')
28
+ }
29
+
30
+ function parseTransactionEffectsV2({ bytes, effects }) {
31
+ const changedObjects = effects.changedObjects.map(([id, change]) => {
32
+ return {
33
+ id,
34
+ inputState: change.inputState.$kind === 'Exist' ? 'Exists' : 'DoesNotExist',
35
+ inputVersion: change.inputState.Exist?.[0][0] ?? null,
36
+ inputDigest: change.inputState.Exist?.[0][1] ?? null,
37
+ inputOwner: change.inputState.Exist?.[1] ?? null,
38
+ outputState:
39
+ change.outputState.$kind === 'NotExist' ? 'DoesNotExist' : change.outputState.$kind,
40
+ outputVersion:
41
+ change.outputState.$kind === 'PackageWrite'
42
+ ? change.outputState.PackageWrite?.[0]
43
+ : change.outputState.ObjectWrite
44
+ ? effects.lamportVersion
45
+ : null,
46
+ outputDigest:
47
+ change.outputState.$kind === 'PackageWrite'
48
+ ? change.outputState.PackageWrite?.[1]
49
+ : change.outputState.ObjectWrite?.[0] ?? null,
50
+ outputOwner: change.outputState.ObjectWrite ? change.outputState.ObjectWrite[1] : null,
51
+ idOperation: change.idOperation.$kind,
52
+ }
53
+ })
54
+ return {
55
+ bcs: bytes,
56
+ digest: effects.transactionDigest,
57
+ version: 2,
58
+ status:
59
+ effects.status.$kind === 'Success'
60
+ ? {
61
+ success: true,
62
+ error: null,
63
+ }
64
+ : {
65
+ success: false,
66
+ // TODO: add command
67
+ error: effects.status.Failed.error.$kind,
68
+ },
69
+ gasUsed: effects.gasUsed,
70
+ transactionDigest: effects.transactionDigest,
71
+ gasObject:
72
+ effects.gasObjectIndex === null ? null : changedObjects[effects.gasObjectIndex] ?? null,
73
+ eventsDigest: effects.eventsDigest,
74
+ dependencies: effects.dependencies,
75
+ lamportVersion: effects.lamportVersion,
76
+ changedObjects,
77
+ unchangedSharedObjects: effects.unchangedSharedObjects.map(([objectId, object]) => {
78
+ return {
79
+ kind: object.$kind,
80
+ objectId,
81
+ version: object.$kind === 'ReadOnlyRoot' ? object.ReadOnlyRoot[0] : object[object.$kind],
82
+ digest: object.$kind === 'ReadOnlyRoot' ? object.ReadOnlyRoot[1] : null,
83
+ }
84
+ }),
85
+ auxiliaryDataDigest: effects.auxDataDigest,
86
+ }
87
+ }
88
+
89
+ export { parseTransactionBcs, parseTransactionEffectsBcs }
90
+ // # sourceMappingURL=utils.js.map
@@ -0,0 +1,22 @@
1
+ import { createObjectMethods } from './object.js'
2
+ import { createPure } from './pure.js'
3
+
4
+ const Arguments = {
5
+ pure: createPure((value) => (tx) => tx.pure(value)),
6
+ object: createObjectMethods((value) => (tx) => tx.object(value)),
7
+ sharedObjectRef:
8
+ (...args) =>
9
+ (tx) =>
10
+ tx.sharedObjectRef(...args),
11
+ objectRef:
12
+ (...args) =>
13
+ (tx) =>
14
+ tx.objectRef(...args),
15
+ receivingRef:
16
+ (...args) =>
17
+ (tx) =>
18
+ tx.receivingRef(...args),
19
+ }
20
+
21
+ export { Arguments }
22
+ // # sourceMappingURL=Arguments.js.map