@exodus/solana-api 2.5.31-alpha.0 → 2.5.31-alpha.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-api",
3
- "version": "2.5.31-alpha.0",
3
+ "version": "2.5.31-alpha.1",
4
4
  "description": "Exodus internal Solana asset API wrapper",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -34,5 +34,5 @@
34
34
  "devDependencies": {
35
35
  "@exodus/assets-testing": "file:../../../__testing__"
36
36
  },
37
- "gitHead": "659340c999fa86c132293643a7fb96f33963e6cd"
37
+ "gitHead": "9ec7084bcdfe14f1c6f11df393351885773046a6"
38
38
  }
package/src/api.js CHANGED
@@ -59,16 +59,18 @@ export class Api {
59
59
  async watchAddress({
60
60
  address,
61
61
  tokensAddresses = [],
62
+ onMessage,
62
63
  handleAccounts,
63
64
  handleTransfers,
64
65
  handleReconnect,
65
66
  reconnectDelay,
66
67
  }) {
67
- if (FORCE_HTTP) return false
68
+ if (this.connections[address]) return // already subscribed
68
69
  const conn = new Connection({
69
70
  endpoint: this.wsUrl,
70
71
  address,
71
72
  tokensAddresses,
73
+ onMsg: (json) => onMessage(json),
72
74
  callback: (updates) =>
73
75
  this.handleUpdates({ updates, address, handleAccounts, handleTransfers }),
74
76
  reconnectCallback: handleReconnect,
package/src/connection.js CHANGED
@@ -20,6 +20,7 @@ export class Connection {
20
20
  address,
21
21
  tokensAddresses = [],
22
22
  callback,
23
+ onMsg,
23
24
  reconnectCallback = () => {},
24
25
  reconnectDelay = DEFAULT_RECONNECT_DELAY,
25
26
  }) {
@@ -27,6 +28,7 @@ export class Connection {
27
28
  this.tokensAddresses = tokensAddresses
28
29
  this.endpoint = endpoint
29
30
  this.callback = callback
31
+ this.onMsg = onMsg
30
32
  this.reconnectCallback = reconnectCallback
31
33
  this.reconnectDelay = reconnectDelay
32
34
 
@@ -136,7 +138,7 @@ export class Connection {
136
138
  debug('pushing msg to queue', msg)
137
139
  this.messageQueue.push(msg) // sub results
138
140
  }
139
- this.processMessages()
141
+ this.processMessages(json)
140
142
  } else {
141
143
  if (lodash.get(this.rpcQueue, json.id)) {
142
144
  this.rpcQueue[json.id].reject(new Error(json.error.message))
@@ -195,7 +197,8 @@ export class Connection {
195
197
  }
196
198
  }
197
199
 
198
- async processMessages() {
200
+ async processMessages(json) {
201
+ if (this.onMsg) await this.onMsg(json)
199
202
  if (this.inProcessMessages) return null
200
203
  this.inProcessMessages = true
201
204
  try {
@@ -10,25 +10,14 @@ const DEFAULT_REMOTE_CONFIG = {
10
10
  staking: { enabled: true, pool: DEFAULT_POOL_ADDRESS },
11
11
  }
12
12
 
13
- const TICKS_BETWEEN_HISTORY_FETCHES = 10
14
- const TICKS_BETWEEN_STAKE_FETCHES = 5
15
-
16
13
  export class SolanaMonitor extends BaseMonitor {
17
- constructor({
18
- api,
19
- includeUnparsed = false,
20
- ticksBetweenHistoryFetches = TICKS_BETWEEN_HISTORY_FETCHES,
21
- ticksBetweenStakeFetches = TICKS_BETWEEN_STAKE_FETCHES,
22
- ...args
23
- }) {
14
+ constructor({ api, includeUnparsed = false, ...args }) {
24
15
  super(args)
25
16
  assert(api, 'api is required')
26
17
  this.api = api
27
18
  this.cursors = {}
28
19
  this.assets = {}
29
20
  this.staking = DEFAULT_REMOTE_CONFIG.staking
30
- this.ticksBetweenStakeFetches = ticksBetweenStakeFetches
31
- this.ticksBetweenHistoryFetches = ticksBetweenHistoryFetches
32
21
  this.includeUnparsed = includeUnparsed
33
22
  this.addHook('before-stop', (...args) => this.beforeStop(...args))
34
23
  }
@@ -52,15 +41,10 @@ export class SolanaMonitor extends BaseMonitor {
52
41
  })
53
42
  return this.api.watchAddress({
54
43
  address,
55
- /*
56
- // OPTIONAL. Relying on polling through ws
57
- tokensAddresses: [], // needed for ASA subs
58
- handleAccounts: (updates) => this.accountsCallback({ updates, walletAccount }),
59
- handleTransfers: (txs) => {
60
- // new SOL tx, ticking monitor
61
- this.tick({ walletAccount }) // it will cause refresh for both sender/receiver. Without necessarily fetching the tx if it's not finalized in the node.
44
+ onMessage: (json) => {
45
+ // new SOL tx event, tick monitor with 15 sec delay (to avoid hitting delayed nodes)
46
+ setTimeout(() => this.tick({ walletAccount }), 15_000)
62
47
  },
63
- */
64
48
  })
65
49
  }
66
50
 
@@ -103,20 +87,6 @@ export class SolanaMonitor extends BaseMonitor {
103
87
  return _.uniq(_.flatten(stakingAddresses))
104
88
  }
105
89
 
106
- balanceChanged({ account, newAccount }) {
107
- const solBalanceChanged = !account.balance || !account.balance.equals(newAccount.balance)
108
- if (solBalanceChanged) return true
109
-
110
- const tokenBalanceChanged =
111
- !account.tokenBalances ||
112
- Object.entries(newAccount.tokenBalances).some(
113
- ([token, balance]) =>
114
- !account.tokenBalances[token] || !account.tokenBalances[token].equals(balance)
115
- )
116
-
117
- return tokenBalanceChanged
118
- }
119
-
120
90
  async tick({ walletAccount, refresh }) {
121
91
  // Check for new wallet account
122
92
  await this.initWalletAccount({ walletAccount })
@@ -129,43 +99,28 @@ export class SolanaMonitor extends BaseMonitor {
129
99
  const address = await this.aci.getReceiveAddress({ assetName, walletAccount, useCache: true })
130
100
  const stakingAddresses = await this.getStakingAddressesFromTxLog({ assetName, walletAccount })
131
101
 
132
- const fetchStakingInfo = this.tickCount[walletAccount] % this.ticksBetweenStakeFetches === 0
133
- const staking = fetchStakingInfo
134
- ? await this.getStakingInfo({ address, stakingAddresses })
135
- : accountState.mem
136
-
137
- const tokenAccounts = await this.api.getTokenAccountsByOwner(address)
138
- const account = await this.getAccount({ address, staking, tokenAccounts })
102
+ const { logItemsByAsset, hasNewTxs, cursorState } = await this.getHistory({
103
+ address,
104
+ accountState,
105
+ walletAccount,
106
+ refresh,
107
+ })
139
108
 
140
- const balanceChanged = this.balanceChanged({ account: accountState, newAccount: account })
109
+ const cursorChanged = this.hasNewCursor({ walletAccount, cursorState })
141
110
 
142
- const isHistoryUpdateTick =
143
- this.tickCount[walletAccount] % this.ticksBetweenHistoryFetches === 0
111
+ if (refresh || hasNewTxs || cursorChanged) {
112
+ const staking =
113
+ refresh || cursorChanged
114
+ ? await this.getStakingInfo({ address, stakingAddresses })
115
+ : accountState.mem
144
116
 
145
- const shouldUpdateHistory = refresh || isHistoryUpdateTick || balanceChanged
146
- const shouldUpdateOnlyBalance = balanceChanged && !shouldUpdateHistory
147
- const shouldUpdateBalanceBeforeHistory = true
117
+ const tokenAccounts = await this.api.getTokenAccountsByOwner(address)
118
+ const account = await this.getAccount({ address, staking, tokenAccounts })
148
119
 
149
- // getHistory is more likely to fail/be rate limited, so we want to update users balance only on a lot of ticks
150
- if (shouldUpdateBalanceBeforeHistory || shouldUpdateOnlyBalance) {
151
120
  // update all state at once
152
- await this.updateState({ account, walletAccount, staking })
153
121
  await this.emitUnknownTokensEvent({ tokenAccounts })
154
- }
155
- if (shouldUpdateHistory) {
156
- const { logItemsByAsset, cursorState } = await this.getHistory({
157
- address,
158
- accountState,
159
- walletAccount,
160
- refresh,
161
- })
162
-
163
- const cursorChanged = this.hasNewCursor({ walletAccount, cursorState })
164
-
165
- // update all state at once
166
122
  await this.updateTxLogByAsset({ walletAccount, logItemsByAsset, refresh })
167
123
  await this.updateState({ account, cursorState, walletAccount, staking })
168
- await this.emitUnknownTokensEvent({ tokenAccounts })
169
124
  if (refresh || cursorChanged) {
170
125
  this.cursors[walletAccount] = cursorState.cursor
171
126
  }