@exodus/solana-api 2.5.20 → 2.5.21

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.
@@ -1,44 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ValidateError = void 0;
7
- exports.validateBeforePay = validateBeforePay;
8
-
9
- var _ = _interopRequireDefault(require(".."));
10
-
11
- var _currency = _interopRequireDefault(require("@exodus/currency"));
12
-
13
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
-
15
- class ValidateError extends Error {
16
- constructor(...args) {
17
- super(...args);
18
- this.name = 'ValidateError';
19
- }
20
-
21
- }
22
-
23
- exports.ValidateError = ValidateError;
24
-
25
- async function validateBeforePay({
26
- asset,
27
- senderInfo,
28
- amount,
29
- feeAmount = 0,
30
- recipient,
31
- checkEnoughBalance = true
32
- }) {
33
- const isNative = asset.name === asset.baseAsset.name;
34
- const recipientInfo = await _.default.getAccountInfo(recipient);
35
- if (!recipientInfo) throw new ValidateError(`recipient ${recipient} not found`);
36
-
37
- if (checkEnoughBalance) {
38
- const totalAmountMustPay = asset.currency.defaultUnit(amount).add(feeAmount);
39
- const currentBalance = isNative ? senderInfo.balance : senderInfo.tokenBalances[asset.name];
40
- if (totalAmountMustPay.gt(currentBalance)) throw new ValidateError(`insufficient funds`);
41
- }
42
-
43
- return true;
44
- }
@@ -1,18 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
-
7
- var _solanaMonitor = require("./solana-monitor");
8
-
9
- Object.keys(_solanaMonitor).forEach(function (key) {
10
- if (key === "default" || key === "__esModule") return;
11
- if (key in exports && exports[key] === _solanaMonitor[key]) return;
12
- Object.defineProperty(exports, key, {
13
- enumerable: true,
14
- get: function () {
15
- return _solanaMonitor[key];
16
- }
17
- });
18
- });
@@ -1,354 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.SolanaMonitor = void 0;
7
-
8
- var _assetLib = require("@exodus/asset-lib");
9
-
10
- var _lodash = _interopRequireDefault(require("lodash"));
11
-
12
- var _minimalisticAssert = _interopRequireDefault(require("minimalistic-assert"));
13
-
14
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
-
16
- const DEFAULT_POOL_ADDRESS = '9QU2QSxhb24FUX3Tu2FpczXjpK3VYrvRudywSZaM29mF'; // Everstake
17
-
18
- const DEFAULT_REMOTE_CONFIG = {
19
- rpcs: [],
20
- ws: [],
21
- staking: {
22
- enabled: true,
23
- pool: DEFAULT_POOL_ADDRESS
24
- }
25
- };
26
-
27
- class SolanaMonitor extends _assetLib.BaseMonitor {
28
- constructor({
29
- api,
30
- includeUnparsed = false,
31
- ...args
32
- }) {
33
- super(args);
34
- (0, _minimalisticAssert.default)(api, 'api is required');
35
- this.api = api;
36
- this.cursors = {};
37
- this.assets = {};
38
- this.includeUnparsed = includeUnparsed;
39
- this.addHook('before-stop', (...args) => this.beforeStop(...args));
40
- }
41
-
42
- async beforeStop() {
43
- const walletAccounts = await this.aci.getWalletAccounts({
44
- assetName: this.asset.name
45
- });
46
- return Promise.all(walletAccounts.map(walletAccount => this.stopListener({
47
- walletAccount
48
- })));
49
- }
50
-
51
- async initWalletAccount({
52
- walletAccount
53
- }) {
54
- if (this.tickCount[walletAccount] === 0) {
55
- await this.startListener({
56
- walletAccount
57
- });
58
- }
59
- }
60
-
61
- async startListener({
62
- walletAccount
63
- }) {
64
- const address = await this.aci.getReceiveAddress({
65
- assetName: this.asset.name,
66
- walletAccount
67
- });
68
- return this.api.watchAddress({
69
- address
70
- /*
71
- // OPTIONAL. Relying on polling through ws
72
- tokensAddresses: [], // needed for ASA subs
73
- handleAccounts: (updates) => this.accountsCallback({ updates, walletAccount }),
74
- handleTransfers: (txs) => {
75
- // new SOL tx, ticking monitor
76
- this.tick({ walletAccount }) // it will cause refresh for both sender/receiver. Without necessarily fetching the tx if it's not finalized in the node.
77
- },
78
- */
79
-
80
- });
81
- }
82
-
83
- async stopListener({
84
- walletAccount
85
- }) {
86
- const address = await this.aci.getReceiveAddress({
87
- assetName: this.asset.name,
88
- walletAccount
89
- });
90
- return this.api.unwatchAddress({
91
- address
92
- });
93
- }
94
-
95
- setServer(config = {}) {
96
- const {
97
- rpcs,
98
- ws,
99
- staking = {}
100
- } = { ...DEFAULT_REMOTE_CONFIG,
101
- ...config
102
- };
103
- this.api.setServer(rpcs[0]);
104
- this.api.setWsEndpoint(ws[0]);
105
- this.staking = staking;
106
- }
107
-
108
- hasNewCursor({
109
- walletAccount,
110
- cursorState
111
- }) {
112
- const {
113
- cursor
114
- } = cursorState;
115
- return this.cursors[walletAccount] !== cursor;
116
- }
117
-
118
- async emitUnknownTokensEvent({
119
- tokenAccounts
120
- }) {
121
- const tokensList = await this.api.getWalletTokensList({
122
- tokenAccounts
123
- });
124
-
125
- if (tokensList.length > 0) {
126
- this.emit('unknown-tokens', tokensList);
127
- }
128
- }
129
-
130
- async tick({
131
- walletAccount,
132
- refresh
133
- }) {
134
- // Check for new wallet account
135
- await this.initWalletAccount({
136
- walletAccount
137
- });
138
- const assetName = this.asset.name;
139
- this.assets = await this.aci.getAssetsForNetwork({
140
- baseAssetName: assetName
141
- });
142
- this.api.setTokens(this.assets);
143
- const accountState = await this.aci.getAccountState({
144
- assetName,
145
- walletAccount
146
- });
147
- const address = await this.aci.getReceiveAddress({
148
- assetName,
149
- walletAccount
150
- });
151
- const {
152
- logItemsByAsset,
153
- hasNewTxs,
154
- cursorState
155
- } = await this.getHistory({
156
- address,
157
- accountState,
158
- walletAccount,
159
- refresh
160
- });
161
- let staking = accountState.mem;
162
- const cursorChanged = this.hasNewCursor({
163
- walletAccount,
164
- cursorState
165
- });
166
-
167
- if (refresh || cursorChanged) {
168
- staking = await this.updateStakingInfo({
169
- walletAccount,
170
- address
171
- });
172
- this.cursors[walletAccount] = cursorState.cursor;
173
- }
174
-
175
- await this.updateTxLogByAsset({
176
- walletAccount,
177
- logItemsByAsset,
178
- refresh
179
- });
180
-
181
- if (refresh || hasNewTxs || cursorChanged) {
182
- const tokenAccounts = await this.api.getTokenAccountsByOwner(address);
183
- await this.emitUnknownTokensEvent({
184
- tokenAccounts
185
- });
186
- const account = await this.getAccount({
187
- address,
188
- staking,
189
- tokenAccounts
190
- });
191
- await this.updateState({
192
- account,
193
- cursorState,
194
- walletAccount
195
- });
196
- }
197
- }
198
-
199
- async getHistory({
200
- address,
201
- accountState,
202
- refresh
203
- } = {}) {
204
- let cursor = refresh ? '' : accountState.cursor;
205
- const baseAsset = this.asset;
206
- const {
207
- transactions,
208
- newCursor
209
- } = await this.api.getTransactions(address, {
210
- cursor,
211
- includeUnparsed: this.includeUnparsed
212
- });
213
- const mappedTransactions = [];
214
-
215
- for (const tx of transactions) {
216
- const assetName = _lodash.default.get(tx, 'token.tokenName', baseAsset.name);
217
-
218
- const asset = this.assets[assetName];
219
- if (assetName === 'unknown' || !asset) continue; // skip unknown tokens
220
-
221
- const coinAmount = asset.currency.baseUnit(tx.amount).toDefault();
222
- const item = {
223
- coinName: assetName,
224
- txId: tx.id,
225
- from: [tx.from],
226
- coinAmount,
227
- confirmations: 1,
228
- // tx.confirmations, // avoid multiple notifications
229
- date: tx.date,
230
- error: tx.error,
231
- data: {
232
- staking: tx.staking || null,
233
- unparsed: !!tx.unparsed,
234
- swapTx: !!(tx.data && tx.data.inner)
235
- }
236
- };
237
-
238
- if (tx.owner === address) {
239
- // send transaction
240
- item.to = tx.to;
241
- item.feeAmount = baseAsset.currency.baseUnit(tx.fee).toDefault(); // in SOL
242
-
243
- item.coinAmount = item.coinAmount.negate();
244
-
245
- if (tx.to === tx.owner) {
246
- item.selfSend = true;
247
- item.coinAmount = asset.currency.ZERO;
248
- }
249
- } else if (tx.unparsed) {
250
- if (tx.fee !== 0) item.feeAmount = baseAsset.currency.baseUnit(tx.fee).toDefault(); // in SOL
251
-
252
- item.data.meta = tx.data.meta;
253
- }
254
-
255
- if (asset.assetType === 'SOLANA_TOKEN' && item.feeAmount && item.feeAmount.isPositive) {
256
- const feeAsset = asset.feeAsset;
257
- const feeItem = { ..._lodash.default.clone(item),
258
- coinName: feeAsset.name,
259
- tokens: [asset.name],
260
- coinAmount: feeAsset.currency.ZERO
261
- };
262
- mappedTransactions.push(feeItem);
263
- }
264
-
265
- mappedTransactions.push(item);
266
- } // logItemsByAsset = { 'solana:': [...], 'serum': [...] }
267
-
268
-
269
- return {
270
- logItemsByAsset: _lodash.default.groupBy(mappedTransactions, item => item.coinName),
271
- hasNewTxs: transactions.length > 0,
272
- cursorState: {
273
- cursor: newCursor
274
- }
275
- };
276
- }
277
-
278
- async getAccount({
279
- address,
280
- staking,
281
- tokenAccounts
282
- }) {
283
- const tokens = Object.keys(this.assets).filter(name => name !== this.asset.name);
284
- const [solBalance, splBalances] = await Promise.all([this.api.getBalance(address), this.api.getTokensBalance({
285
- address,
286
- filterByTokens: tokens,
287
- tokenAccounts
288
- })]);
289
- const stakedBalance = this.asset.currency.baseUnit(staking.locked).toDefault();
290
- const withdrawableBalance = this.asset.currency.baseUnit(staking.withdrawable).toDefault();
291
- const pendingBalance = this.asset.currency.baseUnit(staking.pending).toDefault();
292
- const balance = this.asset.currency.baseUnit(solBalance).toDefault().add(stakedBalance).add(withdrawableBalance).add(pendingBalance);
293
-
294
- const tokenBalances = _lodash.default.mapValues(splBalances, (balance, name) => this.assets[name].currency.baseUnit(balance).toDefault());
295
-
296
- return {
297
- balance,
298
- tokenBalances
299
- };
300
- }
301
-
302
- async updateState({
303
- account,
304
- cursorState,
305
- walletAccount
306
- }) {
307
- const {
308
- balance,
309
- tokenBalances
310
- } = account;
311
- const newData = {
312
- balance,
313
- tokenBalances,
314
- ...cursorState
315
- };
316
- return this.updateAccountState({
317
- newData,
318
- walletAccount
319
- });
320
- }
321
-
322
- async updateStakingInfo({
323
- walletAccount,
324
- address
325
- }) {
326
- const stakingInfo = await this.api.getStakeAccountsInfo(address);
327
- const rewards = await this.api.getRewards(Object.keys(stakingInfo.accounts));
328
- const mem = {
329
- loaded: true,
330
- staking: this.staking,
331
- isDelegating: Object.values(stakingInfo.accounts).some(({
332
- state
333
- }) => ['active', 'activating', 'inactive'].includes(state)),
334
- // true if at least 1 account is delegating
335
- locked: this.asset.currency.baseUnit(stakingInfo.locked).toDefault(),
336
- withdrawable: this.asset.currency.baseUnit(stakingInfo.withdrawable).toDefault(),
337
- pending: this.asset.currency.baseUnit(stakingInfo.pending).toDefault(),
338
- // still undelegating (not yet available for withdraw)
339
- earned: this.asset.currency.baseUnit(rewards).toDefault(),
340
- accounts: stakingInfo.accounts // Obj
341
-
342
- };
343
- await this.updateAccountState({
344
- walletAccount,
345
- newData: {
346
- mem
347
- }
348
- });
349
- return mem;
350
- }
351
-
352
- }
353
-
354
- exports.SolanaMonitor = SolanaMonitor;
@@ -1,286 +0,0 @@
1
- import lodash from 'lodash'
2
- import { fetch } from '@exodus/fetch'
3
- import wretch from 'wretch'
4
- import * as splToken from '@exodus/solana-spl-token'
5
- import {
6
- generateKeyPair,
7
- PublicKey,
8
- LAMPORTS_PER_SOL,
9
- TOKEN_PROGRAM_ID,
10
- SystemProgram,
11
- U64,
12
- SolanaWeb3Transaction as Transaction,
13
- } from '@exodus/solana-lib'
14
- import { Api } from '../index'
15
- import { SOL_TRANSFER_TX } from './fixtures'
16
- import assets from './assets'
17
-
18
- const api = new Api({ assets })
19
-
20
- wretch().polyfills({
21
- fetch,
22
- })
23
-
24
- const SECONDS = 1000
25
- jest.setTimeout(60 * SECONDS)
26
-
27
- const ADDRESS_WITH_HISTORY = 'Cd1uLoQwGZiDSyJxrwbpYmuDZM2tDftkowPjWv5YE5Z7'
28
- const ADDRESS_FRESH = '3bbnZt1mzp1EBRR9Rm6TX2cXzZYbVvM9THutWL8bCmVH' // no txs history
29
- const TXID =
30
- '4AwqVmXFzciF89JyzMrTr5FT2tX1iMg7ypzCzUsGMD54dwjrE8w4GLNTbXXRpNcE8JkTSGRbZ8SzEc5umQJNFS3m'
31
- const SRM_TOKEN_TXID =
32
- '2zJ7dnXMZYUhw4uaeLaBRcoDsgfM9HZ1GSbfjJp4SxWrFhzEdwNKFfFFJ8LL6ckKwh5Xg5ik5pUuRtjMkGMDZpgV'
33
-
34
- beforeAll(() => {
35
- return api.watchAddress({ address: ADDRESS_WITH_HISTORY }) // Open WS connection
36
- })
37
-
38
- test('Solana: getRecentBlockHash', async () => {
39
- const blockHash = await api.getRecentBlockHash()
40
- expect(typeof blockHash).toEqual('string')
41
- expect(blockHash.length > 40).toEqual(true)
42
- })
43
-
44
- test('Solana: getTransactionById', async () => {
45
- const result = await api.getTransactionById(TXID)
46
- expect(result.transaction.signatures[0]).toEqual(TXID) // string
47
- expect(result.meta.fee > 0).toEqual(true)
48
- expect(Array.isArray(result.meta.preBalances)).toEqual(true)
49
- expect(Array.isArray(result.meta.postBalances)).toEqual(true)
50
- expect(result.transaction.message.accountKeys.length > 1).toEqual(true)
51
- expect(result.transaction.message.accountKeys[0].pubkey).toEqual(
52
- 'Bnjx2F242ZK6LsKbAvRwDoeKczJTK8LFM918p8sZCb69'
53
- )
54
- })
55
-
56
- test('Solana: getFee', async () => {
57
- const fee = await api.getFee()
58
- expect(fee >= 4000).toEqual(true) // usually 5000 lamports
59
- })
60
-
61
- test('Solana: getBalance', async () => {
62
- const [balance1, balance2] = await Promise.all([
63
- api.getBalance(ADDRESS_WITH_HISTORY),
64
- api.getBalance(ADDRESS_FRESH),
65
- ])
66
- expect(typeof balance1 === 'number').toEqual(true)
67
- expect(typeof balance2 === 'number').toEqual(true)
68
- expect(balance2).toEqual(0)
69
- })
70
-
71
- test('Solana: getBlockTime', async () => {
72
- const ts = await api.getBlockTime(49901505)
73
- expect(ts) // if null the backend started pruning old tx entries and has partial ledger!
74
- })
75
-
76
- test('Solana: getConfirmedSignaturesForAddress', async () => {
77
- const addrTxs = await api.getConfirmedSignaturesForAddress(ADDRESS_WITH_HISTORY)
78
- expect(addrTxs.length > 4)
79
- const addrTxs2 = await api.getConfirmedSignaturesForAddress(ADDRESS_WITH_HISTORY, { until: '' })
80
- expect(addrTxs2.length > 4).toEqual(true)
81
- })
82
-
83
- describe('getTransactions', () => {
84
- test('Solana: Returns empty transactions if account is not created', async () => {
85
- const { transactions } = await api.getTransactions(ADDRESS_FRESH)
86
- expect(transactions.length === 0).toBeTruthy()
87
- })
88
-
89
- test('Solana: Returns correct transactions if account is created', async () => {
90
- const { transactions, newCursor } = await api.getTransactions(ADDRESS_WITH_HISTORY)
91
- expect(transactions.length >= 1).toBeTruthy()
92
- expect(typeof newCursor).toEqual('string')
93
- // check tokens txs returned as well
94
- const tokenTx = transactions.find(({ id }) => id === SRM_TOKEN_TXID)
95
- // console.log('tokenTx:', tokenTx)
96
- expect(tokenTx.timestamp).toEqual(1608598478000)
97
- expect(tokenTx.id).toEqual(SRM_TOKEN_TXID)
98
- expect(tokenTx.token.tokenName).toEqual('serum')
99
- expect(tokenTx.owner).toEqual(null)
100
- expect(tokenTx.from).toEqual('71GbXnJkHz15kzjagB2f7N9H7HPA99v6BFuqfdYL5qtf')
101
- expect(tokenTx.amount).toEqual(1000)
102
- expect(tokenTx.fee).toEqual(0)
103
- })
104
-
105
- test('Solana: Returns expected transactions with cursor', async () => {
106
- const OLD_TXID =
107
- '3qFUz73ZPiexM8yHyTFhnEhXgVinzXCQMieJbTsfZKcW5V3vPSJJvRCSKjCHJDYtWqReeBrFV98hcCFaAznpjgUC' // existing old txid that should not be returned in txs history after cursor date.
108
- const { transactions } = await api.getTransactions(ADDRESS_WITH_HISTORY, {
109
- cursor:
110
- '4kzJjAbaT1PVSuk7JDh6697Y63MrGGMwDgi8CKRtctHbXWRjPtrzY1RnbMuedPDefru2hrGeLfRDpqtdFmQQAMXm',
111
- })
112
- expect(transactions.length >= 1).toBeTruthy()
113
- expect(lodash.find(transactions, { id: OLD_TXID })).toBeFalsy()
114
- expect(
115
- lodash.find(transactions, {
116
- id:
117
- '3V7eDr5BbgZfgiADpUxM2fHQ1ZAoJHddxxfFRd8KtDQeTqLsv9wkfeg6NrVHzQc7wnEmVRQPdsYBT3RKLztL6Suf',
118
- })
119
- ).toBeTruthy()
120
- })
121
- })
122
-
123
- test('Solana: parseTransaction on a SOL tx', async () => {
124
- const owner = 'Bnjx2F242ZK6LsKbAvRwDoeKczJTK8LFM918p8sZCb69'
125
- const tx = await api.parseTransaction(owner, SOL_TRANSFER_TX)
126
- // console.log(tx)
127
- expect(tx.id).toEqual(
128
- '4AwqVmXFzciF89JyzMrTr5FT2tX1iMg7ypzCzUsGMD54dwjrE8w4GLNTbXXRpNcE8JkTSGRbZ8SzEc5umQJNFS3m'
129
- )
130
- expect(tx.slot).toEqual(36446581)
131
- expect(tx.error).toEqual(false)
132
- expect(tx.from).toEqual('Bnjx2F242ZK6LsKbAvRwDoeKczJTK8LFM918p8sZCb69')
133
- expect(tx.to).toEqual('84HuMpN8JPy9DLerT5nRcEHjkb1UQS3TD4CvR9y8MqgD')
134
- expect(tx.amount).toEqual(2e13)
135
- expect(tx.fee).toEqual(5e3)
136
- })
137
-
138
- test('Solana: getMinimumBalanceForRentExemption', async () => {
139
- const minimumBalance = await api.getMinimumBalanceForRentExemption(80)
140
- expect(typeof minimumBalance === 'number').toEqual(true)
141
- })
142
-
143
- test('Solana: getMetaplexMetadata', async () => {
144
- const MANGO_SPL_TOKEN = 'MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac'
145
-
146
- const metadata = await api.getMetaplexMetadata(MANGO_SPL_TOKEN)
147
- // console.log('metadata', metadata)
148
- expect(metadata.name).toEqual('Mango')
149
- expect(metadata.symbol).toEqual('MNGO')
150
- })
151
-
152
- test('Solana: getMetaplexMetadata of invalid address returns null', async () => {
153
- const RANDOM_SPL_TOKEN = 'Dhg9XnzJWzSQqH2aAnhPTEJHGQAkALDfD98MA499A7pa' // doesn't have metaplex data
154
-
155
- const metadata = await api.getMetaplexMetadata(RANDOM_SPL_TOKEN)
156
- expect(metadata).toEqual(null)
157
- })
158
-
159
- test('Solana: broadcastTransaction', async () => {
160
- // execute setTimeout callback immediately (no wait)
161
- jest.spyOn(global, 'setTimeout')
162
- global.setTimeout.mockImplementation((callback) => {
163
- return callback()
164
- })
165
-
166
- try {
167
- // send transaction
168
- const signedTx =
169
- 'AVXo5X7UNzpuOmYzkZ+fqHDGiRLTSMlWlUCcZKzEV5CIKlrdvZa3/2GrJJfPrXgZqJbYDaGiOnP99tI/sRJfiwwBAAEDRQ/n5E5CLbMbHanUG3+iVvBAWZu0WFM6NoB5xfybQ7kNwwgfIhv6odn2qTUu/gOisDtaeCW1qlwW/gx3ccr/4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvsInicc+E3IZzLqeA+iM5cn9kSaeFzOuClz1Z2kZQy0BAgIAAQwCAAAAAPIFKgEAAAA='
170
- await api.broadcastTransaction(signedTx)
171
- expect('To Fail').toEqual(true)
172
- } catch (err) {
173
- expect(err.message.match(/Blockhash not found/)).toBeTruthy()
174
- } finally {
175
- global.setTimeout.mockRestore()
176
- }
177
- })
178
-
179
- describe('Solana: getDecimals', () => {
180
- test('returns decimals if valid SPL address', async () => {
181
- const decimals = await api.getDecimals('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v') // USDC
182
- expect(decimals).toEqual(6)
183
- })
184
-
185
- test('Solana: getDecimals: errors if invalid SPL address', async () => {
186
- try {
187
- await api.getDecimals('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1w')
188
- expect('To Fail').toEqual(true)
189
- } catch (err) {
190
- expect(err.message.match(/could not find account/)).toBeTruthy()
191
- }
192
-
193
- try {
194
- await api.getDecimals('EPjFWdd5ZwTDt1')
195
- expect('To Fail').toEqual(true)
196
- } catch (err) {
197
- expect(err.message.match(/WrongSize/)).toBeTruthy()
198
- }
199
- })
200
- })
201
-
202
- describe('Solana: getAccountInfo', () => {
203
- test('returns parsed data', async () => {
204
- const value = await api.getAccountInfo('26EA6FxEfryse9iJbZrtYCUwhAwxmqG1GurrfbJarmem')
205
- expect(typeof value?.data?.parsed === 'object').toBeTruthy()
206
- })
207
-
208
- test('returns base64 data', async () => {
209
- const value = await api.getAccountInfo('26EA6FxEfryse9iJbZrtYCUwhAwxmqG1GurrfbJarmem', 'base64')
210
- expect(Array.isArray(value.data)).toBeTruthy()
211
- expect(Buffer.from(value.data[0], 'base64').length).toEqual(8184)
212
- })
213
- })
214
-
215
- describe('Solana: isSpl', () => {
216
- test('returns true for addresses owned by the Token Program', async () => {
217
- expect(await api.isSpl('BmxZ1pghpcoyT7aykj7D1o4AxWirTqvD7zD2tNngjirT')).toBeTruthy()
218
- expect(await api.isSpl('buMnhMd5xSyXBssTQo15jouu8VhuEZJCfbtBUZgRcuW')).toBeTruthy()
219
- expect(await api.isSpl('MAPS41MDahZ9QdKXhVa4dWB9RuyfV4XqhyAZ8XcYepb')).toBeTruthy()
220
- })
221
-
222
- test('returns false for addresses not owned by the Token Program', async () => {
223
- expect(await api.isSpl('9zyPU1mjgzaVyQsYwKJJ7AhVz5bgx5uc1NPABvAcUXsT')).toBeFalsy()
224
- })
225
- })
226
-
227
- describe('Solana: simulateAndRetrieveSideEffects', () => {
228
- const ownerSolAccount = new PublicKey('3b1rT3knyRnZHKHCCxKWnJDWo8k6SDdSTvYzqyXUYwxL') // 1 SOL
229
- const ownerTokenAddress = new PublicKey('FRaWiAGc3bvQKAkqqAxwmLvCVH7AaUGo6MrBUYhCmojK') // 0 Wrapped SOL (Token)
230
-
231
- const secondarySolAccount = new PublicKey('41TDiDgCLz2eh5vPMhWCdNeaoMfrs3tM4rGdZgX4jL56') // 1 SOL
232
- const secondaryTokenAddress = new PublicKey('8HPbjj4QVnYGUcRrfeZ3XdMhq36RnaJqvUHjHZYZ2p65') // 1 Wrapped SOL (Token)
233
-
234
- // Use solana devnet since we need actual accounts with available balance for simulation
235
- const testApi = new Api({ rpcUrl: 'https://api.devnet.solana.com', assets })
236
-
237
- test('returns willSend and willReceive account changes', async () => {
238
- const SOL_SEND_AMOUNT = LAMPORTS_PER_SOL * 0.1
239
- const TOKEN_SEND_AMOUNT = LAMPORTS_PER_SOL * 0.531
240
-
241
- const txFee = await api.getFee()
242
- const transaction = new Transaction()
243
- // Owner sends SOL
244
- transaction.add(
245
- SystemProgram.transfer({
246
- fromPubkey: ownerSolAccount,
247
- toPubkey: generateKeyPair().publicKey,
248
- lamports: SOL_SEND_AMOUNT,
249
- })
250
- )
251
- // Owner receives Wrapped SOL token
252
- transaction.add(
253
- splToken.Token.createTransferInstruction(
254
- TOKEN_PROGRAM_ID,
255
- secondaryTokenAddress,
256
- ownerTokenAddress,
257
- secondarySolAccount,
258
- [],
259
- TOKEN_SEND_AMOUNT
260
- )
261
- )
262
- transaction.recentBlockhash = await testApi.getRecentBlockHash()
263
- transaction.feePayer = ownerSolAccount
264
- transaction.signatures = [
265
- { signature: null, publicKey: ownerSolAccount },
266
- { signature: null, publicKey: secondarySolAccount },
267
- ]
268
-
269
- const result = await testApi.simulateAndRetrieveSideEffects(
270
- transaction.compileMessage(),
271
- ownerSolAccount.toString()
272
- )
273
-
274
- expect(result.willSend.length === 1).toBeTruthy()
275
- expect(result.willSend[0].type).toEqual('SOL')
276
- expect(result.willSend[0].balance).toEqual(new U64((SOL_SEND_AMOUNT + txFee * 2) * -1)) // two tx and will send would be negative
277
-
278
- expect(result.willReceive.length === 1).toBeTruthy()
279
- expect(result.willReceive[0].type).toEqual('TOKEN')
280
- expect(result.willReceive[0].balance).toEqual(new U64(TOKEN_SEND_AMOUNT))
281
- })
282
- })
283
-
284
- afterAll(async () => {
285
- return api.unwatchAddress({ address: ADDRESS_WITH_HISTORY })
286
- })
@@ -1,7 +0,0 @@
1
- import { keyBy } from '@exodus/basic-utils'
2
- import { connectAssets } from '@exodus/assets'
3
- import assetsList from '@exodus/solana-meta'
4
-
5
- const assets = connectAssets(keyBy(assetsList, (asset) => asset.name))
6
-
7
- export default assets