@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
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @exodus/sui-api · [](https://www.npmjs.com/package/@exodus/sui-api)
|
|
2
|
+
|
|
3
|
+
The **sui-api** provides utilities for interacting with the Sui blockchain.
|
|
4
|
+
It offers functionality to query transaction data, monitor network health, and broadcast transactions
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Install the package via `yarn`:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
yarn add @exodus/sui-api
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## License
|
|
17
|
+
|
|
18
|
+
This project is licensed under the MIT License.
|
|
19
|
+
You are free to use, modify, and distribute this software under the terms of the MIT License.
|
|
20
|
+
For more details, see the [LICENSE](LICENSE) file.
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@exodus/sui-api",
|
|
3
|
+
"packageManager": "yarn@4.3.1",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Transaction monitors, RPC with the blockchain node, and other networking code for Sui",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "src/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"src",
|
|
10
|
+
"CHANGELOG.md",
|
|
11
|
+
"package.json",
|
|
12
|
+
"README.md",
|
|
13
|
+
"!src/**/__tests__"
|
|
14
|
+
],
|
|
15
|
+
"author": "Exodus Movement, Inc.",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "run -T exodus-test --jest",
|
|
22
|
+
"lint": "run -T eslintc .",
|
|
23
|
+
"lint:fix": "yarn lint --fix"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@exodus/asset-lib": "^5.0.0",
|
|
27
|
+
"@exodus/assets": "^11.1.2",
|
|
28
|
+
"@exodus/basic-utils": "^4.2.1",
|
|
29
|
+
"@exodus/crypto": "^1.0.0-rc.26",
|
|
30
|
+
"@exodus/currency": "^6.0.1",
|
|
31
|
+
"@exodus/models": "^12.0.1",
|
|
32
|
+
"@exodus/sui-lib": "^1.1.2",
|
|
33
|
+
"@exodus/sui-meta": "^1.1.0",
|
|
34
|
+
"bn.js": "^5.2.1",
|
|
35
|
+
"lodash": "^4.17.21",
|
|
36
|
+
"minimalistic-assert": "^1.0.1",
|
|
37
|
+
"ms": "^2.1.3",
|
|
38
|
+
"url-join": "^4.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@exodus/asset": "^2.0.4",
|
|
42
|
+
"@exodus/assets-testing": "^1.0.0"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/ExodusMovement/assets/tree/master/sui/sui-api",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/ExodusMovement/assets/issues?q=is%3Aissue+is%3Aopen+label%3Asui-api"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
51
|
+
},
|
|
52
|
+
"gitHead": "c942fdd35a045680780f1dc5b096aebc5ca54c81"
|
|
53
|
+
}
|
package/src/api.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { RPC_URL } from '@exodus/sui-lib'
|
|
2
|
+
import assert from 'minimalistic-assert'
|
|
3
|
+
|
|
4
|
+
import { SuiClient } from './vendors/client/index.js'
|
|
5
|
+
|
|
6
|
+
const getTextFromResponse = async (response) => {
|
|
7
|
+
try {
|
|
8
|
+
const responseBody = await response.text()
|
|
9
|
+
return responseBody.slice(0, 100)
|
|
10
|
+
} catch {
|
|
11
|
+
return ''
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const fetchJson = async (url, fetchOptions) => {
|
|
16
|
+
const response = await fetch(url, fetchOptions)
|
|
17
|
+
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`${url} returned ${response.status}: ${
|
|
21
|
+
response.statusText || 'Unknown Status Text'
|
|
22
|
+
}. Body: ${await getTextFromResponse(response)}`
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return response.json()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class SuiApi {
|
|
30
|
+
constructor({ rpcUrl }) {
|
|
31
|
+
this.setServer({ rpcUrl })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setServer({ rpcUrl = RPC_URL }) {
|
|
35
|
+
this.rpcUrl = encodeURI(rpcUrl)
|
|
36
|
+
this.client = new SuiClient({
|
|
37
|
+
url: rpcUrl,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
handleJsonRPCResponse(response) {
|
|
42
|
+
const result = response?.result
|
|
43
|
+
const error = response?.error
|
|
44
|
+
if (error || result === undefined) {
|
|
45
|
+
const message = error?.message || error?.code || 'no result'
|
|
46
|
+
throw new Error(`Bad rpc response: ${message}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async request({ baseApiPath, path = '', method, body, headers = Object.create(null) }) {
|
|
53
|
+
assert(typeof baseApiPath === 'string', 'expected string baseApiPath')
|
|
54
|
+
|
|
55
|
+
const url = new URL(`${baseApiPath}${path}`)
|
|
56
|
+
const fetchOptions = {
|
|
57
|
+
method,
|
|
58
|
+
headers: { ...this.getDefaultHeaders(), ...headers },
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (body) fetchOptions.body = JSON.stringify(body)
|
|
62
|
+
const response = await fetchJson(url, fetchOptions)
|
|
63
|
+
return this.handleJsonRPCResponse(response)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getDefaultHeaders() {
|
|
67
|
+
return {
|
|
68
|
+
Accept: 'application/json',
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Transactions
|
|
74
|
+
|
|
75
|
+
async getTransactionByHash({ hash }) {
|
|
76
|
+
return this.request({
|
|
77
|
+
baseApiPath: this.rpcUrl,
|
|
78
|
+
method: 'POST',
|
|
79
|
+
body: {
|
|
80
|
+
jsonrpc: '2.0',
|
|
81
|
+
id: 1,
|
|
82
|
+
method: 'sui_getTransactionBlock',
|
|
83
|
+
params: [
|
|
84
|
+
hash,
|
|
85
|
+
{
|
|
86
|
+
showInput: true,
|
|
87
|
+
showRawInput: false,
|
|
88
|
+
showEffects: true,
|
|
89
|
+
showEvents: false,
|
|
90
|
+
showObjectChanges: true,
|
|
91
|
+
showBalanceChanges: true,
|
|
92
|
+
showRawEffects: false,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async getAccountBalance({ address }) {
|
|
100
|
+
return this.request({
|
|
101
|
+
baseApiPath: this.rpcUrl,
|
|
102
|
+
method: 'POST',
|
|
103
|
+
body: {
|
|
104
|
+
jsonrpc: '2.0',
|
|
105
|
+
id: 1,
|
|
106
|
+
method: 'suix_getBalance',
|
|
107
|
+
params: [address],
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async getAccountAllBalances({ address }) {
|
|
113
|
+
return this.request({
|
|
114
|
+
baseApiPath: this.rpcUrl,
|
|
115
|
+
method: 'POST',
|
|
116
|
+
body: {
|
|
117
|
+
jsonrpc: '2.0',
|
|
118
|
+
id: 1,
|
|
119
|
+
method: 'suix_getAllBalances',
|
|
120
|
+
params: [address],
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async getTransactions({ txIds }) {
|
|
126
|
+
return this.client.multiGetTransactionBlocks({
|
|
127
|
+
digests: txIds,
|
|
128
|
+
options: {
|
|
129
|
+
showEffects: true,
|
|
130
|
+
showObjectChanges: true,
|
|
131
|
+
showBalanceChanges: true,
|
|
132
|
+
showInput: true,
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async getAllCoins({ owner }) {
|
|
138
|
+
const requestByCursor = async ({ cursor }) => {
|
|
139
|
+
return this.client.getAllCoins({
|
|
140
|
+
owner,
|
|
141
|
+
cursor,
|
|
142
|
+
limit: 100,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const coins = []
|
|
147
|
+
let cursor = null
|
|
148
|
+
|
|
149
|
+
while (true) {
|
|
150
|
+
const response = await requestByCursor({ cursor })
|
|
151
|
+
coins.push(...response.data)
|
|
152
|
+
if (!response.nextCursor || !response.hasNextPage) {
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
cursor = response.nextCursor
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return coins
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async getCoins({ owner, coinType }) {
|
|
163
|
+
const requestByCursor = async ({ cursor }) => {
|
|
164
|
+
return this.client.getCoins({
|
|
165
|
+
owner,
|
|
166
|
+
coinType,
|
|
167
|
+
cursor,
|
|
168
|
+
limit: 100,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const coins = []
|
|
173
|
+
let cursor = null
|
|
174
|
+
while (true) {
|
|
175
|
+
const response = await requestByCursor({ cursor })
|
|
176
|
+
coins.push(...response.data)
|
|
177
|
+
if (!response.nextCursor || !response.hasNextPage) {
|
|
178
|
+
break
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
cursor = response.nextCursor
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return coins
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async broadcastTx(plainTx) {
|
|
188
|
+
return this.client.executeTransactionBlock({
|
|
189
|
+
transactionBlock: plainTx.bytes,
|
|
190
|
+
signature: plainTx.signature,
|
|
191
|
+
options: {
|
|
192
|
+
showEffects: true,
|
|
193
|
+
showObjectChanges: true,
|
|
194
|
+
showBalanceChanges: true,
|
|
195
|
+
},
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
}
|
package/src/balances.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const getBalances = ({ asset, accountState }) => {
|
|
2
|
+
const isBaseAsset = asset.name === asset.baseAssetName
|
|
3
|
+
|
|
4
|
+
if (isBaseAsset) {
|
|
5
|
+
return {
|
|
6
|
+
total: accountState.utxos.value,
|
|
7
|
+
spendable: accountState.utxos.value,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (accountState.tokenUtxos[asset.name]) {
|
|
12
|
+
return {
|
|
13
|
+
total: accountState.tokenUtxos[asset.name].value,
|
|
14
|
+
spendable: accountState.tokenUtxos[asset.name].value,
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
total: asset.currency.ZERO,
|
|
20
|
+
spendable: asset.currency.ZERO,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AccountState, UtxoCollection } from '@exodus/models'
|
|
2
|
+
import { asset } from '@exodus/sui-meta'
|
|
3
|
+
|
|
4
|
+
export const createBasicAccountState = () => {
|
|
5
|
+
return class SuiAccountState extends AccountState {
|
|
6
|
+
static defaults = {
|
|
7
|
+
utxos: UtxoCollection.createEmpty({ currency: asset.currency }),
|
|
8
|
+
tokenUtxos: Object.create(null),
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { UtxoCollection } from '@exodus/models'
|
|
2
|
+
import assert from 'minimalistic-assert'
|
|
3
|
+
|
|
4
|
+
import { Transaction } from './vendors/transactions/index.js'
|
|
5
|
+
|
|
6
|
+
export const createUnsignedTxFactory = ({ assetClientInterface, serverApi }) => {
|
|
7
|
+
assert(assetClientInterface, 'assetClientInterface is required')
|
|
8
|
+
return async ({
|
|
9
|
+
asset,
|
|
10
|
+
senderAddress,
|
|
11
|
+
recipientAddress,
|
|
12
|
+
amount,
|
|
13
|
+
gasBudget,
|
|
14
|
+
walletAccount,
|
|
15
|
+
buildOptions = Object.create(null),
|
|
16
|
+
}) => {
|
|
17
|
+
const accountState = await assetClientInterface.getAccountState({
|
|
18
|
+
assetName: asset.baseAssetName,
|
|
19
|
+
walletAccount,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const txb = new Transaction()
|
|
23
|
+
|
|
24
|
+
txb.setGasBudget(BigInt(gasBudget))
|
|
25
|
+
|
|
26
|
+
if (asset.name === asset.baseAssetName) {
|
|
27
|
+
txb.transferObjects([txb.splitCoins(txb.gas, [amount])], txb.pure.address(recipientAddress))
|
|
28
|
+
txb.setSender(senderAddress)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
txData: await txb.build({ client: serverApi.client, ...buildOptions }),
|
|
32
|
+
txMeta: Object.create(null),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const usedCoins = (
|
|
37
|
+
accountState.tokenUtxos[asset.name] ||
|
|
38
|
+
UtxoCollection.createEmpty({ currency: asset.currency })
|
|
39
|
+
).toArray()
|
|
40
|
+
|
|
41
|
+
usedCoins.reverse()
|
|
42
|
+
|
|
43
|
+
const coinObjectIds = usedCoins.map((coin) => coin.coinObjectId)
|
|
44
|
+
|
|
45
|
+
const [inputCoin, ...rest] = coinObjectIds
|
|
46
|
+
if (rest.length > 0) {
|
|
47
|
+
txb.mergeCoins(inputCoin, rest)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const [coin] = txb.splitCoins(inputCoin, [amount])
|
|
51
|
+
|
|
52
|
+
txb.transferObjects([coin], txb.pure.address(recipientAddress))
|
|
53
|
+
txb.setSender(senderAddress)
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
txData: await txb.build({ client: serverApi.client, ...buildOptions }),
|
|
57
|
+
txMeta: Object.create(null),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MIN_GAS_BUDGET } from '@exodus/sui-lib'
|
|
2
|
+
import assert from 'minimalistic-assert'
|
|
3
|
+
|
|
4
|
+
import { getFeeAmount, getGasBudget } from './transform-tx.js'
|
|
5
|
+
|
|
6
|
+
export const getFeeAsyncFactory = ({ assetClientInterface, serverApi, createUnsignedTx }) => {
|
|
7
|
+
assert(assetClientInterface, 'assetClientInterface is required')
|
|
8
|
+
|
|
9
|
+
return async ({
|
|
10
|
+
asset,
|
|
11
|
+
walletAccount,
|
|
12
|
+
toAddress: recipientAddress,
|
|
13
|
+
fromAddress: senderAddress,
|
|
14
|
+
amount,
|
|
15
|
+
}) => {
|
|
16
|
+
const feeAsset = asset.feeAsset
|
|
17
|
+
|
|
18
|
+
const { txData } = await createUnsignedTx({
|
|
19
|
+
asset,
|
|
20
|
+
senderAddress,
|
|
21
|
+
walletAccount,
|
|
22
|
+
recipientAddress:
|
|
23
|
+
recipientAddress || '0x7a2ddb6af66beb0d9987c6c9010cb9053454f067e16775a8ecf19961195c3d28',
|
|
24
|
+
amount: amount.toBaseString(),
|
|
25
|
+
serverApi,
|
|
26
|
+
gasBudget: MIN_GAS_BUDGET,
|
|
27
|
+
buildOptions: {
|
|
28
|
+
onlyTransactionKind: true,
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const simulation = await serverApi.client.devInspectTransactionBlock({
|
|
33
|
+
sender: senderAddress,
|
|
34
|
+
transactionBlock: txData,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const { gasUsed } = simulation.effects
|
|
38
|
+
|
|
39
|
+
assert(
|
|
40
|
+
gasUsed?.computationCost && gasUsed?.storageCost && gasUsed?.storageRebate,
|
|
41
|
+
'Invalid simulation result'
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
let fee = getFeeAmount({
|
|
45
|
+
feeAsset,
|
|
46
|
+
gasUsed,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const gasBudget = getGasBudget({ gasUsed, feeAsset })
|
|
50
|
+
|
|
51
|
+
if (fee.lt(asset.feeAsset.currency.baseUnit(MIN_GAS_BUDGET))) {
|
|
52
|
+
// sometimes we can receive a negative fee because of storage rebate
|
|
53
|
+
// so we use the gas budget instead
|
|
54
|
+
fee = gasBudget
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
fee,
|
|
59
|
+
gasBudget: gasBudget.toNumber(),
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/get-fee.js
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { SuiApi } from './api.js'
|
|
2
|
+
|
|
3
|
+
export { getFeeFactory } from './get-fee.js'
|
|
4
|
+
export { createBasicAccountState } from './basic-account-state.js'
|
|
5
|
+
export { getBalances } from './balances.js'
|
|
6
|
+
export { createSuiNoHistoryMonitor } from './sui-no-history-monitor.js'
|
|
7
|
+
export { getFeeAsyncFactory } from './get-fee-async.js'
|
|
8
|
+
export { txSendFactory } from './tx-send.js'
|
|
9
|
+
export { createUnsignedTxFactory } from './create-unsigned-tx.js'
|