@exodus/sui-api 1.3.1 → 1.4.1
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 +18 -0
- package/package.json +5 -9
- package/src/api.js +402 -151
- package/src/balances.js +10 -10
- package/src/basic-account-state.js +1 -0
- package/src/broadcast-error.js +8 -0
- package/src/create-unsigned-tx.js +2 -2
- package/src/get-fee-async.js +1 -1
- package/src/graphql/graphql-client.js +82 -0
- package/src/graphql/json-rpc-shapes.js +342 -0
- package/src/graphql/queries.js +166 -0
- package/src/index.js +1 -0
- package/src/staking/delegated-stakes.js +66 -0
- package/src/staking/staking-transactions.js +10 -1
- package/src/sui-no-history-monitor.js +32 -19
- package/src/tx-send.js +51 -2
- package/src/vendors/transactions/resolve.js +2 -3
- package/src/vendors/{experimental/transports/json-rpc-resolver.js → transactions/resolver.js} +18 -56
- package/src/vendors/client/client.js +0 -680
- package/src/vendors/client/errors.js +0 -38
- package/src/vendors/client/http-transport.js +0 -71
- package/src/vendors/client/index.js +0 -1
- package/src/vendors/experimental/cache.js +0 -90
- package/src/vendors/experimental/client.js +0 -27
- package/src/vendors/experimental/core.js +0 -11
- package/src/vendors/experimental/transports/jsonRPC.js +0 -106
package/src/balances.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { getStakingBalances } from './staking/staking-balances.js'
|
|
2
2
|
|
|
3
|
+
const getAddressBalance = ({ asset, accountState }) =>
|
|
4
|
+
asset.currency.baseUnit(accountState.addressBalances[asset.name] ?? 0)
|
|
5
|
+
|
|
3
6
|
export const getBalances = ({ asset, accountState }) => {
|
|
4
7
|
const isBaseAsset = asset.name === asset.baseAssetName
|
|
8
|
+
const addressBalance = getAddressBalance({ asset, accountState })
|
|
5
9
|
|
|
6
10
|
if (isBaseAsset) {
|
|
7
11
|
const spendable = accountState.utxos.value
|
|
8
12
|
const stakingBalances = getStakingBalances({ asset, stakingInfo: accountState.stakingInfo })
|
|
9
13
|
const { staking, staked, unstaked } = stakingBalances
|
|
10
14
|
return {
|
|
11
|
-
total: spendable.add(staking).add(staked).add(unstaked),
|
|
15
|
+
total: spendable.add(addressBalance).add(staking).add(staked).add(unstaked),
|
|
12
16
|
spendable,
|
|
13
17
|
stakeable: spendable,
|
|
18
|
+
unspendable: addressBalance,
|
|
14
19
|
...stakingBalances,
|
|
15
20
|
}
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
total: accountState.tokenUtxos[asset.name].value,
|
|
21
|
-
spendable: accountState.tokenUtxos[asset.name].value,
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
23
|
+
const spendable = accountState.tokenUtxos[asset.name]?.value ?? asset.currency.ZERO
|
|
25
24
|
return {
|
|
26
|
-
total:
|
|
27
|
-
spendable
|
|
25
|
+
total: spendable.add(addressBalance),
|
|
26
|
+
spendable,
|
|
27
|
+
unspendable: addressBalance,
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -28,7 +28,7 @@ export const createUnsignedTxFactory = ({ assetClientInterface, serverApi }) =>
|
|
|
28
28
|
txb.setSender(senderAddress)
|
|
29
29
|
|
|
30
30
|
return {
|
|
31
|
-
txData: await
|
|
31
|
+
txData: await serverApi.buildTransaction({ transaction: txb, ...buildOptions }),
|
|
32
32
|
txMeta: Object.create(null),
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -53,7 +53,7 @@ export const createUnsignedTxFactory = ({ assetClientInterface, serverApi }) =>
|
|
|
53
53
|
txb.setSender(senderAddress)
|
|
54
54
|
|
|
55
55
|
return {
|
|
56
|
-
txData: await
|
|
56
|
+
txData: await serverApi.buildTransaction({ transaction: txb, ...buildOptions }),
|
|
57
57
|
txMeta: Object.create(null),
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/get-fee-async.js
CHANGED
|
@@ -27,7 +27,7 @@ export const getFeeAsyncFactory = ({ assetClientInterface, serverApi, createUnsi
|
|
|
27
27
|
},
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
const simulation = await serverApi.
|
|
30
|
+
const simulation = await serverApi.devInspectTransaction({
|
|
31
31
|
sender: senderAddress,
|
|
32
32
|
transactionBlock: txData,
|
|
33
33
|
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import ms from 'ms'
|
|
2
|
+
|
|
3
|
+
const MAX_ERROR_BODY_LENGTH = 100
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = ms('30s')
|
|
5
|
+
|
|
6
|
+
export class GraphQLTimeoutError extends Error {
|
|
7
|
+
constructor({ url, timeoutMs }) {
|
|
8
|
+
super(`${url} timed out after ${timeoutMs}ms`)
|
|
9
|
+
this.name = 'GraphQLTimeoutError'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class GraphQLTransportError extends Error {
|
|
14
|
+
constructor(message, { cause } = Object.create(null)) {
|
|
15
|
+
super(message, { cause })
|
|
16
|
+
this.name = 'GraphQLTransportError'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const SERVER_ERROR_STATUS = 500
|
|
21
|
+
|
|
22
|
+
const readErrorBody = async (response) => {
|
|
23
|
+
try {
|
|
24
|
+
const text = await response.text()
|
|
25
|
+
return text.slice(0, MAX_ERROR_BODY_LENGTH)
|
|
26
|
+
} catch {
|
|
27
|
+
return ''
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class GraphQLClient {
|
|
32
|
+
#url
|
|
33
|
+
#timeoutMs
|
|
34
|
+
|
|
35
|
+
constructor({ url, timeoutMs = DEFAULT_TIMEOUT_MS }) {
|
|
36
|
+
this.#url = url
|
|
37
|
+
this.#timeoutMs = timeoutMs
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async #post({ query, variables, timeoutMs }) {
|
|
41
|
+
const controller = new AbortController()
|
|
42
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs)
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetch(this.#url, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
47
|
+
body: JSON.stringify({ query, variables }),
|
|
48
|
+
signal: controller.signal,
|
|
49
|
+
}).catch((error) => {
|
|
50
|
+
if (controller.signal.aborted) throw error
|
|
51
|
+
throw new GraphQLTransportError(`${this.#url} request failed: ${error.message}`, {
|
|
52
|
+
cause: error,
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const message = `${this.#url} returned ${response.status}: ${
|
|
58
|
+
response.statusText || 'Unknown Status Text'
|
|
59
|
+
}. Body: ${await readErrorBody(response)}`
|
|
60
|
+
if (response.status >= SERVER_ERROR_STATUS) throw new GraphQLTransportError(message)
|
|
61
|
+
throw new Error(message)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return await response.json()
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (controller.signal.aborted) throw new GraphQLTimeoutError({ url: this.#url, timeoutMs })
|
|
67
|
+
throw error
|
|
68
|
+
} finally {
|
|
69
|
+
clearTimeout(timeout)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async query({ query, variables = Object.create(null), timeoutMs = this.#timeoutMs }) {
|
|
74
|
+
const { data, errors } = await this.#post({ query, variables, timeoutMs })
|
|
75
|
+
if (errors?.length > 0) {
|
|
76
|
+
throw new Error(`Bad graphql response: ${errors.map(({ message }) => message).join('; ')}`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!data) throw new Error('Bad graphql response: no data')
|
|
80
|
+
return data
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { vendorLib } from '@exodus/sui-lib'
|
|
2
|
+
|
|
3
|
+
import { getPureBcsSchema } from '../vendors/transactions/serializer.js'
|
|
4
|
+
|
|
5
|
+
const { fromBase64, normalizeSuiAddress } = vendorLib.utils
|
|
6
|
+
|
|
7
|
+
const SPECIAL_ADDRESS_REGEX = /0x0{63}([\dA-Fa-f])(?![\dA-Fa-f])/g
|
|
8
|
+
const NO_GAS_OBJECT_ID = `0x${'0'.repeat(64)}`
|
|
9
|
+
|
|
10
|
+
export const toJsonRpcTypeTag = (typeTag) =>
|
|
11
|
+
typeTag.replaceAll(SPECIAL_ADDRESS_REGEX, '0x$1').replaceAll(/,\s*/g, ', ')
|
|
12
|
+
|
|
13
|
+
export const toMoveFunctionKey = ({ package: pkg, module, function: fn }) =>
|
|
14
|
+
`${normalizeSuiAddress(pkg)}::${module}::${fn}`
|
|
15
|
+
|
|
16
|
+
const toOwner = (owner) => {
|
|
17
|
+
switch (owner?.kind) {
|
|
18
|
+
case 'ADDRESS':
|
|
19
|
+
return { AddressOwner: owner.address }
|
|
20
|
+
case 'OBJECT':
|
|
21
|
+
return { ObjectOwner: owner.address }
|
|
22
|
+
case 'SHARED':
|
|
23
|
+
return { Shared: { initial_shared_version: Number(owner.version) } }
|
|
24
|
+
case 'IMMUTABLE':
|
|
25
|
+
return 'Immutable'
|
|
26
|
+
case 'CONSENSUS_ADDRESS':
|
|
27
|
+
return {
|
|
28
|
+
ConsensusAddressOwner: { owner: owner.address, start_version: Number(owner.version) },
|
|
29
|
+
}
|
|
30
|
+
default:
|
|
31
|
+
return { Unknown: owner?.kind ?? null }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const toArgument = (argument) => {
|
|
36
|
+
switch (argument?.kind) {
|
|
37
|
+
case 'GAS':
|
|
38
|
+
return 'GasCoin'
|
|
39
|
+
case 'INPUT':
|
|
40
|
+
return { Input: argument.input }
|
|
41
|
+
case 'RESULT':
|
|
42
|
+
return argument.subresult === undefined
|
|
43
|
+
? { Result: argument.result }
|
|
44
|
+
: { NestedResult: [argument.result, argument.subresult] }
|
|
45
|
+
default:
|
|
46
|
+
return { Unknown: argument?.kind ?? null }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const toCommand = (command) => {
|
|
51
|
+
if (command.moveCall) {
|
|
52
|
+
const { package: pkg, module, function: fn, typeArguments, arguments: args } = command.moveCall
|
|
53
|
+
return {
|
|
54
|
+
MoveCall: {
|
|
55
|
+
package: pkg,
|
|
56
|
+
module,
|
|
57
|
+
function: fn,
|
|
58
|
+
...(typeArguments?.length > 0 && { type_arguments: typeArguments.map(toJsonRpcTypeTag) }),
|
|
59
|
+
arguments: (args ?? []).map(toArgument),
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (command.splitCoins) {
|
|
65
|
+
const { coin, amounts = [] } = command.splitCoins
|
|
66
|
+
return { SplitCoins: [toArgument(coin), amounts.map(toArgument)] }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (command.transferObjects) {
|
|
70
|
+
const { objects = [], address } = command.transferObjects
|
|
71
|
+
return { TransferObjects: [objects.map(toArgument), toArgument(address)] }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (command.mergeCoins) {
|
|
75
|
+
const { coin, coinsToMerge = [] } = command.mergeCoins
|
|
76
|
+
return { MergeCoins: [toArgument(coin), coinsToMerge.map(toArgument)] }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (command.makeMoveVector) {
|
|
80
|
+
const { elementType, elements = [] } = command.makeMoveVector
|
|
81
|
+
return {
|
|
82
|
+
MakeMoveVec: [elementType ? toJsonRpcTypeTag(elementType) : null, elements.map(toArgument)],
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (command.publish) {
|
|
87
|
+
return { Publish: command.publish.dependencies ?? [] }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (command.upgrade) {
|
|
91
|
+
const { dependencies, package: pkg, ticket } = command.upgrade
|
|
92
|
+
return { Upgrade: [dependencies ?? [], pkg, toArgument(ticket)] }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { Unknown: Object.keys(command) }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const getInputTypes = ({ commands, moveFunctionParameters }) => {
|
|
99
|
+
const types = new Map()
|
|
100
|
+
const setType = (argument, type) => {
|
|
101
|
+
if (argument?.kind === 'INPUT' && !types.has(argument.input)) types.set(argument.input, type)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const command of commands) {
|
|
105
|
+
if (command.splitCoins) {
|
|
106
|
+
;(command.splitCoins.amounts ?? []).forEach((amount) => setType(amount, 'u64'))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (command.transferObjects) setType(command.transferObjects.address, 'address')
|
|
110
|
+
if (command.moveCall) {
|
|
111
|
+
const parameters = moveFunctionParameters.get(toMoveFunctionKey(command.moveCall)) ?? []
|
|
112
|
+
command.moveCall.arguments?.forEach((argument, index) =>
|
|
113
|
+
setType(argument, parameters[index]?.body)
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return types
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const toRawPureInput = (bytes) => ({ type: 'pure', valueType: null, value: [...bytes] })
|
|
122
|
+
|
|
123
|
+
const toPureInput = ({ bytes, type }) => {
|
|
124
|
+
if (typeof type !== 'string') return toRawPureInput(bytes)
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
return { type: 'pure', valueType: type, value: getPureBcsSchema(type).parse(bytes) }
|
|
128
|
+
} catch {
|
|
129
|
+
return toRawPureInput(bytes)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const toInput = ({ input, type }) => {
|
|
134
|
+
switch (input.kind) {
|
|
135
|
+
case 'PURE':
|
|
136
|
+
return toPureInput({ bytes: fromBase64(input.pure), type })
|
|
137
|
+
case 'IMMUTABLE_OR_OWNED':
|
|
138
|
+
return {
|
|
139
|
+
type: 'object',
|
|
140
|
+
objectType: 'immOrOwnedObject',
|
|
141
|
+
objectId: input.objectId,
|
|
142
|
+
version: input.version,
|
|
143
|
+
digest: input.digest,
|
|
144
|
+
}
|
|
145
|
+
case 'SHARED':
|
|
146
|
+
return {
|
|
147
|
+
type: 'object',
|
|
148
|
+
objectType: 'sharedObject',
|
|
149
|
+
objectId: input.objectId,
|
|
150
|
+
initialSharedVersion: input.version,
|
|
151
|
+
mutable: input.mutable,
|
|
152
|
+
}
|
|
153
|
+
case 'RECEIVING':
|
|
154
|
+
return {
|
|
155
|
+
type: 'object',
|
|
156
|
+
objectType: 'receiving',
|
|
157
|
+
objectId: input.objectId,
|
|
158
|
+
version: input.version,
|
|
159
|
+
digest: input.digest,
|
|
160
|
+
}
|
|
161
|
+
default:
|
|
162
|
+
return { type: 'unknown', kind: input.kind }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const toTransactionKind = ({ kind, moveFunctionParameters }) => {
|
|
167
|
+
if (kind.kind !== 'PROGRAMMABLE_TRANSACTION') return { kind: kind.kind }
|
|
168
|
+
|
|
169
|
+
const { inputs = [], commands = [] } = kind.programmableTransaction
|
|
170
|
+
const inputTypes = getInputTypes({ commands, moveFunctionParameters })
|
|
171
|
+
return {
|
|
172
|
+
kind: 'ProgrammableTransaction',
|
|
173
|
+
inputs: inputs.map((input, index) => toInput({ input, type: inputTypes.get(index) })),
|
|
174
|
+
transactions: commands.map(toCommand),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const getMoveCalls = (transactionJson) =>
|
|
179
|
+
(transactionJson?.kind?.programmableTransaction?.commands ?? [])
|
|
180
|
+
.map((command) => command.moveCall)
|
|
181
|
+
.filter(Boolean)
|
|
182
|
+
|
|
183
|
+
const toTransactionData = ({ transactionJson, moveFunctionParameters }) => {
|
|
184
|
+
const { sender, gasPayment, kind } = transactionJson
|
|
185
|
+
return {
|
|
186
|
+
messageVersion: 'v1',
|
|
187
|
+
transaction: toTransactionKind({ kind, moveFunctionParameters }),
|
|
188
|
+
sender,
|
|
189
|
+
gasData: {
|
|
190
|
+
payment: (gasPayment.objects ?? []).map(({ objectId, version, digest }) => ({
|
|
191
|
+
objectId,
|
|
192
|
+
version: Number(version),
|
|
193
|
+
digest,
|
|
194
|
+
})),
|
|
195
|
+
owner: gasPayment.owner,
|
|
196
|
+
price: gasPayment.price,
|
|
197
|
+
budget: gasPayment.budget,
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const toStatus = ({ success, error }) =>
|
|
203
|
+
success
|
|
204
|
+
? { status: 'success' }
|
|
205
|
+
: { status: 'failure', error: error?.description ?? error?.kind ?? 'unknown' }
|
|
206
|
+
|
|
207
|
+
const toGasObject = (gasObject) => {
|
|
208
|
+
if (!gasObject) {
|
|
209
|
+
return {
|
|
210
|
+
owner: { AddressOwner: NO_GAS_OBJECT_ID },
|
|
211
|
+
reference: {
|
|
212
|
+
objectId: NO_GAS_OBJECT_ID,
|
|
213
|
+
version: 0,
|
|
214
|
+
digest: '11111111111111111111111111111111',
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
owner: toOwner(gasObject.outputOwner),
|
|
221
|
+
reference: {
|
|
222
|
+
objectId: gasObject.objectId,
|
|
223
|
+
version: Number(gasObject.outputVersion),
|
|
224
|
+
digest: gasObject.outputDigest,
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const toEffects = (effectsJson) => ({
|
|
230
|
+
messageVersion: 'v1',
|
|
231
|
+
status: toStatus(effectsJson.status),
|
|
232
|
+
executedEpoch: effectsJson.epoch,
|
|
233
|
+
gasUsed: effectsJson.gasUsed,
|
|
234
|
+
transactionDigest: effectsJson.transactionDigest,
|
|
235
|
+
gasObject: toGasObject(effectsJson.gasObject),
|
|
236
|
+
dependencies: effectsJson.dependencies ?? [],
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
const toObjectChange = ({ change, sender, lamportVersion }) => {
|
|
240
|
+
const { objectId, inputState, outputState, idOperation } = change
|
|
241
|
+
const objectType = change.objectType && toJsonRpcTypeTag(change.objectType)
|
|
242
|
+
const inputExists = inputState === 'INPUT_OBJECT_STATE_EXISTS'
|
|
243
|
+
|
|
244
|
+
if (outputState === 'OUTPUT_OBJECT_STATE_ACCUMULATOR_WRITE') return
|
|
245
|
+
|
|
246
|
+
if (outputState === 'OUTPUT_OBJECT_STATE_PACKAGE_WRITE') {
|
|
247
|
+
return {
|
|
248
|
+
type: 'published',
|
|
249
|
+
packageId: objectId,
|
|
250
|
+
version: change.outputVersion,
|
|
251
|
+
digest: change.outputDigest,
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (
|
|
256
|
+
idOperation === 'DELETED' ||
|
|
257
|
+
(inputExists && outputState !== 'OUTPUT_OBJECT_STATE_OBJECT_WRITE')
|
|
258
|
+
) {
|
|
259
|
+
return {
|
|
260
|
+
type: idOperation === 'DELETED' ? 'deleted' : 'wrapped',
|
|
261
|
+
sender,
|
|
262
|
+
objectType,
|
|
263
|
+
objectId,
|
|
264
|
+
version: lamportVersion,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const written = {
|
|
269
|
+
sender,
|
|
270
|
+
owner: toOwner(change.outputOwner),
|
|
271
|
+
objectType,
|
|
272
|
+
objectId,
|
|
273
|
+
version: change.outputVersion,
|
|
274
|
+
...(inputExists && { previousVersion: change.inputVersion }),
|
|
275
|
+
digest: change.outputDigest,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return { type: inputExists ? 'mutated' : 'created', ...written }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const toObjectChanges = ({ effectsJson, sender }) =>
|
|
282
|
+
(effectsJson.changedObjects ?? [])
|
|
283
|
+
.map((change) => toObjectChange({ change, sender, lamportVersion: effectsJson.lamportVersion }))
|
|
284
|
+
.filter(Boolean)
|
|
285
|
+
|
|
286
|
+
const toBalanceChanges = (balanceChangesJson) =>
|
|
287
|
+
(balanceChangesJson ?? []).map(({ address, coinType, amount }) => ({
|
|
288
|
+
owner: { AddressOwner: address },
|
|
289
|
+
coinType: toJsonRpcTypeTag(coinType),
|
|
290
|
+
amount,
|
|
291
|
+
}))
|
|
292
|
+
|
|
293
|
+
export const toTransactionBlockResponse = ({
|
|
294
|
+
digest,
|
|
295
|
+
transactionJson,
|
|
296
|
+
effects,
|
|
297
|
+
moveFunctionParameters,
|
|
298
|
+
}) => {
|
|
299
|
+
const { effectsJson, balanceChangesJson, timestamp, checkpoint } = effects
|
|
300
|
+
return {
|
|
301
|
+
digest,
|
|
302
|
+
transaction: { data: toTransactionData({ transactionJson, moveFunctionParameters }) },
|
|
303
|
+
effects: toEffects(effectsJson),
|
|
304
|
+
objectChanges: toObjectChanges({ effectsJson, sender: transactionJson.sender }),
|
|
305
|
+
balanceChanges: toBalanceChanges(balanceChangesJson),
|
|
306
|
+
...(timestamp && { timestampMs: String(Date.parse(timestamp)) }),
|
|
307
|
+
...(checkpoint && { checkpoint: String(checkpoint.sequenceNumber) }),
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export const toCoin = (node) => {
|
|
312
|
+
const coinObjectType = toJsonRpcTypeTag(node.contents.type.repr)
|
|
313
|
+
const [, coinType] = coinObjectType.match(/^0x2::coin::Coin<(.+)>$/) ?? []
|
|
314
|
+
if (!coinType) throw new Error(`Unexpected sui coin type ${coinObjectType}`)
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
coinType,
|
|
318
|
+
coinObjectId: node.address,
|
|
319
|
+
version: String(node.version),
|
|
320
|
+
digest: node.digest,
|
|
321
|
+
balance: node.contents.json.balance,
|
|
322
|
+
previousTransaction: node.previousTransaction?.digest,
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const getInitialSharedVersion = (owner) => {
|
|
327
|
+
if (owner?.__typename === 'Shared') return String(owner.initialSharedVersion)
|
|
328
|
+
if (owner?.__typename === 'ConsensusAddressOwner') return String(owner.startVersion)
|
|
329
|
+
return null
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export const toObjectRef = (object) => ({
|
|
333
|
+
objectId: object.address,
|
|
334
|
+
digest: object.digest,
|
|
335
|
+
version: String(object.version),
|
|
336
|
+
initialSharedVersion: getInitialSharedVersion(object.owner),
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
export const toSimulationEffects = (effectsJson) => ({
|
|
340
|
+
status: toStatus(effectsJson.status),
|
|
341
|
+
gasUsed: effectsJson.gasUsed,
|
|
342
|
+
})
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const TRANSACTION_EFFECTS_FIELDS = `
|
|
2
|
+
digest
|
|
3
|
+
timestamp
|
|
4
|
+
checkpoint { sequenceNumber }
|
|
5
|
+
effectsJson
|
|
6
|
+
balanceChangesJson
|
|
7
|
+
`
|
|
8
|
+
|
|
9
|
+
export const MULTI_GET_TRANSACTIONS = `
|
|
10
|
+
query ($digests: [String!]!) {
|
|
11
|
+
multiGetTransactions(keys: $digests) {
|
|
12
|
+
digest
|
|
13
|
+
transactionJson
|
|
14
|
+
effects { ${TRANSACTION_EFFECTS_FIELDS} }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
export const EXECUTE_TRANSACTION = `
|
|
20
|
+
mutation ($transactionDataBcs: Base64!, $signatures: [Base64!]!) {
|
|
21
|
+
executeTransaction(transactionDataBcs: $transactionDataBcs, signatures: $signatures) {
|
|
22
|
+
effects {
|
|
23
|
+
${TRANSACTION_EFFECTS_FIELDS}
|
|
24
|
+
transaction { digest transactionJson }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
export const SIMULATE_TRANSACTION = `
|
|
31
|
+
query ($transaction: JSON!, $checksEnabled: Boolean) {
|
|
32
|
+
simulateTransaction(transaction: $transaction, checksEnabled: $checksEnabled) {
|
|
33
|
+
effects { effectsJson }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
export const OWNED_OBJECTS = `
|
|
39
|
+
query ($owner: SuiAddress!, $type: String!, $cursor: String) {
|
|
40
|
+
address(address: $owner) {
|
|
41
|
+
objects(first: 50, after: $cursor, filter: { type: $type }) {
|
|
42
|
+
pageInfo { hasNextPage endCursor }
|
|
43
|
+
nodes {
|
|
44
|
+
address
|
|
45
|
+
version
|
|
46
|
+
digest
|
|
47
|
+
previousTransaction { digest }
|
|
48
|
+
contents { type { repr } json }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`
|
|
54
|
+
|
|
55
|
+
export const BALANCE = `
|
|
56
|
+
query ($owner: SuiAddress!, $coinType: String!) {
|
|
57
|
+
address(address: $owner) {
|
|
58
|
+
balance(coinType: $coinType) { coinType { repr } totalBalance }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`
|
|
62
|
+
|
|
63
|
+
export const ALL_BALANCES = `
|
|
64
|
+
query ($owner: SuiAddress!, $cursor: String) {
|
|
65
|
+
address(address: $owner) {
|
|
66
|
+
balances(first: 50, after: $cursor) {
|
|
67
|
+
pageInfo { hasNextPage endCursor }
|
|
68
|
+
nodes { coinType { repr } totalBalance addressBalance }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
`
|
|
73
|
+
|
|
74
|
+
export const MULTI_GET_OBJECTS = `
|
|
75
|
+
query ($keys: [ObjectKey!]!) {
|
|
76
|
+
multiGetObjects(keys: $keys) {
|
|
77
|
+
address
|
|
78
|
+
version
|
|
79
|
+
digest
|
|
80
|
+
owner {
|
|
81
|
+
__typename
|
|
82
|
+
... on Shared { initialSharedVersion }
|
|
83
|
+
... on ConsensusAddressOwner { startVersion }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`
|
|
88
|
+
|
|
89
|
+
export const MOVE_FUNCTION_PARAMETERS = `
|
|
90
|
+
query ($package: SuiAddress!, $module: String!, $function: String!) {
|
|
91
|
+
package(address: $package) {
|
|
92
|
+
module(name: $module) {
|
|
93
|
+
function(name: $function) { parameters { signature } }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
`
|
|
98
|
+
|
|
99
|
+
export const LATEST_EPOCH = `
|
|
100
|
+
query {
|
|
101
|
+
epoch {
|
|
102
|
+
epochId
|
|
103
|
+
referenceGasPrice
|
|
104
|
+
systemState {
|
|
105
|
+
startMs: extract(path: "epoch_start_timestamp_ms") { json }
|
|
106
|
+
durationMs: extract(path: "parameters.epoch_duration_ms") { json }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`
|
|
111
|
+
|
|
112
|
+
export const ACTIVE_VALIDATOR_POOLS = `
|
|
113
|
+
query ($cursor: String) {
|
|
114
|
+
epoch {
|
|
115
|
+
epochId
|
|
116
|
+
validatorSet {
|
|
117
|
+
inactivePoolsId: contents { extract(path: "inactive_validators.id") { json } }
|
|
118
|
+
activeValidators(first: 50, after: $cursor) {
|
|
119
|
+
pageInfo { hasNextPage endCursor }
|
|
120
|
+
nodes {
|
|
121
|
+
contents {
|
|
122
|
+
validatorAddress: extract(path: "metadata.sui_address") { json }
|
|
123
|
+
stakingPool: extract(path: "staking_pool") { json }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`
|
|
131
|
+
|
|
132
|
+
export const INACTIVE_VALIDATOR_WRAPPER = `
|
|
133
|
+
query ($table: SuiAddress!, $poolId: Base64!) {
|
|
134
|
+
address(address: $table) {
|
|
135
|
+
dynamicField(name: { type: "0x2::object::ID", bcs: $poolId }) {
|
|
136
|
+
value { ... on MoveValue { json } }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
`
|
|
141
|
+
|
|
142
|
+
export const VERSIONED_VALIDATOR = `
|
|
143
|
+
query ($versioned: SuiAddress!, $version: String!) {
|
|
144
|
+
address(address: $versioned) {
|
|
145
|
+
dynamicField(name: { literal: $version }) {
|
|
146
|
+
value {
|
|
147
|
+
... on MoveValue {
|
|
148
|
+
validatorAddress: extract(path: "metadata.sui_address") { json }
|
|
149
|
+
stakingPool: extract(path: "staking_pool") { json }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`
|
|
156
|
+
|
|
157
|
+
export const EXCHANGE_RATES = `
|
|
158
|
+
query ($table: SuiAddress!, $keys: [DynamicFieldName!]!) {
|
|
159
|
+
address(address: $table) {
|
|
160
|
+
multiGetDynamicFields(keys: $keys) {
|
|
161
|
+
name { json }
|
|
162
|
+
value { ... on MoveValue { json } }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
`
|