@exodus/sui-api 1.1.3 → 1.2.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/CHANGELOG.md +10 -0
- package/package.json +2 -2
- package/src/get-fee-async.js +0 -1
- package/src/index.js +1 -0
- package/src/sui-no-history-monitor.js +3 -3
- package/src/tx-create.js +40 -0
- package/src/tx-send.js +63 -61
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.2.0](https://github.com/ExodusMovement/assets/compare/@exodus/sui-api@1.1.3...@exodus/sui-api@1.2.0) (2026-01-06)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* feat: add createTx for SUI (#7143)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
## [1.1.3](https://github.com/ExodusMovement/assets/compare/@exodus/sui-api@1.1.2...@exodus/sui-api@1.1.3) (2025-11-03)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @exodus/sui-api
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/sui-api",
|
|
3
3
|
"packageManager": "yarn@4.3.1",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"description": "Transaction monitors, RPC with the blockchain node, and other networking code for Sui",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "src/index.js",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"type": "git",
|
|
51
51
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "f32e29d35f604bd6857bb1d19e2896b34352f122"
|
|
54
54
|
}
|
package/src/get-fee-async.js
CHANGED
|
@@ -21,7 +21,6 @@ export const getFeeAsyncFactory = ({ assetClientInterface, serverApi, createUnsi
|
|
|
21
21
|
walletAccount,
|
|
22
22
|
recipientAddress: recipientAddress || senderAddress,
|
|
23
23
|
amount: amount.toBaseString(),
|
|
24
|
-
serverApi,
|
|
25
24
|
gasBudget: MIN_GAS_BUDGET,
|
|
26
25
|
buildOptions: {
|
|
27
26
|
onlyTransactionKind: true,
|
package/src/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { createSuiNoHistoryMonitor } from './sui-no-history-monitor.js'
|
|
|
7
7
|
export { getFeeAsyncFactory } from './get-fee-async.js'
|
|
8
8
|
export { txSendFactory } from './tx-send.js'
|
|
9
9
|
export { createUnsignedTxFactory } from './create-unsigned-tx.js'
|
|
10
|
+
export { createTxFactory } from './tx-create.js'
|
|
@@ -53,7 +53,7 @@ export class SuiNoHistoryMonitor extends BaseMonitor {
|
|
|
53
53
|
|
|
54
54
|
let batch = this.aci.createOperationsBatch()
|
|
55
55
|
|
|
56
|
-
batch =
|
|
56
|
+
batch = this.aci.updateAccountStateBatch({
|
|
57
57
|
assetName,
|
|
58
58
|
walletAccount,
|
|
59
59
|
newData: {
|
|
@@ -71,11 +71,11 @@ export class SuiNoHistoryMonitor extends BaseMonitor {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
for (const [assetName, txsToRemove] of Object.entries(txsToRemoveByAsset)) {
|
|
74
|
-
batch =
|
|
74
|
+
batch = this.aci.removeTxLogBatch({ assetName, walletAccount, txs: txsToRemove, batch })
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
for (const [assetName, txLogItems] of Object.entries(txLogItemsByAsset)) {
|
|
78
|
-
batch =
|
|
78
|
+
batch = this.aci.updateTxLogAndNotifyBatch({
|
|
79
79
|
assetName,
|
|
80
80
|
walletAccount,
|
|
81
81
|
txs: txLogItems,
|
package/src/tx-create.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
2
|
+
|
|
3
|
+
export const createTxFactory = ({
|
|
4
|
+
assetClientInterface: aci,
|
|
5
|
+
serverApi,
|
|
6
|
+
getFeeAsync,
|
|
7
|
+
createUnsignedTx,
|
|
8
|
+
}) => {
|
|
9
|
+
assert(aci, 'assetClientInterface is required')
|
|
10
|
+
assert(serverApi, 'serverApi is required')
|
|
11
|
+
assert(getFeeAsync, 'getFeeAsync is required')
|
|
12
|
+
assert(createUnsignedTx, 'createUnsignedTx is required')
|
|
13
|
+
|
|
14
|
+
return async ({ asset, walletAccount, fromAddress, address: toAddress, amount, gasBudget }) => {
|
|
15
|
+
const { name: assetName } = asset
|
|
16
|
+
|
|
17
|
+
if (!fromAddress) {
|
|
18
|
+
fromAddress = await aci.getReceiveAddress({ assetName, walletAccount })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const unsignedTx = await createUnsignedTx({
|
|
22
|
+
asset,
|
|
23
|
+
senderAddress: fromAddress,
|
|
24
|
+
recipientAddress: toAddress,
|
|
25
|
+
amount: amount.toBaseString(),
|
|
26
|
+
gasBudget,
|
|
27
|
+
walletAccount,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const { fee } = await getFeeAsync({
|
|
31
|
+
asset,
|
|
32
|
+
walletAccount,
|
|
33
|
+
toAddress,
|
|
34
|
+
fromAddress,
|
|
35
|
+
amount,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return { unsignedTx, fee }
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/tx-send.js
CHANGED
|
@@ -125,40 +125,26 @@ const getObjectAndBalanceChangesByType = ({
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
recipientAddress,
|
|
136
|
-
}) => {
|
|
128
|
+
const getRecipientAddress = ({ balanceChanges, ownerAddress }) => {
|
|
129
|
+
const receiverItem = balanceChanges.find((item) => item.owner.AddressOwner !== ownerAddress)
|
|
130
|
+
return receiverItem?.owner?.AddressOwner
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// TODO: extract into separate file
|
|
134
|
+
const getStateChanges = async ({ aci, asset, walletAccount, result }) => {
|
|
137
135
|
const { feeAsset, currency: assetCurrency, name: assetName } = asset
|
|
138
136
|
const { objectChanges, balanceChanges, digest, effects } = result
|
|
139
137
|
const { gasUsed } = effects
|
|
140
138
|
const wasBaseAssetSent = assetName === asset.baseAssetName
|
|
141
139
|
|
|
142
140
|
const fee = getFeeAmount({ gasUsed, feeAsset })
|
|
141
|
+
const ownerAddress = await aci.getReceiveAddress({ assetName, walletAccount })
|
|
143
142
|
|
|
144
143
|
const accountState = await aci.getAccountState({
|
|
145
144
|
assetName: asset.baseAssetName,
|
|
146
145
|
walletAccount,
|
|
147
146
|
})
|
|
148
147
|
|
|
149
|
-
const tx = {
|
|
150
|
-
txId: digest,
|
|
151
|
-
confirmations: 0,
|
|
152
|
-
coinName: assetName,
|
|
153
|
-
coinAmount: amount.abs().negate(),
|
|
154
|
-
feeAmount: fee,
|
|
155
|
-
feeCoinName: feeAsset.name,
|
|
156
|
-
selfSend: false,
|
|
157
|
-
to: recipientAddress,
|
|
158
|
-
data: Object.create(null),
|
|
159
|
-
currencies: { [assetName]: assetCurrency, [feeAsset.name]: feeAsset.currency },
|
|
160
|
-
}
|
|
161
|
-
|
|
162
148
|
const { ownerObjectChangesByType, ownerBalanceChangesByType } = getObjectAndBalanceChangesByType({
|
|
163
149
|
objectChanges,
|
|
164
150
|
balanceChanges,
|
|
@@ -178,7 +164,10 @@ const updateAccountStateAfterSend = async ({
|
|
|
178
164
|
|
|
179
165
|
newAccountState.utxos = newBaseUtxos.utxos
|
|
180
166
|
|
|
181
|
-
|
|
167
|
+
let amount
|
|
168
|
+
if (wasBaseAssetSent) {
|
|
169
|
+
amount = asset.currency.baseUnit(ownerBalanceChangesByType[coinTypes.Base]).add(fee)
|
|
170
|
+
} else {
|
|
182
171
|
const coinType = toCoinType(asset.assetId)
|
|
183
172
|
const newTokenUtxos = deriveNewUtxos({
|
|
184
173
|
mutatedObject: ownerObjectChangesByType[coinType].mutated[0],
|
|
@@ -189,14 +178,26 @@ const updateAccountStateAfterSend = async ({
|
|
|
189
178
|
})
|
|
190
179
|
|
|
191
180
|
newAccountState.tokenUtxos = { [asset.name]: newTokenUtxos.utxos }
|
|
181
|
+
amount = asset.currency.baseUnit(ownerBalanceChangesByType[asset.assetId])
|
|
192
182
|
}
|
|
193
183
|
|
|
194
|
-
|
|
184
|
+
const to = getRecipientAddress({ balanceChanges, ownerAddress })
|
|
195
185
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
186
|
+
const tx = {
|
|
187
|
+
txId: digest,
|
|
188
|
+
confirmations: 0,
|
|
189
|
+
coinName: assetName,
|
|
190
|
+
coinAmount: amount.abs().negate(),
|
|
191
|
+
feeAmount: fee,
|
|
192
|
+
feeCoinName: feeAsset.name,
|
|
193
|
+
selfSend: false,
|
|
194
|
+
to,
|
|
195
|
+
data: Object.create(null),
|
|
196
|
+
currencies: { [assetName]: assetCurrency, [feeAsset.name]: feeAsset.currency },
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
accountStateData: {
|
|
200
201
|
utxos: newAccountState.utxos,
|
|
201
202
|
tokenUtxos: {
|
|
202
203
|
...accountState.tokenUtxos,
|
|
@@ -205,52 +206,53 @@ const updateAccountStateAfterSend = async ({
|
|
|
205
206
|
: Object.create(null)),
|
|
206
207
|
},
|
|
207
208
|
},
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
await aci.executeOperationsBatch(batch)
|
|
209
|
+
txLogsByAssetName: {
|
|
210
|
+
[assetName]: [tx],
|
|
211
|
+
// TODO: add fee-to-send entry to the feeAsset txLog
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
export const txSendFactory = ({ assetClientInterface: aci, serverApi,
|
|
216
|
+
export const txSendFactory = ({ assetClientInterface: aci, serverApi, createTx }) => {
|
|
217
217
|
assert(aci, 'assetClientInterface is required')
|
|
218
|
+
assert(serverApi, 'serverApi is required')
|
|
219
|
+
assert(createTx, 'createTx is required')
|
|
218
220
|
|
|
219
|
-
return async ({
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
amount,
|
|
224
|
-
gasBudget,
|
|
225
|
-
feeOpts,
|
|
226
|
-
}) => {
|
|
227
|
-
const { name: assetName } = asset
|
|
228
|
-
|
|
229
|
-
const senderAddress = await aci.getReceiveAddress({ assetName, walletAccount })
|
|
221
|
+
return async ({ asset, walletAccount, unsignedTx: providedUnsignedTx, ...legacyParams }) => {
|
|
222
|
+
const { unsignedTx } = providedUnsignedTx
|
|
223
|
+
? { unsignedTx: providedUnsignedTx }
|
|
224
|
+
: await createTx({ asset, walletAccount, ...legacyParams })
|
|
230
225
|
|
|
231
|
-
const
|
|
232
|
-
asset,
|
|
233
|
-
|
|
234
|
-
recipientAddress,
|
|
235
|
-
amount: amount.toBaseString(),
|
|
236
|
-
serverApi,
|
|
237
|
-
gasBudget: gasBudget || feeOpts.gasBudget,
|
|
226
|
+
const { plainTx } = await aci.signTransaction({
|
|
227
|
+
assetName: asset.name,
|
|
228
|
+
unsignedTx,
|
|
238
229
|
walletAccount,
|
|
239
230
|
})
|
|
240
231
|
|
|
241
|
-
const
|
|
232
|
+
const result = await asset.baseAsset.api.broadcastTx(plainTx)
|
|
242
233
|
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
await updateAccountStateAfterSend({
|
|
246
|
-
aci,
|
|
234
|
+
const { accountStateData: newData, txLogsByAssetName } = await getStateChanges({
|
|
247
235
|
asset,
|
|
248
236
|
walletAccount,
|
|
249
|
-
ownerAddress: senderAddress,
|
|
250
237
|
result,
|
|
251
|
-
|
|
252
|
-
recipientAddress,
|
|
238
|
+
aci,
|
|
253
239
|
})
|
|
240
|
+
|
|
241
|
+
let batch = aci.createOperationsBatch()
|
|
242
|
+
|
|
243
|
+
batch = await aci.updateAccountStateBatch({
|
|
244
|
+
assetName: asset.baseAsset.name,
|
|
245
|
+
walletAccount,
|
|
246
|
+
newData,
|
|
247
|
+
batch,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
Object.entries(txLogsByAssetName).forEach(([assetName, txs]) => {
|
|
251
|
+
batch = aci.updateTxLogAndNotifyBatch({ assetName, walletAccount, txs, batch })
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
await aci.executeOperationsBatch(batch)
|
|
255
|
+
|
|
254
256
|
return { txId: result.digest }
|
|
255
257
|
}
|
|
256
258
|
}
|