@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.
- package/README.md +20 -0
- package/package.json +53 -0
- package/src/api.js +198 -0
- package/src/balances.js +22 -0
- package/src/basic-account-state.js +11 -0
- package/src/create-unsigned-tx.js +60 -0
- package/src/get-fee-async.js +62 -0
- package/src/get-fee.js +10 -0
- package/src/index.js +9 -0
- package/src/sui-no-history-monitor.js +277 -0
- package/src/transform-tx.js +100 -0
- package/src/tx-send.js +249 -0
- package/src/vendors/client/client.js +891 -0
- package/src/vendors/client/errors.js +38 -0
- package/src/vendors/client/http-transport.js +108 -0
- package/src/vendors/client/index.js +1 -0
- package/src/vendors/client/rpc-websocket-client.js +178 -0
- package/src/vendors/experimental/cache.js +89 -0
- package/src/vendors/experimental/client.js +27 -0
- package/src/vendors/experimental/core.js +105 -0
- package/src/vendors/experimental/errors.js +32 -0
- package/src/vendors/experimental/index.js +5 -0
- package/src/vendors/experimental/mvr.js +443 -0
- package/src/vendors/experimental/transports/json-rpc-resolver.js +359 -0
- package/src/vendors/experimental/transports/jsonRPC.js +504 -0
- package/src/vendors/experimental/transports/utils.js +90 -0
- package/src/vendors/transactions/Arguments.js +22 -0
- package/src/vendors/transactions/Commands.js +108 -0
- package/src/vendors/transactions/Inputs.js +59 -0
- package/src/vendors/transactions/ObjectCache.js +272 -0
- package/src/vendors/transactions/Transaction.js +843 -0
- package/src/vendors/transactions/TransactionData.js +293 -0
- package/src/vendors/transactions/data/internal.js +218 -0
- package/src/vendors/transactions/data/v1.js +538 -0
- package/src/vendors/transactions/data/v2.js +119 -0
- package/src/vendors/transactions/hash.js +12 -0
- package/src/vendors/transactions/index.js +1 -0
- package/src/vendors/transactions/object.js +22 -0
- package/src/vendors/transactions/plugins/NamedPackagesPlugin.js +49 -0
- package/src/vendors/transactions/pure.js +40 -0
- package/src/vendors/transactions/resolve.js +95 -0
- package/src/vendors/transactions/serializer.js +203 -0
- package/src/vendors/transactions/utils.js +67 -0
- package/src/vendors/valibot.js +7920 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/* eslint-disable @exodus/mutable/no-param-reassign-prop-only */
|
|
2
|
+
/* eslint-disable no-return-await */
|
|
3
|
+
import { vendorLib } from '@exodus/sui-lib'
|
|
4
|
+
|
|
5
|
+
import { ObjectRef } from '../../transactions/data/internal.js'
|
|
6
|
+
import { Inputs } from '../../transactions/Inputs.js'
|
|
7
|
+
import {
|
|
8
|
+
getPureBcsSchema,
|
|
9
|
+
isTxContext,
|
|
10
|
+
normalizedTypeToMoveTypeSignature,
|
|
11
|
+
} from '../../transactions/serializer.js'
|
|
12
|
+
import { parse } from '../../valibot.js'
|
|
13
|
+
|
|
14
|
+
const { normalizeSuiAddress, normalizeSuiObjectId, SUI_TYPE_ARG } = vendorLib.utils
|
|
15
|
+
|
|
16
|
+
const { chunk } = vendorLib.utils
|
|
17
|
+
|
|
18
|
+
const MAX_OBJECTS_PER_FETCH = 50
|
|
19
|
+
const GAS_SAFE_OVERHEAD = BigInt(1000)
|
|
20
|
+
const MAX_GAS = 5e10
|
|
21
|
+
|
|
22
|
+
function suiClientResolveTransactionPlugin(client) {
|
|
23
|
+
return async function resolveTransactionData(transactionData, options, next) {
|
|
24
|
+
await normalizeInputs(transactionData, client)
|
|
25
|
+
await resolveObjectReferences(transactionData, client)
|
|
26
|
+
if (!options.onlyTransactionKind) {
|
|
27
|
+
await setGasPrice(transactionData, client)
|
|
28
|
+
await setGasBudget(transactionData, client)
|
|
29
|
+
await setGasPayment(transactionData, client)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return await next()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function setGasPrice(transactionData, client) {
|
|
37
|
+
if (!transactionData.gasConfig.price) {
|
|
38
|
+
transactionData.gasConfig.price = String(await client.getReferenceGasPrice())
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function setGasBudget(transactionData, client) {
|
|
43
|
+
if (transactionData.gasConfig.budget) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const dryRunResult = await client.dryRunTransactionBlock({
|
|
48
|
+
transactionBlock: transactionData.build({
|
|
49
|
+
overrides: {
|
|
50
|
+
gasData: {
|
|
51
|
+
budget: String(MAX_GAS),
|
|
52
|
+
payment: [],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
})
|
|
57
|
+
if (dryRunResult.effects.status.status !== 'success') {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Dry run failed, could not automatically determine a budget: ${dryRunResult.effects.status.error}`,
|
|
60
|
+
{ cause: dryRunResult }
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const safeOverhead = GAS_SAFE_OVERHEAD * BigInt(transactionData.gasConfig.price || BigInt(1))
|
|
65
|
+
const baseComputationCostWithOverhead =
|
|
66
|
+
BigInt(dryRunResult.effects.gasUsed.computationCost) + safeOverhead
|
|
67
|
+
const gasBudget =
|
|
68
|
+
baseComputationCostWithOverhead +
|
|
69
|
+
BigInt(dryRunResult.effects.gasUsed.storageCost) -
|
|
70
|
+
BigInt(dryRunResult.effects.gasUsed.storageRebate)
|
|
71
|
+
transactionData.gasConfig.budget = String(
|
|
72
|
+
gasBudget > baseComputationCostWithOverhead ? gasBudget : baseComputationCostWithOverhead
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function setGasPayment(transactionData, client) {
|
|
77
|
+
if (!transactionData.gasConfig.payment) {
|
|
78
|
+
const coins = await client.getCoins({
|
|
79
|
+
owner: transactionData.gasConfig.owner || transactionData.sender,
|
|
80
|
+
coinType: SUI_TYPE_ARG,
|
|
81
|
+
})
|
|
82
|
+
const paymentCoins = coins.data
|
|
83
|
+
.filter((coin) => {
|
|
84
|
+
const matchingInput = transactionData.inputs.find((input) => {
|
|
85
|
+
if (input.Object?.ImmOrOwnedObject) {
|
|
86
|
+
return coin.coinObjectId === input.Object.ImmOrOwnedObject.objectId
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false
|
|
90
|
+
})
|
|
91
|
+
return !matchingInput
|
|
92
|
+
})
|
|
93
|
+
.map((coin) => ({
|
|
94
|
+
objectId: coin.coinObjectId,
|
|
95
|
+
digest: coin.digest,
|
|
96
|
+
version: coin.version,
|
|
97
|
+
}))
|
|
98
|
+
if (paymentCoins.length === 0) {
|
|
99
|
+
throw new Error('No valid gas coins found for the transaction.')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
transactionData.gasConfig.payment = paymentCoins.map((payment) => parse(ObjectRef, payment))
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function resolveObjectReferences(transactionData, client) {
|
|
107
|
+
const objectsToResolve = transactionData.inputs.filter((input) => {
|
|
108
|
+
return (
|
|
109
|
+
input.UnresolvedObject &&
|
|
110
|
+
!(input.UnresolvedObject.version || input.UnresolvedObject?.initialSharedVersion)
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
const dedupedIds = [
|
|
114
|
+
...new Set(
|
|
115
|
+
objectsToResolve.map((input) => normalizeSuiObjectId(input.UnresolvedObject.objectId))
|
|
116
|
+
),
|
|
117
|
+
]
|
|
118
|
+
const objectChunks = dedupedIds.length > 0 ? chunk(dedupedIds, MAX_OBJECTS_PER_FETCH) : []
|
|
119
|
+
const resolved = (
|
|
120
|
+
await Promise.all(
|
|
121
|
+
objectChunks.map((chunk2) =>
|
|
122
|
+
client.multiGetObjects({
|
|
123
|
+
ids: chunk2,
|
|
124
|
+
options: { showOwner: true },
|
|
125
|
+
})
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
// eslint-disable-next-line unicorn/no-await-expression-member
|
|
130
|
+
.flat()
|
|
131
|
+
const responsesById = new Map(
|
|
132
|
+
dedupedIds.map((id, index) => {
|
|
133
|
+
return [id, resolved[index]]
|
|
134
|
+
})
|
|
135
|
+
)
|
|
136
|
+
const invalidObjects = [...responsesById]
|
|
137
|
+
.filter(([_, obj]) => obj.error)
|
|
138
|
+
.map(([_, obj]) => JSON.stringify(obj.error))
|
|
139
|
+
if (invalidObjects.length > 0) {
|
|
140
|
+
throw new Error(`The following input objects are invalid: ${invalidObjects.join(', ')}`)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const objects = resolved.map((object) => {
|
|
144
|
+
if (object.error || !object.data) {
|
|
145
|
+
throw new Error(`Failed to fetch object: ${object.error}`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const owner = object.data.owner
|
|
149
|
+
const initialSharedVersion =
|
|
150
|
+
owner && typeof owner === 'object'
|
|
151
|
+
? 'Shared' in owner
|
|
152
|
+
? owner.Shared.initial_shared_version
|
|
153
|
+
: 'ConsensusAddressOwner' in owner
|
|
154
|
+
? owner.ConsensusAddressOwner.start_version
|
|
155
|
+
: null
|
|
156
|
+
: null
|
|
157
|
+
return {
|
|
158
|
+
objectId: object.data.objectId,
|
|
159
|
+
digest: object.data.digest,
|
|
160
|
+
version: object.data.version,
|
|
161
|
+
initialSharedVersion,
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
const objectsById = new Map(
|
|
165
|
+
dedupedIds.map((id, index) => {
|
|
166
|
+
return [id, objects[index]]
|
|
167
|
+
})
|
|
168
|
+
)
|
|
169
|
+
for (const [index, input] of transactionData.inputs.entries()) {
|
|
170
|
+
if (!input.UnresolvedObject) {
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let updated
|
|
175
|
+
const id = normalizeSuiAddress(input.UnresolvedObject.objectId)
|
|
176
|
+
const object = objectsById.get(id)
|
|
177
|
+
if (input.UnresolvedObject.initialSharedVersion ?? object?.initialSharedVersion) {
|
|
178
|
+
updated = Inputs.SharedObjectRef({
|
|
179
|
+
objectId: id,
|
|
180
|
+
initialSharedVersion:
|
|
181
|
+
input.UnresolvedObject.initialSharedVersion || object?.initialSharedVersion,
|
|
182
|
+
mutable: isUsedAsMutable(transactionData, index),
|
|
183
|
+
})
|
|
184
|
+
} else if (isUsedAsReceiving(transactionData, index)) {
|
|
185
|
+
updated = Inputs.ReceivingRef({
|
|
186
|
+
objectId: id,
|
|
187
|
+
digest: input.UnresolvedObject.digest ?? object?.digest,
|
|
188
|
+
version: input.UnresolvedObject.version ?? object?.version,
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
transactionData.inputs[transactionData.inputs.indexOf(input)] =
|
|
193
|
+
updated ??
|
|
194
|
+
Inputs.ObjectRef({
|
|
195
|
+
objectId: id,
|
|
196
|
+
digest: input.UnresolvedObject.digest ?? object?.digest,
|
|
197
|
+
version: input.UnresolvedObject.version ?? object?.version,
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function normalizeInputs(transactionData, client) {
|
|
203
|
+
const { inputs, commands } = transactionData
|
|
204
|
+
const moveCallsToResolve = []
|
|
205
|
+
const moveFunctionsToResolve = /* @__PURE__ */ new Set()
|
|
206
|
+
commands.forEach((command) => {
|
|
207
|
+
if (command.MoveCall) {
|
|
208
|
+
if (command.MoveCall._argumentTypes) {
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const inputs2 = command.MoveCall.arguments.map((arg) => {
|
|
213
|
+
if (arg.$kind === 'Input') {
|
|
214
|
+
return transactionData.inputs[arg.Input]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return null
|
|
218
|
+
})
|
|
219
|
+
const needsResolution = inputs2.some(
|
|
220
|
+
(input) => input?.UnresolvedPure || input?.UnresolvedObject
|
|
221
|
+
)
|
|
222
|
+
if (needsResolution) {
|
|
223
|
+
const functionName = `${command.MoveCall.package}::${command.MoveCall.module}::${command.MoveCall.function}`
|
|
224
|
+
moveFunctionsToResolve.add(functionName)
|
|
225
|
+
moveCallsToResolve.push(command.MoveCall)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
const moveFunctionParameters = /* @__PURE__ */ new Map()
|
|
230
|
+
if (moveFunctionsToResolve.size > 0) {
|
|
231
|
+
await Promise.all(
|
|
232
|
+
[...moveFunctionsToResolve].map(async (functionName) => {
|
|
233
|
+
const [packageId, moduleId, functionId] = functionName.split('::')
|
|
234
|
+
const def = await client.getNormalizedMoveFunction({
|
|
235
|
+
package: packageId,
|
|
236
|
+
module: moduleId,
|
|
237
|
+
function: functionId,
|
|
238
|
+
})
|
|
239
|
+
moveFunctionParameters.set(
|
|
240
|
+
functionName,
|
|
241
|
+
def.parameters.map((param) => normalizedTypeToMoveTypeSignature(param))
|
|
242
|
+
)
|
|
243
|
+
})
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (moveCallsToResolve.length > 0) {
|
|
248
|
+
await Promise.all(
|
|
249
|
+
moveCallsToResolve.map(async (moveCall) => {
|
|
250
|
+
const parameters = moveFunctionParameters.get(
|
|
251
|
+
`${moveCall.package}::${moveCall.module}::${moveCall.function}`
|
|
252
|
+
)
|
|
253
|
+
if (!parameters) {
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const hasTxContext = parameters.length > 0 && isTxContext(parameters.at(-1))
|
|
258
|
+
const params = hasTxContext ? parameters.slice(0, -1) : parameters
|
|
259
|
+
moveCall._argumentTypes = params
|
|
260
|
+
})
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
commands.forEach((command) => {
|
|
265
|
+
if (!command.MoveCall) {
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const moveCall = command.MoveCall
|
|
270
|
+
const fnName = `${moveCall.package}::${moveCall.module}::${moveCall.function}`
|
|
271
|
+
const params = moveCall._argumentTypes
|
|
272
|
+
if (!params) {
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (params.length !== command.MoveCall.arguments.length) {
|
|
277
|
+
throw new Error(`Incorrect number of arguments for ${fnName}`)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
params.forEach((param, i) => {
|
|
281
|
+
const arg = moveCall.arguments[i]
|
|
282
|
+
if (arg.$kind !== 'Input') return
|
|
283
|
+
const input = inputs[arg.Input]
|
|
284
|
+
if (!input.UnresolvedPure && !input.UnresolvedObject) {
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const inputValue = input.UnresolvedPure?.value ?? input.UnresolvedObject?.objectId
|
|
289
|
+
const schema = getPureBcsSchema(param.body)
|
|
290
|
+
if (schema) {
|
|
291
|
+
arg.type = 'pure'
|
|
292
|
+
inputs[inputs.indexOf(input)] = Inputs.Pure(schema.serialize(inputValue))
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (typeof inputValue !== 'string') {
|
|
297
|
+
throw new TypeError(
|
|
298
|
+
`Expect the argument to be an object id string, got ${JSON.stringify(
|
|
299
|
+
inputValue,
|
|
300
|
+
null,
|
|
301
|
+
2
|
|
302
|
+
)}`
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
arg.type = 'object'
|
|
307
|
+
const unresolvedObject = input.UnresolvedPure
|
|
308
|
+
? {
|
|
309
|
+
$kind: 'UnresolvedObject',
|
|
310
|
+
UnresolvedObject: {
|
|
311
|
+
objectId: inputValue,
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
: input
|
|
315
|
+
inputs[arg.Input] = unresolvedObject
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function isUsedAsMutable(transactionData, index) {
|
|
321
|
+
let usedAsMutable = false
|
|
322
|
+
transactionData.getInputUses(index, (arg, tx) => {
|
|
323
|
+
if (tx.MoveCall && tx.MoveCall._argumentTypes) {
|
|
324
|
+
const argIndex = tx.MoveCall.arguments.indexOf(arg)
|
|
325
|
+
usedAsMutable = tx.MoveCall._argumentTypes[argIndex].ref !== '&' || usedAsMutable
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (tx.$kind === 'MakeMoveVec' || tx.$kind === 'MergeCoins' || tx.$kind === 'SplitCoins') {
|
|
329
|
+
usedAsMutable = true
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
return usedAsMutable
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function isUsedAsReceiving(transactionData, index) {
|
|
336
|
+
let usedAsReceiving = false
|
|
337
|
+
transactionData.getInputUses(index, (arg, tx) => {
|
|
338
|
+
if (tx.MoveCall && tx.MoveCall._argumentTypes) {
|
|
339
|
+
const argIndex = tx.MoveCall.arguments.indexOf(arg)
|
|
340
|
+
usedAsReceiving = isReceivingType(tx.MoveCall._argumentTypes[argIndex]) || usedAsReceiving
|
|
341
|
+
}
|
|
342
|
+
})
|
|
343
|
+
return usedAsReceiving
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function isReceivingType(type) {
|
|
347
|
+
if (typeof type.body !== 'object' || !('datatype' in type.body)) {
|
|
348
|
+
return false
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return (
|
|
352
|
+
type.body.datatype.package === '0x2' &&
|
|
353
|
+
type.body.datatype.module === 'transfer' &&
|
|
354
|
+
type.body.datatype.type === 'Receiving'
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export { suiClientResolveTransactionPlugin }
|
|
359
|
+
// # sourceMappingURL=json-rpc-resolver.js.map
|