@exodus/sui-api 1.3.0 → 1.4.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.
@@ -1,4 +1,5 @@
1
1
  import { Transaction } from '../vendors/transactions/index.js'
2
+ import { getPureBcsSchema } from '../vendors/transactions/serializer.js'
2
3
  import {
3
4
  ADD_STAKE_FUNCTION,
4
5
  ADD_STAKE_TARGET,
@@ -49,6 +50,14 @@ const getInput = ({ inputs, argument }) =>
49
50
  ? inputs[argument.Input]
50
51
  : undefined
51
52
 
53
+ const ADDRESS_LENGTH = 32
54
+
55
+ const getAddressValue = (input) => {
56
+ if (input?.valueType !== null) return input?.value
57
+ if (input.value.length !== ADDRESS_LENGTH) return
58
+ return getPureBcsSchema('address').parse(Uint8Array.from(input.value))
59
+ }
60
+
52
61
  const getSplitAmount = ({ inputs, commands, argument }) => {
53
62
  const resultIndex = argument?.NestedResult?.[0] ?? argument?.Result
54
63
  const amountArgument = commands[resultIndex]?.SplitCoins?.[1]?.[0]
@@ -74,7 +83,7 @@ export const parseStakingCommands = (programmableTransaction) => {
74
83
  const amount = getSplitAmount({ inputs, commands, argument: coinArgument })
75
84
  return {
76
85
  operation,
77
- validator: getInput({ inputs, argument: validatorArgument })?.value,
86
+ validator: getAddressValue(getInput({ inputs, argument: validatorArgument })),
78
87
  ...(amount && { amount }),
79
88
  }
80
89
  }
package/src/tx-send.js CHANGED
@@ -1,11 +1,48 @@
1
1
  import { UtxoCollection } from '@exodus/models'
2
2
  import { coinTypes, objectTypes, toCoinType } from '@exodus/sui-lib'
3
3
  import assert from 'minimalistic-assert'
4
+ import ms from 'ms'
4
5
 
6
+ import { SuiBroadcastError } from './broadcast-error.js'
5
7
  import { applyStakingTx } from './staking/staking-info.js'
6
8
  import { parseStakingCommands } from './staking/staking-transactions.js'
7
9
  import { getFeeAmount } from './transform-tx.js'
8
10
 
11
+ const LOOKUP_ATTEMPTS = 5
12
+ const LOOKUP_DELAY_MS = ms('3s')
13
+
14
+ const wait = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs))
15
+
16
+ const findExecutedTransaction = async ({ serverApi, digest, lookupAttempts, lookupDelayMs }) => {
17
+ for (let attempt = 0; attempt < lookupAttempts; attempt++) {
18
+ await wait(lookupDelayMs)
19
+ const [transaction] = await serverApi.getTransactions({ txIds: [digest] }).catch(() => [])
20
+ if (transaction) return transaction
21
+ }
22
+ }
23
+
24
+ const broadcastTransaction = async ({
25
+ asset,
26
+ plainTx,
27
+ serverApi,
28
+ lookupAttempts,
29
+ lookupDelayMs,
30
+ }) => {
31
+ try {
32
+ return await asset.baseAsset.api.broadcastTx(plainTx)
33
+ } catch (error) {
34
+ if (!(error instanceof SuiBroadcastError)) throw error
35
+ const transaction = await findExecutedTransaction({
36
+ serverApi,
37
+ digest: error.digest,
38
+ lookupAttempts,
39
+ lookupDelayMs,
40
+ })
41
+ if (!transaction) throw error
42
+ return transaction
43
+ }
44
+ }
45
+
9
46
  const deriveNewUtxos = ({ mutatedObject, balanceChange, deletedObjects, utxos, asset }) => {
10
47
  const utxoArray = utxos.toArray()
11
48
 
@@ -252,7 +289,13 @@ const getStateChanges = async ({ aci, asset, walletAccount, result }) => {
252
289
  }
253
290
  }
254
291
 
255
- export const txSendFactory = ({ assetClientInterface: aci, serverApi, createTx }) => {
292
+ export const txSendFactory = ({
293
+ assetClientInterface: aci,
294
+ serverApi,
295
+ createTx,
296
+ lookupAttempts = LOOKUP_ATTEMPTS,
297
+ lookupDelayMs = LOOKUP_DELAY_MS,
298
+ }) => {
256
299
  assert(aci, 'assetClientInterface is required')
257
300
  assert(serverApi, 'serverApi is required')
258
301
  assert(createTx, 'createTx is required')
@@ -268,7 +311,13 @@ export const txSendFactory = ({ assetClientInterface: aci, serverApi, createTx }
268
311
  walletAccount,
269
312
  })
270
313
 
271
- const result = await asset.baseAsset.api.broadcastTx(plainTx)
314
+ const result = await broadcastTransaction({
315
+ asset,
316
+ plainTx,
317
+ serverApi,
318
+ lookupAttempts,
319
+ lookupDelayMs,
320
+ })
272
321
 
273
322
  const { accountStateData: newData, txLogsByAssetName } = await getStateChanges({
274
323
  asset,
@@ -1,7 +1,7 @@
1
1
  import { vendorLib } from '@exodus/sui-lib'
2
2
 
3
- import { suiClientResolveTransactionPlugin } from '../experimental/transports/json-rpc-resolver.js'
4
3
  import { Inputs } from './Inputs.js'
4
+ import { suiClientResolveTransactionPlugin } from './resolver.js'
5
5
 
6
6
  const { bcs } = vendorLib
7
7
 
@@ -33,8 +33,7 @@ async function resolveTransactionPlugin(transactionData, options, next) {
33
33
  }
34
34
 
35
35
  const client = getClient(options)
36
- const plugin =
37
- client.core?.resolveTransactionPlugin() ?? suiClientResolveTransactionPlugin(client)
36
+ const plugin = suiClientResolveTransactionPlugin(client)
38
37
  return plugin(transactionData, options, async () => {
39
38
  await validate(transactionData)
40
39
  await next()
@@ -2,14 +2,10 @@
2
2
  /* eslint-disable no-return-await */
3
3
  import { vendorLib } from '@exodus/sui-lib'
4
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'
5
+ import { parse } from '../valibot.js'
6
+ import { ObjectRef } from './data/internal.js'
7
+ import { Inputs } from './Inputs.js'
8
+ import { getPureBcsSchema, isTxContext } from './serializer.js'
13
9
 
14
10
  const { normalizeSuiAddress, normalizeSuiObjectId, SUI_TYPE_ARG } = vendorLib.utils
15
11
 
@@ -18,6 +14,7 @@ const { chunk } = vendorLib.utils
18
14
  const MAX_OBJECTS_PER_FETCH = 50
19
15
  const GAS_SAFE_OVERHEAD = BigInt(1000)
20
16
  const MAX_GAS = 5e10
17
+ const MAX_GAS_OBJECTS = 256
21
18
 
22
19
  function suiClientResolveTransactionPlugin(client) {
23
20
  return async function resolveTransactionData(transactionData, options, next) {
@@ -35,7 +32,7 @@ function suiClientResolveTransactionPlugin(client) {
35
32
 
36
33
  async function setGasPrice(transactionData, client) {
37
34
  if (!transactionData.gasConfig.price) {
38
- transactionData.gasConfig.price = String(await client.getReferenceGasPrice())
35
+ transactionData.gasConfig.price = await client.getReferenceGasPrice()
39
36
  }
40
37
  }
41
38
 
@@ -44,7 +41,7 @@ async function setGasBudget(transactionData, client) {
44
41
  return
45
42
  }
46
43
 
47
- const dryRunResult = await client.dryRunTransactionBlock({
44
+ const dryRunResult = await client.dryRunTransaction({
48
45
  transactionBlock: transactionData.build({
49
46
  overrides: {
50
47
  gasData: {
@@ -79,7 +76,7 @@ async function setGasPayment(transactionData, client) {
79
76
  owner: transactionData.gasConfig.owner || transactionData.sender,
80
77
  coinType: SUI_TYPE_ARG,
81
78
  })
82
- const paymentCoins = coins.data
79
+ const paymentCoins = coins
83
80
  .filter((coin) => {
84
81
  const matchingInput = transactionData.inputs.find((input) => {
85
82
  if (input.Object?.ImmOrOwnedObject) {
@@ -90,6 +87,8 @@ async function setGasPayment(transactionData, client) {
90
87
  })
91
88
  return !matchingInput
92
89
  })
90
+ .sort((a, b) => (BigInt(b.balance) > BigInt(a.balance) ? 1 : -1))
91
+ .slice(0, MAX_GAS_OBJECTS)
93
92
  .map((coin) => ({
94
93
  objectId: coin.coinObjectId,
95
94
  digest: coin.digest,
@@ -117,47 +116,13 @@ async function resolveObjectReferences(transactionData, client) {
117
116
  ]
118
117
  const objectChunks = dedupedIds.length > 0 ? chunk(dedupedIds, MAX_OBJECTS_PER_FETCH) : []
119
118
  const resolvedChunks = await Promise.all(
120
- objectChunks.map((chunk2) =>
121
- client.multiGetObjects({
122
- ids: chunk2,
123
- options: { showOwner: true },
124
- })
125
- )
126
- )
127
- const resolved = resolvedChunks.flat()
128
- const responsesById = new Map(
129
- dedupedIds.map((id, index) => {
130
- return [id, resolved[index]]
131
- })
119
+ objectChunks.map((chunk2) => client.getObjectRefs({ ids: chunk2 }))
132
120
  )
133
- const invalidObjects = [...responsesById]
134
- .filter(([_, obj]) => obj.error)
135
- .map(([_, obj]) => JSON.stringify(obj.error))
136
- if (invalidObjects.length > 0) {
137
- throw new Error(`The following input objects are invalid: ${invalidObjects.join(', ')}`)
121
+ const objects = resolvedChunks.flat()
122
+ const missing = dedupedIds.filter((id, index) => !objects[index])
123
+ if (missing.length > 0) {
124
+ throw new Error(`The following input objects are invalid: ${missing.join(', ')}`)
138
125
  }
139
-
140
- const objects = resolved.map((object) => {
141
- if (object.error || !object.data) {
142
- throw new Error(`Failed to fetch object: ${object.error}`)
143
- }
144
-
145
- const owner = object.data.owner
146
- const initialSharedVersion =
147
- owner && typeof owner === 'object'
148
- ? 'Shared' in owner
149
- ? owner.Shared.initial_shared_version
150
- : 'ConsensusAddressOwner' in owner
151
- ? owner.ConsensusAddressOwner.start_version
152
- : null
153
- : null
154
- return {
155
- objectId: object.data.objectId,
156
- digest: object.data.digest,
157
- version: object.data.version,
158
- initialSharedVersion,
159
- }
160
- })
161
126
  const objectsById = new Map(
162
127
  dedupedIds.map((id, index) => {
163
128
  return [id, objects[index]]
@@ -228,15 +193,12 @@ async function normalizeInputs(transactionData, client) {
228
193
  await Promise.all(
229
194
  [...moveFunctionsToResolve].map(async (functionName) => {
230
195
  const [packageId, moduleId, functionId] = functionName.split('::')
231
- const def = await client.getNormalizedMoveFunction({
196
+ const parameters = await client.getMoveFunctionParameters({
232
197
  package: packageId,
233
198
  module: moduleId,
234
199
  function: functionId,
235
200
  })
236
- moveFunctionParameters.set(
237
- functionName,
238
- def.parameters.map((param) => normalizedTypeToMoveTypeSignature(param))
239
- )
201
+ moveFunctionParameters.set(functionName, parameters)
240
202
  })
241
203
  )
242
204
  }
@@ -346,7 +308,7 @@ function isReceivingType(type) {
346
308
  }
347
309
 
348
310
  return (
349
- type.body.datatype.package === '0x2' &&
311
+ normalizeSuiAddress(type.body.datatype.package) === normalizeSuiAddress('0x2') &&
350
312
  type.body.datatype.module === 'transfer' &&
351
313
  type.body.datatype.type === 'Receiving'
352
314
  )