@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.
Files changed (2) hide show
  1. package/package.json +2 -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.1",
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": "49f3f78ac41acaf405e2849cccb2f9ecd0060424"
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
- const { fee, preTokenBalances, postTokenBalances } = txDetails.meta
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 (solanaTx) {
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 && preBalances.length === postBalances.length) {
320
- // parse and compute pre-balances/post-balances difference (1 token per tx spported at the moment)
321
- const amount =
322
- Number(lodash.get(postBalances, '[0].uiTokenAmount.amount', 0)) -
323
- Number(lodash.get(preBalances, '[0].uiTokenAmount.amount', 0))
324
- const isSending = Math.sign(amount) <= 0
325
-
326
- const mint = lodash.get(postBalances, '[0].mint', '')
327
- const { name, ticker } = this.tokens[mint] || {
328
- name: 'unknown',
329
- ticker: 'UNKNOWN',
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
- owner: isSending ? ownerAddress : TOKEN_PROGRAM_ID.toBase58(),
334
- token: {
335
- tokenAccountAddress: '', // token account is closed after the swap
336
- tokenName: name,
337
- ticker,
338
- },
339
- from: isSending ? ownerAddress : TOKEN_PROGRAM_ID.toBase58(),
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: sull all the amounts with destination included in tokenAccountsByOwner (aggregating by ticker)
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 {