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