@exodus/solana-api 2.5.18 → 2.5.20

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.
@@ -0,0 +1,353 @@
1
+ import lodash from 'lodash'
2
+
3
+ export default {
4
+ setServer: jest.fn(),
5
+ setAssets: jest.fn(),
6
+ setTokens: jest.fn(function(assets) {
7
+ const solTokens = lodash.pickBy(assets, (asset) => asset.assetType === 'SOLANA_TOKEN')
8
+ this.tokens = new Map(Object.values(solTokens).map((v) => [v.mintAddress, v]))
9
+ }),
10
+ watchAddress: jest.fn(),
11
+ unwatchAddress: jest.fn(),
12
+ getTransactions: async (...args) => {
13
+ expect(args).toEqual([
14
+ '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
15
+ { cursor: '', includeUnparsed: true },
16
+ ])
17
+ return {
18
+ transactions: [
19
+ {
20
+ timestamp: 1654797546000,
21
+ date: '2022-06-09T17:59:06.000Z',
22
+ id:
23
+ '3fiGCohizfVEeSSq4mrfGHve2tZA439toP9RwS1hFX1ViM4VEWX3Dw172ynsrJQ8evcCphohf7r7VTYg1KmvhnUC',
24
+ slot: 136939890,
25
+ error: false,
26
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
27
+ from: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
28
+ to: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
29
+ amount: 134570800,
30
+ fee: 5000,
31
+ staking: {
32
+ method: 'createAccountWithSeed',
33
+ seed: 'exodus:1654797541558',
34
+ stakeAddresses: ['7XHXjJQ7Gzno3QarhCFXXmd4xxBrRDsSGooLLLbYV7XR'],
35
+ stake: 134570800,
36
+ },
37
+ },
38
+ {
39
+ timestamp: 1653925537000,
40
+ date: '2022-05-30T15:45:37.000Z',
41
+ id:
42
+ '2uwzLyasKxnxB4FjxUbvUDbdpEaWmgzi6Te9PTAqwXsGdnYZdbeHh3LGmwBeVBLXqjCZM5kbvDuGR2Uth4bmon8f',
43
+ slot: 135760822,
44
+ error: false,
45
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
46
+ token: {
47
+ tokenAccountAddress: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
48
+ tokenName: 'mean_solana_c5cba5c4',
49
+ ticker: 'MEANsolanaC5CBA5C4',
50
+ mintAddress: 'MEANeD3XDdUmNMsRGjASkSWdC8prLYsoRJ61pPeHctD',
51
+ },
52
+ from: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
53
+ to: '3aSdqidyDDVvsh4ZRVrVNezVNDLKnHmyuoWGeFGi93m7',
54
+ amount: 10000,
55
+ fee: 5000,
56
+ },
57
+ {
58
+ timestamp: 1653353991000,
59
+ date: '2022-05-24T00:59:51.000Z',
60
+ id:
61
+ '4RegBpwrcmFvvq8qs9mGBGLY3sabdbUzFSeThXyFof9J64chQEWGRkBQSUeaq5p7sA4nV3wLH2hyoLex5TGoxTcQ',
62
+ slot: 134970480,
63
+ error: false,
64
+ owner: null,
65
+ token: {
66
+ tokenAccountAddress: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
67
+ tokenName: 'mean_solana_c5cba5c4',
68
+ ticker: 'MEANsolanaC5CBA5C4',
69
+ mintAddress: 'MEANeD3XDdUmNMsRGjASkSWdC8prLYsoRJ61pPeHctD',
70
+ },
71
+ from: '6YrejbdKKuxNT86QBfvPSpQYHLbQVMFSh14ZMJ9vALEt',
72
+ to: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
73
+ amount: 20000,
74
+ fee: 0,
75
+ },
76
+ {
77
+ timestamp: 1653353402000,
78
+ date: '2022-05-24T00:50:02.000Z',
79
+ id:
80
+ 'LA7eSpohaGFFMhgfMdyWV8EbuqKen1DQtLeqcv2V3tb9zQMUV7bj3VW9nq3ksHzubGEqoGmug5UNDXZRNUjnrLG',
81
+ slot: 134969651,
82
+ error: false,
83
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
84
+ token: {
85
+ tokenAccountAddress: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
86
+ tokenName: 'mean_solana_c5cba5c4',
87
+ ticker: 'MEANsolanaC5CBA5C4',
88
+ mintAddress: 'MEANeD3XDdUmNMsRGjASkSWdC8prLYsoRJ61pPeHctD',
89
+ },
90
+ from: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
91
+ to: 'DzEGYQg8QoT8QqFD3ERCcbXsdxDvGnxe3JaeJHd65vDJ',
92
+ amount: 70000,
93
+ fee: 5000,
94
+ },
95
+ {
96
+ timestamp: 1653343974000,
97
+ date: '2022-05-23T22:12:54.000Z',
98
+ id:
99
+ '3ZqxuAWfAGwF1HyHxFmHu3w1CHFh8EPJQe5wuaZ5V6udo17nYGJmEQR77UfrAWbNkH4eY7uXXPKzYpB4ZxWbpuBR',
100
+ slot: 134956663,
101
+ error: false,
102
+ owner: null,
103
+ token: {
104
+ tokenAccountAddress: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
105
+ tokenName: 'mean_solana_c5cba5c4',
106
+ ticker: 'MEANsolanaC5CBA5C4',
107
+ mintAddress: 'MEANeD3XDdUmNMsRGjASkSWdC8prLYsoRJ61pPeHctD',
108
+ },
109
+ from: '6YrejbdKKuxNT86QBfvPSpQYHLbQVMFSh14ZMJ9vALEt',
110
+ to: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
111
+ amount: 600000,
112
+ fee: 0,
113
+ },
114
+ {
115
+ timestamp: 1651246205000,
116
+ date: '2022-04-29T15:30:05.000Z',
117
+ id:
118
+ 'VAok8n3wmVCTfu5FDV9aEiHbjcSpj4ULZkonyoXPmeNynFHrkwakmmfsoLBn7bCnfy9nvRXjjbfo5sn7RKFV3Z6',
119
+ slot: 131819518,
120
+ error: false,
121
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
122
+ token: {
123
+ tokenAccountAddress: '5oq1Kvmmtgp9UtDzLEwYi7QcmcHUm45ZWgmV9yRKL6rS',
124
+ tokenName: '8hgy_solana_43b58185',
125
+ ticker: '8HGYsolana43B58185',
126
+ mintAddress: '8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh',
127
+ },
128
+ from: '5oq1Kvmmtgp9UtDzLEwYi7QcmcHUm45ZWgmV9yRKL6rS',
129
+ to: '7qfQ9QU47Lo9JjiVtj2PTSbbzgbQ4Hmz92Ct6AwwBVAp',
130
+ amount: 1000000,
131
+ fee: 5000,
132
+ },
133
+ {
134
+ timestamp: 1651240748000,
135
+ date: '2022-04-29T13:59:08.000Z',
136
+ id:
137
+ '24GQxKf8UCYu81qfGNNMj11vMPNp2tTBwiBSHiLEQajeYH4MkRmoeUoEedrssn4ZMigscXLZDbTRx836Wrhm7ibe',
138
+ slot: 131811126,
139
+ error: false,
140
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
141
+ token: {
142
+ tokenAccountAddress: '5oq1Kvmmtgp9UtDzLEwYi7QcmcHUm45ZWgmV9yRKL6rS',
143
+ tokenName: '8hgy_solana_43b58185',
144
+ ticker: '8HGYsolana43B58185',
145
+ mintAddress: '8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh',
146
+ },
147
+ from: '5oq1Kvmmtgp9UtDzLEwYi7QcmcHUm45ZWgmV9yRKL6rS',
148
+ to: '7qfQ9QU47Lo9JjiVtj2PTSbbzgbQ4Hmz92Ct6AwwBVAp',
149
+ amount: 5000000,
150
+ fee: 5000,
151
+ },
152
+ {
153
+ timestamp: 1651240452000,
154
+ date: '2022-04-29T13:54:12.000Z',
155
+ id:
156
+ '26mdaEU1sbBj3Y13L3aLM88cgrYWLy6u25teUz95zbiQG8kNoBoDAwHubYYuoLNvpToXBKoHJMdti3hSDHCYrLkf',
157
+ slot: 131810675,
158
+ error: false,
159
+ owner: null,
160
+ token: {
161
+ tokenAccountAddress: '5oq1Kvmmtgp9UtDzLEwYi7QcmcHUm45ZWgmV9yRKL6rS',
162
+ tokenName: '8hgy_solana_43b58185',
163
+ ticker: '8HGYsolana43B58185',
164
+ mintAddress: '8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh',
165
+ },
166
+ from: '5ddWhogjCr3mVGbiTYnJLa2ki9cHxERG2djH7qKNkjqw',
167
+ to: '5oq1Kvmmtgp9UtDzLEwYi7QcmcHUm45ZWgmV9yRKL6rS',
168
+ amount: 15000000,
169
+ fee: 0,
170
+ },
171
+ {
172
+ timestamp: 1649863458000,
173
+ date: '2022-04-13T15:24:18.000Z',
174
+ id:
175
+ '82SC4G8usbwM2Zsk2J3nURZmMz4omtneSRr7nX1i7sjzfajokAfUiusSYVd2ws5bn4ucaC1dvdiAYSS8pM8yqZS',
176
+ slot: 129572609,
177
+ error: false,
178
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
179
+ token: {
180
+ tokenAccountAddress: '4r6P9xCvb7413MB8bPrR7VCU8qcNbgpaFagkAJa57mPt',
181
+ tokenName: 'raydium',
182
+ ticker: 'RAY',
183
+ mintAddress: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
184
+ },
185
+ from: '4r6P9xCvb7413MB8bPrR7VCU8qcNbgpaFagkAJa57mPt',
186
+ to: '6do2vExLT9LETSUk1vKkm9A6BxgqtemQazKWndatjWFL',
187
+ amount: 386292,
188
+ fee: 5000,
189
+ },
190
+ {
191
+ timestamp: 1649857428000,
192
+ date: '2022-04-13T13:43:48.000Z',
193
+ id:
194
+ '4LsihvxHQwq58iQRJk3vuAxyvNaj4dxZ87i7BJqjh2UTsFEDouWscoKqu6orVV6zyywpmdNJGQxm3kKvxobg6UD6',
195
+ slot: 129562497,
196
+ error: false,
197
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
198
+ token: {
199
+ tokenAccountAddress: '4r6P9xCvb7413MB8bPrR7VCU8qcNbgpaFagkAJa57mPt',
200
+ tokenName: 'raydium',
201
+ ticker: 'RAY',
202
+ mintAddress: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
203
+ },
204
+ from: '4r6P9xCvb7413MB8bPrR7VCU8qcNbgpaFagkAJa57mPt',
205
+ to: '6do2vExLT9LETSUk1vKkm9A6BxgqtemQazKWndatjWFL',
206
+ amount: 772585,
207
+ fee: 5000,
208
+ },
209
+ {
210
+ timestamp: 1649857316000,
211
+ date: '2022-04-13T13:41:56.000Z',
212
+ id:
213
+ '25reWK943nzgGEDuJ5e56JCxmskwBzHtqQTQxV1FuhewDnPdfxYDJgSRaUFDVw6cP27qUj1J5vTVCFNumXvZX4BB',
214
+ slot: 129562293,
215
+ error: false,
216
+ owner: null,
217
+ token: {
218
+ tokenAccountAddress: '4r6P9xCvb7413MB8bPrR7VCU8qcNbgpaFagkAJa57mPt',
219
+ tokenName: 'raydium',
220
+ ticker: 'RAY',
221
+ mintAddress: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
222
+ },
223
+ from: 'Faszfxg7k2HWUT4CSGUn9MAVGUsPijvDQ3i2h7fi46M6',
224
+ to: '4r6P9xCvb7413MB8bPrR7VCU8qcNbgpaFagkAJa57mPt',
225
+ amount: 3545170,
226
+ fee: 0,
227
+ },
228
+ {
229
+ timestamp: 1649425956000,
230
+ date: '2022-04-08T13:52:36.000Z',
231
+ id:
232
+ '2Apnh4WBnVLpjceHMiMuY3rr49kzTJCMjwH4imsgjSHxtd7hvDQkmScTwuNCN22uAWNu4koDLLSvcFG6TwQnQWp5',
233
+ slot: 128799407,
234
+ error: false,
235
+ owner: '7HAULvGEChgLZSXV3zwzkJCnArtWbmMA5RygDpsfryEA',
236
+ from: '7HAULvGEChgLZSXV3zwzkJCnArtWbmMA5RygDpsfryEA',
237
+ to: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
238
+ amount: 142762920,
239
+ fee: 0,
240
+ },
241
+ {
242
+ timestamp: 1649425849000,
243
+ date: '2022-04-08T13:50:49.000Z',
244
+ id:
245
+ 'UaZAVAZEyBCuXJCbK8xh97KgB2ktMCgeppdomEf6riDsdSyK8HbAGJDVtqVYA54PduN9ZxZsnjuUTHV22LzsPNj',
246
+ slot: 128799211,
247
+ error: false,
248
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
249
+ from: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
250
+ to: '7HAULvGEChgLZSXV3zwzkJCnArtWbmMA5RygDpsfryEA',
251
+ amount: 152767920,
252
+ fee: 5000,
253
+ },
254
+ {
255
+ timestamp: 1649425475000,
256
+ date: '2022-04-08T13:44:35.000Z',
257
+ id:
258
+ '2GtBQDGgWYA3Sziht49yiFTk4BBoxk2pp8Bx5kLJzqf8PrZofmniKRUKt5ZSi1RjpJpYkwdDBP8PeVLAyiiTuA1c',
259
+ slot: 128798507,
260
+ error: false,
261
+ owner: '6ZRCB7AAqGre6c72PRz3MHLC73VMYvJ8bi9KHf1HFpNk',
262
+ from: '6ZRCB7AAqGre6c72PRz3MHLC73VMYvJ8bi9KHf1HFpNk',
263
+ to: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
264
+ amount: 162772920,
265
+ fee: 0,
266
+ },
267
+ ],
268
+ newCursor:
269
+ '3fiGCohizfVEeSSq4mrfGHve2tZA439toP9RwS1hFX1ViM4VEWX3Dw172ynsrJQ8evcCphohf7r7VTYg1KmvhnUC',
270
+ }
271
+ },
272
+
273
+ getStakeAccountsInfo: async (...args) => {
274
+ expect(args).toEqual(['8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X'])
275
+ return {
276
+ accounts: {
277
+ '7XHXjJQ7Gzno3QarhCFXXmd4xxBrRDsSGooLLLbYV7XR': {
278
+ activationEpoch: 316,
279
+ deactivationEpoch: 18446744073709552000,
280
+ stake: 132287920,
281
+ voter: '9QU2QSxhb24FUX3Tu2FpczXjpK3VYrvRudywSZaM29mF',
282
+ warmupCooldownRate: 0.25,
283
+ lamports: 134570800,
284
+ state: 'active',
285
+ isDeactivating: false,
286
+ canWithdraw: false,
287
+ },
288
+ },
289
+ totalStake: 132287920,
290
+ locked: 134570800,
291
+ withdrawable: 111,
292
+ pending: 222,
293
+ }
294
+ },
295
+
296
+ getRewards: async (...args) => {
297
+ expect(args).toEqual([['7XHXjJQ7Gzno3QarhCFXXmd4xxBrRDsSGooLLLbYV7XR']])
298
+ return 3333
299
+ },
300
+
301
+ getTokenAccountsByOwner: async (...args) => {
302
+ expect(args).toEqual(['8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X'])
303
+ return [
304
+ {
305
+ tokenAccountAddress: '5oq1Kvmmtgp9UtDzLEwYi7QcmcHUm45ZWgmV9yRKL6rS',
306
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
307
+ tokenName: '8hgy_solana_43b58185',
308
+ ticker: '8HGYsolana43B58185',
309
+ balance: '9000000',
310
+ mintAddress: '8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh',
311
+ },
312
+ {
313
+ tokenAccountAddress: 'DfV8UWiwyn8fiMdNZv3mA3wtNJw3eRYKVtV8itRDVF1R',
314
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
315
+ tokenName: 'mean_solana_c5cba5c4',
316
+ ticker: 'MEANsolanaC5CBA5C4',
317
+ balance: '540000',
318
+ mintAddress: 'MEANeD3XDdUmNMsRGjASkSWdC8prLYsoRJ61pPeHctD',
319
+ },
320
+ {
321
+ tokenAccountAddress: '4r6P9xCvb7413MB8bPrR7VCU8qcNbgpaFagkAJa57mPt',
322
+ owner: '8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X',
323
+ tokenName: 'raydium',
324
+ ticker: 'RAY',
325
+ balance: '2386293',
326
+ mintAddress: '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
327
+ },
328
+ ]
329
+ },
330
+
331
+ getWalletTokensList: async () => {
332
+ return [
333
+ '8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh',
334
+ 'MEANeD3XDdUmNMsRGjASkSWdC8prLYsoRJ61pPeHctD',
335
+ '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
336
+ 'UNKNOWN1AAAAyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNL',
337
+ 'UNKNOWN2BBBByjzvzp8eMZWUXbBCjEvwSkkk59S5iCNL',
338
+ ]
339
+ },
340
+
341
+ getTokensBalance: async () => {
342
+ return {
343
+ '8hgy_solana_43b58185': 9000000,
344
+ mean_solana_c5cba5c4: 540000,
345
+ raydium: 2386293,
346
+ }
347
+ },
348
+
349
+ getBalance: async (...args) => {
350
+ expect(args).toEqual(['8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X'])
351
+ return 10000000
352
+ },
353
+ }
@@ -0,0 +1,119 @@
1
+ import { intersection } from 'lodash'
2
+ import wretch from 'wretch'
3
+ import { fetch } from '@exodus/fetch'
4
+ import { InMemoryAssetClientInterface, PlainAddressResolver, logger } from '@exodus/assets-testing'
5
+
6
+ import assets from '../../__tests__/assets'
7
+ import { SolanaAccountState } from '../../account-state'
8
+ import { Api } from '../../api'
9
+ import { SolanaMonitor } from '../solana-monitor'
10
+
11
+ const { solana: asset } = assets
12
+
13
+ wretch().polyfills({
14
+ fetch,
15
+ })
16
+
17
+ jest.setTimeout(60000)
18
+
19
+ asset.api = {
20
+ createAccountState: () => SolanaAccountState,
21
+ }
22
+
23
+ // Fernando's dev wallet. Profile 1
24
+ const walletAddresses = {
25
+ solana: ['8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X'],
26
+ }
27
+
28
+ const api = new Api({ assets })
29
+
30
+ describe('solana monitor', () => {
31
+ test('can start monitor, update txs and balances', async () => {
32
+ const unknownAddress = '8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh'
33
+ const assetsWithoutUnknown = Object.fromEntries(
34
+ Object.values(assets)
35
+ .filter((asset) => {
36
+ const isUnknown =
37
+ asset.assetType === 'SOLANA_TOKEN' && asset.mintAddress === unknownAddress
38
+ return !isUnknown
39
+ })
40
+ .map((asset) => {
41
+ return [asset.name, asset]
42
+ })
43
+ )
44
+ const assetClientInterface = new InMemoryAssetClientInterface({
45
+ assets: assetsWithoutUnknown,
46
+ logger,
47
+ addressResolver: new PlainAddressResolver({ walletAddresses }),
48
+ })
49
+ const monitor = new SolanaMonitor({
50
+ api: api,
51
+ interval: 1000,
52
+ asset,
53
+ assetClientInterface,
54
+ logger,
55
+ })
56
+ try {
57
+ const unknownTokensHandler = jest.fn()
58
+ monitor.on('unknown-tokens', unknownTokensHandler)
59
+
60
+ await monitor.start()
61
+
62
+ expect(logger.error).not.toBeCalled()
63
+
64
+ // Syntax sugar
65
+ const txs = (walletAccount, assetName) => {
66
+ return assetClientInterface.getTxLog({ walletAccount, assetName })
67
+ }
68
+
69
+ const accountState = await assetClientInterface.getAccountState({
70
+ walletAccount: 'exodus0',
71
+ assetName: 'solana',
72
+ })
73
+
74
+ expect(accountState).toBeDefined()
75
+
76
+ expect(accountState.balance.gt(asset.currency.defaultUnit(0))).toEqual(true)
77
+ expect(accountState.cursor).toBeDefined()
78
+
79
+ // Some token balances my account has.
80
+ expect(
81
+ accountState.tokenBalances.mean_solana_c5cba5c4.gt(
82
+ assets.mean_solana_c5cba5c4.currency.defaultUnit(0)
83
+ )
84
+ ).toEqual(true)
85
+
86
+ expect(accountState.tokenBalances['8hgy_solana_43b58185']).toEqual(undefined)
87
+
88
+ expect((await txs('exodus0', 'solana')).size).toBeGreaterThanOrEqual(10)
89
+ expect((await txs('exodus0', 'raydium')).size).toBeGreaterThanOrEqual(3)
90
+ expect((await txs('exodus0', '8hgy_solana_43b58185')).size).toEqual(0)
91
+
92
+ expect(accountState.mem.loaded).toEqual(true)
93
+ expect(accountState.mem.staking).toStrictEqual({
94
+ enabled: true,
95
+ pool: '9QU2QSxhb24FUX3Tu2FpczXjpK3VYrvRudywSZaM29mF',
96
+ })
97
+
98
+ // expect(accountState.mem.isDelegating).toEqual(true)
99
+ // expect(accountState.mem.locked.gt(asset.currency.defaultUnit(0))).toEqual(true)
100
+ expect(accountState.mem.withdrawable).toBeDefined()
101
+ expect(accountState.mem.pending).toBeDefined()
102
+ expect(accountState.mem.earned).toBeDefined()
103
+
104
+ expect(logger.error).not.toBeCalled()
105
+ expect(logger.warn).not.toBeCalled()
106
+
107
+ expect(unknownTokensHandler.mock.calls.length).toBe(1)
108
+
109
+ const expectedAddresses = [unknownAddress]
110
+ expect(intersection(unknownTokensHandler.mock.calls[0][0], expectedAddresses).sort()).toEqual(
111
+ expectedAddresses.sort()
112
+ )
113
+ } finally {
114
+ await monitor.stop()
115
+ }
116
+ expect(logger.error).not.toBeCalled()
117
+ expect(monitor.timer.isRunning).toEqual(false)
118
+ })
119
+ })
@@ -0,0 +1,132 @@
1
+ import { InMemoryAssetClientInterface, PlainAddressResolver, logger } from '@exodus/assets-testing'
2
+
3
+ import assets from '../../__tests__/assets'
4
+ import { SolanaAccountState } from '../../account-state'
5
+ import { SolanaMonitor } from '../solana-monitor'
6
+ import solanaMonitorApiMock from './solana-monitor-api-mock'
7
+
8
+ const { solana: asset } = assets
9
+
10
+ asset.api = {
11
+ createAccountState: () => SolanaAccountState,
12
+ }
13
+
14
+ // Fernando's dev wallet. Profile 1
15
+ const walletAddresses = {
16
+ solana: ['8APBjTndtCF4kfKHaJG9boR2dREGNCK4yFRVfqUfzS4X'],
17
+ }
18
+
19
+ describe('solana monitor', () => {
20
+ test('can start monitor, update txs and balances', async () => {
21
+ const assetClientInterface = new InMemoryAssetClientInterface({
22
+ assets,
23
+ logger,
24
+ addressResolver: new PlainAddressResolver({ walletAddresses }),
25
+ })
26
+
27
+ const monitor = new SolanaMonitor({
28
+ api: solanaMonitorApiMock,
29
+ interval: 1000,
30
+ asset,
31
+ assetClientInterface,
32
+ logger,
33
+ includeUnparsed: true,
34
+ })
35
+
36
+ const unknownTokensHandler = jest.fn()
37
+ monitor.on('unknown-tokens', unknownTokensHandler)
38
+
39
+ try {
40
+ await monitor.start()
41
+ expect(logger.error).not.toBeCalled()
42
+ expect(logger.warn).not.toBeCalled()
43
+
44
+ // Sintax sugar
45
+ const txs = (walletAccount, assetName) => {
46
+ return assetClientInterface.getTxLog({ walletAccount, assetName })
47
+ }
48
+
49
+ const accountState = await assetClientInterface.getAccountState({
50
+ walletAccount: 'exodus0',
51
+ assetName: 'solana',
52
+ })
53
+
54
+ expect(accountState).toBeDefined()
55
+
56
+ const expectSameValue = (actual, expected) => {
57
+ expect(actual).toEqual(expected)
58
+ expect(actual.equals(expected)).toEqual(true)
59
+ }
60
+
61
+ expectSameValue(accountState.balance, asset.currency.defaultUnit('0.144571133'))
62
+ expect(accountState.cursor).toEqual(
63
+ '3fiGCohizfVEeSSq4mrfGHve2tZA439toP9RwS1hFX1ViM4VEWX3Dw172ynsrJQ8evcCphohf7r7VTYg1KmvhnUC'
64
+ )
65
+
66
+ // Some token balances my account has.
67
+ expectSameValue(
68
+ accountState.tokenBalances.mean_solana_c5cba5c4,
69
+ assets.mean_solana_c5cba5c4.currency.defaultUnit('0.54')
70
+ )
71
+ expectSameValue(
72
+ accountState.tokenBalances.raydium,
73
+ assets.raydium.currency.defaultUnit('2.386293')
74
+ )
75
+
76
+ expectSameValue(
77
+ accountState.tokenBalances['8hgy_solana_43b58185'],
78
+ assets['8hgy_solana_43b58185'].currency.defaultUnit('9')
79
+ )
80
+
81
+ const solanaTxs = await txs('exodus0', 'solana')
82
+ expect(solanaTxs.size).toEqual(10)
83
+ expect((await txs('exodus0', 'raydium')).size).toEqual(3)
84
+ expect((await txs('exodus0', '8hgy_solana_43b58185')).size).toEqual(3)
85
+
86
+ // A regular solana transaction.
87
+ const regularTransaction = solanaTxs.get(
88
+ '2GtBQDGgWYA3Sziht49yiFTk4BBoxk2pp8Bx5kLJzqf8PrZofmniKRUKt5ZSi1RjpJpYkwdDBP8PeVLAyiiTuA1c'
89
+ )
90
+ expectSameValue(regularTransaction.coinAmount, asset.currency.defaultUnit('0.16277292'))
91
+
92
+ // A transaction for another token has been added to Solana to reflect the paid fees.
93
+ const feeTransaction = solanaTxs.get(
94
+ '4LsihvxHQwq58iQRJk3vuAxyvNaj4dxZ87i7BJqjh2UTsFEDouWscoKqu6orVV6zyywpmdNJGQxm3kKvxobg6UD6'
95
+ )
96
+ expectSameValue(feeTransaction.feeAmount, asset.currency.defaultUnit('0.000005'))
97
+ expect(feeTransaction.tokens).toEqual(['raydium'])
98
+ expectSameValue(
99
+ asset.currency.defaultUnit(feeTransaction.coinAmount.toNumber()),
100
+ asset.currency.defaultUnit(0).toDefault()
101
+ )
102
+
103
+ // Stake info
104
+ expect(accountState.mem.loaded).toEqual(true)
105
+ expect(accountState.mem.staking).toStrictEqual({
106
+ enabled: true,
107
+ pool: '9QU2QSxhb24FUX3Tu2FpczXjpK3VYrvRudywSZaM29mF',
108
+ })
109
+ expect(accountState.mem.isDelegating).toEqual(true)
110
+ expectSameValue(accountState.mem.locked, asset.currency.defaultUnit('0.1345708'))
111
+ expectSameValue(accountState.mem.withdrawable, asset.currency.defaultUnit('0.000000111'))
112
+ expectSameValue(accountState.mem.pending, asset.currency.defaultUnit('0.000000222'))
113
+ expectSameValue(accountState.mem.earned, asset.currency.defaultUnit('0.000003333'))
114
+
115
+ const expectedAddresses = [
116
+ 'UNKNOWN1AAAAyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNL',
117
+ 'UNKNOWN2BBBByjzvzp8eMZWUXbBCjEvwSkkk59S5iCNL',
118
+ ]
119
+
120
+ expect(unknownTokensHandler.mock.calls.length).toBe(1)
121
+ expect(unknownTokensHandler.mock.calls[0][0].sort()).toEqual(expectedAddresses.sort())
122
+
123
+ expect(logger.error).not.toBeCalled()
124
+ expect(logger.warn).not.toBeCalled()
125
+ } finally {
126
+ await monitor.stop()
127
+ }
128
+ expect(logger.error).not.toBeCalled()
129
+ expect(logger.warn).not.toBeCalled()
130
+ expect(monitor.timer.isRunning).toEqual(false)
131
+ })
132
+ })