@exodus/solana-api 1.2.6 → 1.2.7

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 +113 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-api",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "Exodus internal Solana asset API wrapper",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -14,12 +14,12 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@exodus/asset-json-rpc": "^1.0.0",
17
- "@exodus/solana-lib": "^1.2.5",
17
+ "@exodus/solana-lib": "^1.2.7",
18
18
  "lodash": "^4.17.11",
19
19
  "wretch": "^1.5.2"
20
20
  },
21
21
  "devDependencies": {
22
22
  "node-fetch": "~1.6.3"
23
23
  },
24
- "gitHead": "eb8a5bdbe538630232a503c7bdbd130a0de19706"
24
+ "gitHead": "892c55db784616256e065a2c931a5e5611f5404f"
25
25
  }
package/src/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // @flow
2
2
  import createApi from '@exodus/asset-json-rpc'
3
- import { tokens, SYSTEM_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@exodus/solana-lib'
3
+ import { tokens, SYSTEM_PROGRAM_ID, STAKE_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@exodus/solana-lib'
4
4
  import assert from 'assert'
5
5
  import lodash from 'lodash'
6
6
 
7
7
  // Doc: https://docs.solana.com/apps/jsonrpc-api
8
8
 
9
- const RPC_URL = 'https://vip-api.mainnet-beta.solana.com/' // https://api.mainnet-beta.solana.com, https://solana-api.projectserum.com
9
+ const RPC_URL = 'https://solana.a.exodus.io' // https://vip-api.mainnet-beta.solana.com/, https://api.mainnet-beta.solana.com, https://solana-api.projectserum.com
10
10
 
11
11
  // Tokens + SOL api support
12
12
  class Api {
@@ -19,6 +19,21 @@ class Api {
19
19
  this.api = createApi(this.rpcUrl)
20
20
  }
21
21
 
22
+ async getCurrentEpoch(): number {
23
+ const { epoch } = await this.api.post({
24
+ method: 'getEpochInfo',
25
+ })
26
+ return Number(epoch)
27
+ }
28
+
29
+ async getStakeActivation(address): string {
30
+ const { state } = await this.api.post({
31
+ method: 'getStakeActivation',
32
+ params: [address],
33
+ })
34
+ return state
35
+ }
36
+
22
37
  async getRecentBlockHash(): string {
23
38
  const {
24
39
  value: { blockhash },
@@ -88,8 +103,6 @@ class Api {
88
103
  .map(({ tokenAccountAddress }) => tokenAccountAddress)
89
104
  const accountsToCheck = [address, ...tokenAccountAddresses]
90
105
 
91
- console.log('accountsToCheck::', accountsToCheck)
92
-
93
106
  const txsResultsByAccount = await Promise.all(
94
107
  accountsToCheck.map((addr) =>
95
108
  this.getConfirmedSignaturesForAddress(addr, {
@@ -149,8 +162,11 @@ class Api {
149
162
  ...ix.parsed.info,
150
163
  }))
151
164
 
152
- const solanaTx = lodash.find(instructions, { program: 'system', type: 'transfer' }) // get SOL transfer
153
165
  // program:type tells us if it's a SOL or Token transfer
166
+ const solanaTx = lodash.find(instructions, { program: 'system', type: 'transfer' }) // get SOL transfer
167
+ const stakeTx = lodash.find(instructions, { program: 'system', type: 'createAccountWithSeed' })
168
+ const stakeWithdraw = lodash.find(instructions, { program: 'stake', type: 'withdraw' })
169
+ const stakeUndelegate = lodash.find(instructions, { program: 'stake', type: 'deactivate' })
154
170
 
155
171
  let tx
156
172
  if (solanaTx) {
@@ -163,6 +179,47 @@ class Api {
163
179
  amount: solanaTx.lamports, // number
164
180
  fee: isSending ? fee : 0,
165
181
  }
182
+ } else if (stakeTx) {
183
+ // start staking
184
+ tx = {
185
+ owner: stakeTx.base,
186
+ from: stakeTx.base,
187
+ to: stakeTx.owner,
188
+ amount: stakeTx.lamports,
189
+ fee,
190
+ staking: {
191
+ method: 'createAccountWithSeed',
192
+ seed: stakeTx.seed,
193
+ stakeAddress: stakeTx.newAccount,
194
+ stake: stakeTx.lamports,
195
+ },
196
+ }
197
+ } else if (stakeWithdraw) {
198
+ // TODO: lodash.find above returns 1 occurence, there could be multiple withdraw instructions in the same tx.
199
+ tx = {
200
+ owner: stakeWithdraw.withdrawAuthority,
201
+ from: stakeWithdraw.stakeAccount,
202
+ to: stakeWithdraw.destination,
203
+ amount: stakeWithdraw.lamports,
204
+ fee,
205
+ staking: {
206
+ method: 'withdraw',
207
+ stakeAddress: stakeWithdraw.stakeAccount,
208
+ stake: stakeWithdraw.lamports,
209
+ },
210
+ }
211
+ } else if (stakeUndelegate) {
212
+ tx = {
213
+ owner: stakeUndelegate.stakeAuthority,
214
+ from: stakeUndelegate.stakeAuthority,
215
+ to: stakeUndelegate.stakeAccount,
216
+ amount: 0,
217
+ fee,
218
+ staking: {
219
+ method: 'undelegate',
220
+ stakeAddress: stakeUndelegate.stakeAccount,
221
+ },
222
+ }
166
223
  } else {
167
224
  // Token tx
168
225
  assert.ok(
@@ -247,11 +304,12 @@ class Api {
247
304
  : tokenAccounts
248
305
  }
249
306
 
250
- async getTokensBalance(address: string) {
307
+ async getTokensBalance(address: string, filterByTokens = []) {
251
308
  let accounts = await this.getTokenAccountsByOwner(address) // Tokens
252
309
 
253
310
  const tokensBalance = accounts.reduce((acc, { tokenName, balance }) => {
254
- if (tokenName === 'unknown') return acc // filter by supported tokens only
311
+ if (tokenName === 'unknown' || (filterByTokens.length && !filterByTokens.includes(tokenName)))
312
+ return acc // filter by supported tokens only
255
313
  if (!acc[tokenName]) acc[tokenName] = Number(balance)
256
314
  // e.g { 'serum': 123 }
257
315
  else acc[tokenName] += Number(balance) // merge same token account balance
@@ -306,6 +364,54 @@ class Api {
306
364
  return type === 'solana'
307
365
  }
308
366
 
367
+ async getStakeAccountsInfo(address: string) {
368
+ // get staked amount and other info
369
+ const res = await this.api.post({
370
+ method: 'getProgramAccounts',
371
+ params: [
372
+ STAKE_PROGRAM_ID.toBase58(),
373
+ {
374
+ filters: [
375
+ {
376
+ memcmp: {
377
+ offset: 12,
378
+ bytes: address,
379
+ },
380
+ },
381
+ ],
382
+ encoding: 'jsonParsed',
383
+ },
384
+ ],
385
+ })
386
+ const accounts = {}
387
+ let totalStake = 0
388
+ let locked = 0
389
+ let withdrawable = 0
390
+ let pending = 0
391
+ for (let entry of res) {
392
+ const addr = entry.pubkey
393
+ const lamports = lodash.get(entry, 'account.lamports', 0)
394
+ const delegation = lodash.get(entry, 'account.data.parsed.info.stake.delegation', {})
395
+ // could have no delegation if the created stake address did not perform a delegate transaction
396
+
397
+ accounts[addr] = delegation
398
+ accounts[addr].lamports = lamports // sol balance
399
+ accounts[addr].activationEpoch = Number(accounts[addr].activationEpoch) || 0
400
+ accounts[addr].deactivationEpoch = Number(accounts[addr].deactivationEpoch) || 0
401
+ let state = 'inactive'
402
+ if (delegation.activationEpoch) state = await this.getStakeActivation(addr)
403
+ accounts[addr].state = state
404
+ accounts[addr].isDeactivating = state === 'deactivating'
405
+ accounts[addr].canWithdraw = state === 'inactive'
406
+ accounts[addr].stake = Number(accounts[addr].stake) || 0 // active staked amount
407
+ totalStake += accounts[addr].stake
408
+ locked += accounts[addr].canWithdraw ? 0 : lamports
409
+ withdrawable += accounts[addr].canWithdraw ? lamports : 0
410
+ pending += accounts[addr].isDeactivating ? lamports : 0
411
+ }
412
+ return { accounts, totalStake, locked, withdrawable, pending }
413
+ }
414
+
309
415
  /**
310
416
  * Broadcast a signed transaction
311
417
  */