@exodus/solana-api 1.4.1 → 1.4.3
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/package.json +3 -3
- package/src/index.js +74 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/solana-api",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "Exodus internal Solana asset API wrapper",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@exodus/asset-json-rpc": "^1.0.0",
|
|
17
|
-
"@exodus/solana-lib": "^1.3.
|
|
17
|
+
"@exodus/solana-lib": "^1.3.2",
|
|
18
18
|
"lodash": "^4.17.11",
|
|
19
19
|
"url-join": "4.0.0",
|
|
20
20
|
"wretch": "^1.5.2"
|
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"node-fetch": "~1.6.3"
|
|
24
24
|
},
|
|
25
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "8491bef4e895be6251f46a00f11a9de4e5141735"
|
|
26
26
|
}
|
package/src/index.js
CHANGED
|
@@ -35,7 +35,7 @@ class Api {
|
|
|
35
35
|
|
|
36
36
|
setTokens(assets = {}) {
|
|
37
37
|
const solTokens = lodash.pickBy(assets, (asset) => asset.assetType === 'SOLANA_TOKEN')
|
|
38
|
-
this.tokens = lodash.mapKeys(solTokens, (v) => v.mintAddress
|
|
38
|
+
this.tokens = lodash.mapKeys(solTokens, (v) => v.mintAddress)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
request(path, contentType = 'application/json'): Wretcher {
|
|
@@ -67,7 +67,8 @@ class Api {
|
|
|
67
67
|
const {
|
|
68
68
|
value: { blockhash },
|
|
69
69
|
} = await this.api.post({
|
|
70
|
-
method: '
|
|
70
|
+
method: 'getLatestBlockhash',
|
|
71
|
+
params: [{ commitment : 'confirmed' }],
|
|
71
72
|
})
|
|
72
73
|
return blockhash
|
|
73
74
|
}
|
|
@@ -153,6 +154,18 @@ class Api {
|
|
|
153
154
|
const parsedTx = this.parseTransaction(address, txDetail, tokenAccountsByOwner)
|
|
154
155
|
if (!parsedTx.from) return // cannot parse it
|
|
155
156
|
|
|
157
|
+
// split dexTx in separate txs
|
|
158
|
+
if (parsedTx.dexTxs) {
|
|
159
|
+
parsedTx.dexTxs.forEach((tx) => {
|
|
160
|
+
transactions.push({
|
|
161
|
+
timestamp,
|
|
162
|
+
date: new Date(timestamp),
|
|
163
|
+
...tx,
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
delete parsedTx.dexTxs
|
|
167
|
+
}
|
|
168
|
+
|
|
156
169
|
transactions.push({
|
|
157
170
|
timestamp,
|
|
158
171
|
date: new Date(timestamp),
|
|
@@ -172,7 +185,10 @@ class Api {
|
|
|
172
185
|
}
|
|
173
186
|
|
|
174
187
|
parseTransaction(ownerAddress: string, txDetails: Object, tokenAccountsByOwner: ?Array): Object {
|
|
175
|
-
|
|
188
|
+
let { fee, preTokenBalances, postTokenBalances, innerInstructions } = txDetails.meta
|
|
189
|
+
preTokenBalances = preTokenBalances || []
|
|
190
|
+
postTokenBalances = postTokenBalances || []
|
|
191
|
+
innerInstructions = innerInstructions || []
|
|
176
192
|
let { instructions, accountKeys } = txDetails.transaction.message
|
|
177
193
|
instructions = instructions
|
|
178
194
|
.filter((ix) => ix.parsed) // only known instructions
|
|
@@ -181,6 +197,38 @@ class Api {
|
|
|
181
197
|
type: ix.parsed.type, // transfer, createAccount, initializeAccount
|
|
182
198
|
...ix.parsed.info,
|
|
183
199
|
}))
|
|
200
|
+
innerInstructions = innerInstructions
|
|
201
|
+
.reduce((acc, val) => {
|
|
202
|
+
return acc.concat(val.instructions)
|
|
203
|
+
}, [])
|
|
204
|
+
.map((ix) => {
|
|
205
|
+
const type = lodash.get(ix, 'parsed.type')
|
|
206
|
+
const isTransferTx = ix.parsed && ix.program === 'spl-token' && type === 'transfer'
|
|
207
|
+
const source = lodash.get(ix, 'parsed.info.source')
|
|
208
|
+
const destination = lodash.get(ix, 'parsed.info.destination')
|
|
209
|
+
const amount = Number(lodash.get(ix, 'parsed.info.amount', 0))
|
|
210
|
+
|
|
211
|
+
const tokenAccount = tokenAccountsByOwner.find(({ tokenAccountAddress }) => {
|
|
212
|
+
return [source, destination].includes(tokenAccountAddress)
|
|
213
|
+
})
|
|
214
|
+
const isSending = !!tokenAccountsByOwner.find(({ tokenAccountAddress }) => {
|
|
215
|
+
return [source].includes(tokenAccountAddress)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// owner if it's a send tx
|
|
219
|
+
const instruction = {
|
|
220
|
+
id: txDetails.transaction.signatures[0],
|
|
221
|
+
slot: txDetails.slot,
|
|
222
|
+
owner: isSending ? ownerAddress : null,
|
|
223
|
+
from: source,
|
|
224
|
+
to: destination,
|
|
225
|
+
amount,
|
|
226
|
+
token: tokenAccount,
|
|
227
|
+
fee: isSending ? fee : 0,
|
|
228
|
+
}
|
|
229
|
+
return isTransferTx && tokenAccount ? instruction : null
|
|
230
|
+
})
|
|
231
|
+
.filter((ix) => !!ix)
|
|
184
232
|
|
|
185
233
|
// program:type tells us if it's a SOL or Token transfer
|
|
186
234
|
const solanaTx = lodash.find(instructions, (ix) => {
|
|
@@ -190,9 +238,10 @@ class Api {
|
|
|
190
238
|
const stakeTx = lodash.find(instructions, { program: 'system', type: 'createAccountWithSeed' })
|
|
191
239
|
const stakeWithdraw = lodash.find(instructions, { program: 'stake', type: 'withdraw' })
|
|
192
240
|
const stakeUndelegate = lodash.find(instructions, { program: 'stake', type: 'deactivate' })
|
|
241
|
+
const hasSolanaTx = solanaTx && !preTokenBalances.length && !postTokenBalances.length // only SOL moved and no tokens movements
|
|
193
242
|
|
|
194
243
|
let tx = {}
|
|
195
|
-
if (
|
|
244
|
+
if (hasSolanaTx) {
|
|
196
245
|
// Solana tx
|
|
197
246
|
const isSending = ownerAddress === solanaTx.source
|
|
198
247
|
tx = {
|
|
@@ -316,30 +365,28 @@ class Api {
|
|
|
316
365
|
)
|
|
317
366
|
})
|
|
318
367
|
|
|
319
|
-
if (preBalances.length
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
368
|
+
if (preBalances.length || postBalances.length) {
|
|
369
|
+
tx = {}
|
|
370
|
+
|
|
371
|
+
if (solanaTx) {
|
|
372
|
+
// the base tx will be the one that moved solana.
|
|
373
|
+
tx = {
|
|
374
|
+
owner: solanaTx.source,
|
|
375
|
+
from: solanaTx.source,
|
|
376
|
+
to: solanaTx.destination,
|
|
377
|
+
amount: solanaTx.lamports, // number
|
|
378
|
+
fee: ownerAddress === solanaTx.source ? fee : 0,
|
|
379
|
+
}
|
|
330
380
|
}
|
|
331
381
|
|
|
332
|
-
tx
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
to: ownerAddress, // DEX always interact with the owner address
|
|
341
|
-
amount,
|
|
342
|
-
fee: isSending ? fee : 0, // in lamports
|
|
382
|
+
// If it has inner instructions then it's a DEX tx that moved SPL -> SPL
|
|
383
|
+
if (innerInstructions.length) {
|
|
384
|
+
tx.dexTxs = innerInstructions
|
|
385
|
+
// if tx involves only SPL swaps. Expand DEX ix (first element as tx base and the other kept there)
|
|
386
|
+
if (!tx.from && !solanaTx) {
|
|
387
|
+
tx = tx.dexTxs[0]
|
|
388
|
+
tx.dexTxs = innerInstructions.slice(1)
|
|
389
|
+
}
|
|
343
390
|
}
|
|
344
391
|
}
|
|
345
392
|
}
|
|
@@ -348,7 +395,7 @@ class Api {
|
|
|
348
395
|
// How tokens tx are parsed:
|
|
349
396
|
// 0. compute incoming or outgoing tx: it's outgoing if spl-token:transfer has source/destination included in tokenAccountsByOwner
|
|
350
397
|
// 1. if it's a sent tx: sum all instructions amount (spl-token:transfer)
|
|
351
|
-
// 2. if it's an incoming tx:
|
|
398
|
+
// 2. if it's an incoming tx: sum all the amounts with destination included in tokenAccountsByOwner (aggregating by ticker)
|
|
352
399
|
// QUESTION: How do I know what are my tokens addresses deterministically? It's not possible, gotta use tokenAccountsByOwner
|
|
353
400
|
|
|
354
401
|
return {
|