@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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. 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.1",
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.1",
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": "49f3f78ac41acaf405e2849cccb2f9ecd0060424"
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: 'getRecentBlockhash',
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
- const { fee, preTokenBalances, postTokenBalances } = txDetails.meta
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 (solanaTx) {
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 && 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',
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
- 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
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: sull all the amounts with destination included in tokenAccountsByOwner (aggregating by ticker)
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 {