@exodus/solana-api 1.4.7 → 1.4.8

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 +68 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-api",
3
- "version": "1.4.7",
3
+ "version": "1.4.8",
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": "7d0c7a92fe8f9d108f88584b6578edf7a66adfd4"
25
+ "gitHead": "90293962539f80a74060b3010cbfa4ab4570baa7"
26
26
  }
package/src/index.js CHANGED
@@ -120,7 +120,10 @@ class Api {
120
120
  /**
121
121
  * Get transactions from an address
122
122
  */
123
- async getTransactions(address: string, { cursor, before, limit } = {}): any {
123
+ async getTransactions(
124
+ address: string,
125
+ { cursor, before, limit, includeUnparsed = false } = {}
126
+ ): any {
124
127
  let transactions = []
125
128
  // cursor is a txHash
126
129
 
@@ -151,8 +154,10 @@ class Api {
151
154
  if (txDetail === null) return
152
155
 
153
156
  const timestamp = txDetail.blockTime * 1000
154
- const parsedTx = this.parseTransaction(address, txDetail, tokenAccountsByOwner)
155
- if (!parsedTx.from) return // cannot parse it
157
+ const parsedTx = this.parseTransaction(address, txDetail, tokenAccountsByOwner, {
158
+ includeUnparsed,
159
+ })
160
+ if (!parsedTx.from && !includeUnparsed) return // cannot parse it
156
161
 
157
162
  // split dexTx in separate txs
158
163
  if (parsedTx.dexTxs) {
@@ -184,12 +189,39 @@ class Api {
184
189
  return { transactions, newCursor }
185
190
  }
186
191
 
187
- parseTransaction(ownerAddress: string, txDetails: Object, tokenAccountsByOwner: ?Array): Object {
188
- let { fee, preTokenBalances, postTokenBalances, innerInstructions } = txDetails.meta
192
+ parseTransaction(
193
+ ownerAddress: string,
194
+ txDetails: Object,
195
+ tokenAccountsByOwner: ?Array,
196
+ { includeUnparsed = false } = {}
197
+ ): Object {
198
+ let {
199
+ fee,
200
+ preBalances,
201
+ postBalances,
202
+ preTokenBalances,
203
+ postTokenBalances,
204
+ innerInstructions,
205
+ } = txDetails.meta
206
+ preBalances = preBalances || []
207
+ postBalances = postBalances || []
189
208
  preTokenBalances = preTokenBalances || []
190
209
  postTokenBalances = postTokenBalances || []
191
210
  innerInstructions = innerInstructions || []
211
+
192
212
  let { instructions, accountKeys } = txDetails.transaction.message
213
+
214
+ const getUnparsedTx = () => {
215
+ const ownerIndex = accountKeys.findIndex((accountKey) => accountKey.pubkey === ownerAddress)
216
+ const feePaid = ownerIndex === 0 ? fee : 0
217
+
218
+ return {
219
+ unparsed: true,
220
+ amount: postBalances[ownerIndex] - preBalances[ownerIndex] + feePaid,
221
+ fee: feePaid,
222
+ }
223
+ }
224
+
193
225
  instructions = instructions
194
226
  .filter((ix) => ix.parsed) // only known instructions
195
227
  .map((ix) => ({
@@ -368,30 +400,45 @@ class Api {
368
400
  if (preBalances.length || postBalances.length) {
369
401
  tx = {}
370
402
 
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,
403
+ if (includeUnparsed && innerInstructions.length) {
404
+ // when using includeUnparsed for DEX tx we want to keep SOL tx as "unparsed"
405
+ // 1. we want to treat all SOL dex transactions as "Contract transaction", not "Sent SOL"
406
+ // 2. default behavior is not perfect. For example it doesn't see SOL-side tx in
407
+ // SOL->SPL swaps on Raydium and Orca.
408
+ tx = getUnparsedTx(tx)
409
+ tx.dexTxs = innerInstructions.map((i) => ({ ...i, fee: 0 }))
410
+ } else {
411
+ if (solanaTx) {
412
+ // the base tx will be the one that moved solana.
413
+ tx = {
414
+ owner: solanaTx.source,
415
+ from: solanaTx.source,
416
+ to: solanaTx.destination,
417
+ amount: solanaTx.lamports, // number
418
+ fee: ownerAddress === solanaTx.source ? fee : 0,
419
+ }
379
420
  }
380
- }
381
421
 
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)
422
+ // If it has inner instructions then it's a DEX tx that moved SPL -> SPL
423
+ if (innerInstructions.length) {
424
+ tx.dexTxs = innerInstructions
425
+ // if tx involves only SPL swaps. Expand DEX ix (first element as tx base and the other kept there)
426
+ if (!tx.from && !solanaTx) {
427
+ tx = tx.dexTxs[0]
428
+ tx.dexTxs = innerInstructions.slice(1)
429
+ }
389
430
  }
390
431
  }
391
432
  }
392
433
  }
393
434
  }
394
435
 
436
+ const unparsed = Object.keys(tx).length === 0
437
+
438
+ if (unparsed && includeUnparsed) {
439
+ tx = getUnparsedTx(tx)
440
+ }
441
+
395
442
  // How tokens tx are parsed:
396
443
  // 0. compute incoming or outgoing tx: it's outgoing if spl-token:transfer has source/destination included in tokenAccountsByOwner
397
444
  // 1. if it's a sent tx: sum all instructions amount (spl-token:transfer)