@xchainjs/xchain-solana 0.0.4 → 0.0.6

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/lib/index.esm.js CHANGED
@@ -1,12 +1,12 @@
1
- import { mplTokenMetadata, fetchAllDigitalAsset, fetchDigitalAsset } from '@metaplex-foundation/mpl-token-metadata';
1
+ import { mplTokenMetadata, fetchDigitalAsset, fetchAllDigitalAsset } from '@metaplex-foundation/mpl-token-metadata';
2
2
  import { publicKey } from '@metaplex-foundation/umi';
3
3
  import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
4
4
  import { isAddress } from '@solana/addresses';
5
5
  import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, getAccount, createTransferInstruction, TokenAccountNotFoundError, TokenInvalidAccountOwnerError, getOrCreateAssociatedTokenAccount } from '@solana/spl-token';
6
6
  import { Connection, clusterApiUrl, Keypair, PublicKey, Transaction, SystemProgram, TransactionInstruction, ComputeBudgetProgram, SendTransactionError } from '@solana/web3.js';
7
- import { ExplorerProvider, Network, BaseXChainClient, FeeType, FeeOption, TxType } from '@xchainjs/xchain-client';
7
+ import { ExplorerProvider, Network, BaseXChainClient, TxType, FeeType, FeeOption } from '@xchainjs/xchain-client';
8
8
  import { getSeed } from '@xchainjs/xchain-crypto';
9
- import { AssetType, baseAmount, assetFromStringEx, eqAsset, getContractAddressFromAsset, assetToBase, assetAmount } from '@xchainjs/xchain-util';
9
+ import { AssetType, baseAmount, assetToBase, assetAmount, assetFromStringEx, eqAsset, getContractAddressFromAsset } from '@xchainjs/xchain-util';
10
10
  import bs58 from 'bs58';
11
11
  import { HDKey } from 'micro-ed25519-hdkey';
12
12
 
@@ -24,7 +24,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
24
24
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
25
25
  PERFORMANCE OF THIS SOFTWARE.
26
26
  ***************************************************************************** */
27
- /* global Reflect, Promise, SuppressedError, Symbol */
27
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
28
28
 
29
29
 
30
30
  function __awaiter(thisArg, _arguments, P, generator) {
@@ -87,8 +87,55 @@ class Client extends BaseXChainClient {
87
87
  constructor(params = defaultSolanaParams) {
88
88
  super(SOLChain, Object.assign(Object.assign({}, defaultSolanaParams), params));
89
89
  this.explorerProviders = params.explorerProviders;
90
- this.connection = new Connection(clusterApiUrl(getSolanaNetwork(this.getNetwork())));
91
- this.umi = createUmi(this.connection).use(mplTokenMetadata());
90
+ this.clientUrls = params.clientUrls;
91
+ if (!this.clientUrls) {
92
+ const solanaProvider = new Connection(clusterApiUrl(getSolanaNetwork(this.getNetwork())));
93
+ const umiProvider = createUmi(solanaProvider).use(mplTokenMetadata());
94
+ this.providers = [
95
+ {
96
+ solanaProvider,
97
+ umiProvider,
98
+ },
99
+ ];
100
+ }
101
+ else {
102
+ this.providers = this.clientUrls[this.getNetwork()].map((url) => {
103
+ const solanaProvider = new Connection(url);
104
+ const umiProvider = createUmi(solanaProvider).use(mplTokenMetadata());
105
+ return {
106
+ solanaProvider,
107
+ umiProvider,
108
+ };
109
+ });
110
+ }
111
+ }
112
+ /**
113
+ * Set or update the current network.
114
+ * @param {Network} network The network to set
115
+ * @returns {void}
116
+ */
117
+ setNetwork(network) {
118
+ super.setNetwork(network);
119
+ if (!this.clientUrls) {
120
+ const solanaProvider = new Connection(clusterApiUrl(getSolanaNetwork(this.getNetwork())));
121
+ const umiProvider = createUmi(solanaProvider).use(mplTokenMetadata());
122
+ this.providers = [
123
+ {
124
+ solanaProvider,
125
+ umiProvider,
126
+ },
127
+ ];
128
+ }
129
+ else {
130
+ this.providers = this.clientUrls[this.getNetwork()].map((url) => {
131
+ const solanaProvider = new Connection(url);
132
+ const umiProvider = createUmi(solanaProvider).use(mplTokenMetadata());
133
+ return {
134
+ solanaProvider,
135
+ umiProvider,
136
+ };
137
+ });
138
+ }
92
139
  }
93
140
  /**
94
141
  * Get information about the native asset of the Solana.
@@ -173,39 +220,7 @@ class Client extends BaseXChainClient {
173
220
  */
174
221
  getBalance(address, assets) {
175
222
  return __awaiter(this, void 0, void 0, function* () {
176
- const balances = [];
177
- const nativeBalance = yield this.connection.getBalance(new PublicKey(address));
178
- balances.push({
179
- asset: SOLAsset,
180
- amount: baseAmount(nativeBalance, SOL_DECIMALS),
181
- });
182
- const tokenBalances = yield this.connection.getParsedTokenAccountsByOwner(new PublicKey(address), {
183
- programId: TOKEN_PROGRAM_ID,
184
- });
185
- const tokensToRequest = !assets
186
- ? tokenBalances.value
187
- : tokenBalances.value.filter((tokenBalance) => {
188
- const tokenData = tokenBalance.account.data.parsed;
189
- return (assets.findIndex((asset) => {
190
- return asset.symbol.toLowerCase().includes(tokenData.info.mint.toLowerCase());
191
- }) !== -1);
192
- });
193
- const mintPublicKeys = tokensToRequest.map((tokenBalance) => {
194
- const tokenData = tokenBalance.account.data.parsed;
195
- return publicKey(tokenData.info.mint);
196
- });
197
- const assetsData = yield fetchAllDigitalAsset(this.umi, mintPublicKeys);
198
- tokenBalances.value.forEach((balance) => {
199
- const parsedData = balance.account.data.parsed;
200
- const assetData = assetsData.find((assetData) => assetData.publicKey.toString() === parsedData.info.mint);
201
- if (assetData) {
202
- balances.push({
203
- amount: baseAmount(parsedData.info.tokenAmount.amount, parsedData.info.tokenAmount.decimals),
204
- asset: assetFromStringEx(`SOL.${assetData.metadata.symbol.trim()}-${parsedData.info.mint}`),
205
- });
206
- }
207
- });
208
- return balances;
223
+ return this.roundRobinGetBalance(address, assets);
209
224
  });
210
225
  }
211
226
  /**
@@ -219,64 +234,7 @@ class Client extends BaseXChainClient {
219
234
  return __awaiter(this, void 0, void 0, function* () {
220
235
  if (!params)
221
236
  throw new Error('Params need to be passed');
222
- const sender = Keypair.generate();
223
- const toPubkey = new PublicKey(params.recipient);
224
- const transaction = new Transaction();
225
- transaction.recentBlockhash = yield this.connection.getLatestBlockhash().then((block) => block.blockhash);
226
- transaction.feePayer = sender.publicKey;
227
- let createAccountTxFee = 0;
228
- if (!params.asset || eqAsset(params.asset, this.getAssetInfo().asset)) {
229
- // Native transfer
230
- transaction.add(SystemProgram.transfer({
231
- fromPubkey: sender.publicKey,
232
- toPubkey,
233
- lamports: params.amount.amount().toNumber(),
234
- }));
235
- }
236
- else {
237
- // Token transfer
238
- const mintAddress = new PublicKey(getContractAddressFromAsset(params.asset));
239
- const associatedTokenAddress = getAssociatedTokenAddressSync(mintAddress, toPubkey);
240
- try {
241
- yield getAccount(this.connection, associatedTokenAddress, undefined, TOKEN_PROGRAM_ID);
242
- transaction.add(createTransferInstruction(sender.publicKey, // Should be Token account, but as it new KeyPair for estimation, sender public key
243
- associatedTokenAddress, sender.publicKey, params.amount.amount().toNumber()));
244
- }
245
- catch (error) {
246
- if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
247
- // recipient token account has to be created
248
- const dataLength = 165; // Normally used for Token accounts
249
- createAccountTxFee = yield this.connection.getMinimumBalanceForRentExemption(dataLength);
250
- transaction.add(createTransferInstruction(sender.publicKey, // Should be Token account, but as it new KeyPair for estimation, sender public key
251
- toPubkey, // Should be Token account, but as recipient token account should be created, recipient public key
252
- sender.publicKey, params.amount.amount().toNumber()));
253
- }
254
- }
255
- }
256
- if (params.memo) {
257
- transaction.add(new TransactionInstruction({
258
- keys: [{ pubkey: sender.publicKey, isSigner: true, isWritable: true }],
259
- data: Buffer.from(params.memo, 'utf-8'),
260
- programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
261
- }));
262
- }
263
- if (params.priorityFee) {
264
- transaction.add(ComputeBudgetProgram.setComputeUnitPrice({
265
- microLamports: params.priorityFee.amount().toNumber() / Math.pow(10, 3),
266
- }));
267
- }
268
- if (params.limit) {
269
- transaction.add(ComputeBudgetProgram.setComputeUnitLimit({
270
- units: params.limit,
271
- }));
272
- }
273
- const fee = (yield transaction.getEstimatedFee(this.connection)) || 0;
274
- return {
275
- type: FeeType.FlatFee,
276
- [FeeOption.Average]: baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
277
- [FeeOption.Fast]: baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
278
- [FeeOption.Fastest]: baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
279
- };
237
+ return this.roundRobinGetFees(params);
280
238
  });
281
239
  }
282
240
  /**
@@ -287,29 +245,21 @@ class Client extends BaseXChainClient {
287
245
  */
288
246
  getTransactionData(txId) {
289
247
  return __awaiter(this, void 0, void 0, function* () {
290
- const transaction = yield this.connection.getParsedTransaction(txId);
248
+ const transaction = yield this.roundRobinGetTransactionData(txId);
291
249
  if (!transaction)
292
250
  throw Error('Can not find transaction');
293
251
  return this.parseTransaction(transaction);
294
252
  });
295
253
  }
254
+ /**
255
+ * Get the transaction history of a given address with pagination options.
256
+ *
257
+ * @param {TxHistoryParams} params The options to get transaction history.
258
+ * @returns {TxsPage} The transaction history.
259
+ */
296
260
  getTransactions(params) {
297
261
  return __awaiter(this, void 0, void 0, function* () {
298
- const signatures = yield this.connection.getSignaturesForAddress(new PublicKey((params === null || params === void 0 ? void 0 : params.address) || (yield this.getAddressAsync())));
299
- const transactions = yield this.connection.getParsedTransactions(signatures.map(({ signature }) => signature));
300
- const results = yield Promise.allSettled(transactions
301
- .filter((transaction) => !!transaction)
302
- .map((transaction) => this.parseTransaction(transaction)));
303
- const txs = [];
304
- results.forEach((result) => {
305
- if (result.status === 'fulfilled') {
306
- txs.push(result.value);
307
- }
308
- });
309
- return {
310
- txs,
311
- total: txs.length,
312
- };
262
+ return this.roundRobinGetTransactions(params);
313
263
  });
314
264
  }
315
265
  /**
@@ -320,14 +270,8 @@ class Client extends BaseXChainClient {
320
270
  */
321
271
  transfer({ walletIndex, recipient, asset, amount, memo, limit, priorityFee, }) {
322
272
  return __awaiter(this, void 0, void 0, function* () {
323
- const senderKeyPair = this.getPrivateKeyPair(walletIndex || 0);
324
- if (asset && !eqAsset(asset, this.getAssetInfo().asset)) {
325
- // Check if receipt token account is created, otherwise, create it
326
- const mintAddress = new PublicKey(getContractAddressFromAsset(asset));
327
- yield getOrCreateAssociatedTokenAccount(this.connection, senderKeyPair, mintAddress, new PublicKey(recipient));
328
- }
329
- const { rawUnsignedTx } = yield this.prepareTx({
330
- sender: senderKeyPair.publicKey.toBase58(),
273
+ return this.roundRobinTransfer({
274
+ walletIndex,
331
275
  recipient,
332
276
  asset,
333
277
  amount,
@@ -335,9 +279,6 @@ class Client extends BaseXChainClient {
335
279
  limit,
336
280
  priorityFee,
337
281
  });
338
- const transaction = Transaction.from(bs58.decode(rawUnsignedTx));
339
- transaction.sign(senderKeyPair);
340
- return this.broadcastTx(bs58.encode(transaction.serialize()));
341
282
  });
342
283
  }
343
284
  /**
@@ -347,16 +288,7 @@ class Client extends BaseXChainClient {
347
288
  */
348
289
  broadcastTx(txHex) {
349
290
  return __awaiter(this, void 0, void 0, function* () {
350
- try {
351
- const transaction = Transaction.from(bs58.decode(txHex));
352
- return yield this.connection.sendRawTransaction(transaction.serialize());
353
- }
354
- catch (e) {
355
- if (e instanceof SendTransactionError) {
356
- console.log(yield e.getLogs(this.connection));
357
- }
358
- throw Error('Can not broadcast transaction. Unknown error');
359
- }
291
+ return this.roundRobinBroadcastTx(txHex);
360
292
  });
361
293
  }
362
294
  /**
@@ -367,64 +299,15 @@ class Client extends BaseXChainClient {
367
299
  */
368
300
  prepareTx({ sender, recipient, asset, amount, memo, limit, priorityFee, }) {
369
301
  return __awaiter(this, void 0, void 0, function* () {
370
- const transaction = new Transaction();
371
- const fromPubkey = new PublicKey(sender);
372
- const toPubkey = new PublicKey(recipient);
373
- transaction.recentBlockhash = yield this.connection.getLatestBlockhash().then((block) => block.blockhash);
374
- transaction.feePayer = new PublicKey(sender);
375
- if (!asset || eqAsset(asset, this.getAssetInfo().asset)) {
376
- // Native transfer
377
- transaction.add(SystemProgram.transfer({
378
- fromPubkey,
379
- toPubkey,
380
- lamports: amount.amount().toNumber(),
381
- }));
382
- }
383
- else {
384
- // Token transfer
385
- const mintAddress = new PublicKey(getContractAddressFromAsset(asset));
386
- const fromAssociatedAccount = getAssociatedTokenAddressSync(mintAddress, fromPubkey);
387
- let fromTokenAccount;
388
- try {
389
- fromTokenAccount = yield getAccount(this.connection, fromAssociatedAccount);
390
- }
391
- catch (error) {
392
- if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
393
- throw Error('Can not find sender Token account');
394
- }
395
- throw error;
396
- }
397
- const toAssociatedAccount = getAssociatedTokenAddressSync(mintAddress, toPubkey);
398
- let toTokenAccount;
399
- try {
400
- toTokenAccount = yield getAccount(this.connection, toAssociatedAccount);
401
- }
402
- catch (error) {
403
- if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
404
- throw Error('Can not find recipient Token account. Create it first');
405
- }
406
- throw error;
407
- }
408
- transaction.add(createTransferInstruction(fromTokenAccount.address, toTokenAccount.address, fromPubkey, amount.amount().toNumber()));
409
- }
410
- if (memo) {
411
- transaction.add(new TransactionInstruction({
412
- keys: [{ pubkey: fromPubkey, isSigner: true, isWritable: true }],
413
- data: Buffer.from(memo, 'utf-8'),
414
- programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
415
- }));
416
- }
417
- if (priorityFee) {
418
- transaction.add(ComputeBudgetProgram.setComputeUnitPrice({
419
- microLamports: priorityFee.amount().toNumber() / Math.pow(10, 3),
420
- }));
421
- }
422
- if (limit) {
423
- transaction.add(ComputeBudgetProgram.setComputeUnitLimit({
424
- units: limit,
425
- }));
426
- }
427
- return { rawUnsignedTx: bs58.encode(transaction.serialize({ verifySignatures: false })) };
302
+ return this.roundRobinPrepareTx({
303
+ sender,
304
+ recipient,
305
+ asset,
306
+ amount,
307
+ memo,
308
+ limit,
309
+ priorityFee,
310
+ });
428
311
  });
429
312
  }
430
313
  getPrivateKeyPair(index) {
@@ -473,23 +356,28 @@ class Client extends BaseXChainClient {
473
356
  const assetDecimals = tx.meta.postTokenBalances[i].uiTokenAmount.decimals;
474
357
  const mintAddress = tx.meta.postTokenBalances[i].mint;
475
358
  const owner = tx.meta.postTokenBalances[i].owner;
476
- const tokenMetadata = yield fetchDigitalAsset(this.umi, publicKey(mintAddress));
477
- if (owner) {
478
- if (postBalanceAmount > preBalanceAmount) {
479
- to.push({
480
- amount: assetToBase(assetAmount(postBalanceAmount - preBalanceAmount, assetDecimals)),
481
- asset: assetFromStringEx(`SOL.${tokenMetadata.metadata.symbol.trim()}-${mintAddress}`),
482
- to: owner,
483
- });
484
- }
485
- else if (preBalanceAmount > postBalanceAmount) {
486
- from.push({
487
- amount: assetToBase(assetAmount(preBalanceAmount - postBalanceAmount, assetDecimals)),
488
- asset: assetFromStringEx(`SOL.${tokenMetadata.metadata.symbol.trim()}-${mintAddress}`),
489
- from: owner,
490
- });
359
+ try {
360
+ for (const provider of this.providers) {
361
+ const tokenMetadata = yield fetchDigitalAsset(provider.umiProvider, publicKey(mintAddress));
362
+ if (owner) {
363
+ if (postBalanceAmount > preBalanceAmount) {
364
+ to.push({
365
+ amount: assetToBase(assetAmount(postBalanceAmount - preBalanceAmount, assetDecimals)),
366
+ asset: assetFromStringEx(`SOL.${tokenMetadata.metadata.symbol.trim()}-${mintAddress}`),
367
+ to: owner,
368
+ });
369
+ }
370
+ else if (preBalanceAmount > postBalanceAmount) {
371
+ from.push({
372
+ amount: assetToBase(assetAmount(preBalanceAmount - postBalanceAmount, assetDecimals)),
373
+ asset: assetFromStringEx(`SOL.${tokenMetadata.metadata.symbol.trim()}-${mintAddress}`),
374
+ from: owner,
375
+ });
376
+ }
377
+ }
491
378
  }
492
379
  }
380
+ catch (_d) { }
493
381
  }
494
382
  }
495
383
  }
@@ -503,6 +391,324 @@ class Client extends BaseXChainClient {
503
391
  };
504
392
  });
505
393
  }
394
+ /**
395
+ * Retrieves the balance of a given address making a round robin over the providers.
396
+ *
397
+ * @param {Address} address - The address to retrieve the balance for.
398
+ * @param {TokenAsset[]} assets - Assets to retrieve the balance for (optional).
399
+ * @returns {Promise<Balance[]>} An array containing the balance of the address.
400
+ * @throws {Error} if there is no provider able to retrieve the balances
401
+ */
402
+ roundRobinGetBalance(address, assets) {
403
+ return __awaiter(this, void 0, void 0, function* () {
404
+ try {
405
+ for (const provider of this.providers) {
406
+ const balances = [];
407
+ const nativeBalance = yield provider.solanaProvider.getBalance(new PublicKey(address));
408
+ balances.push({
409
+ asset: SOLAsset,
410
+ amount: baseAmount(nativeBalance, SOL_DECIMALS),
411
+ });
412
+ const tokenBalances = yield provider.solanaProvider.getParsedTokenAccountsByOwner(new PublicKey(address), {
413
+ programId: TOKEN_PROGRAM_ID,
414
+ });
415
+ const tokensToRequest = !assets
416
+ ? tokenBalances.value
417
+ : tokenBalances.value.filter((tokenBalance) => {
418
+ const tokenData = tokenBalance.account.data.parsed;
419
+ return (assets.findIndex((asset) => {
420
+ return asset.symbol.toLowerCase().includes(tokenData.info.mint.toLowerCase());
421
+ }) !== -1);
422
+ });
423
+ const mintPublicKeys = tokensToRequest.map((tokenBalance) => {
424
+ const tokenData = tokenBalance.account.data.parsed;
425
+ return publicKey(tokenData.info.mint);
426
+ });
427
+ const assetsData = yield fetchAllDigitalAsset(provider.umiProvider, mintPublicKeys);
428
+ tokenBalances.value.forEach((balance) => {
429
+ const parsedData = balance.account.data.parsed;
430
+ const assetData = assetsData.find((assetData) => assetData.publicKey.toString() === parsedData.info.mint);
431
+ if (assetData) {
432
+ balances.push({
433
+ amount: baseAmount(parsedData.info.tokenAmount.amount, parsedData.info.tokenAmount.decimals),
434
+ asset: assetFromStringEx(`SOL.${assetData.metadata.symbol.trim()}-${parsedData.info.mint}`),
435
+ });
436
+ }
437
+ });
438
+ return balances;
439
+ }
440
+ }
441
+ catch (_a) { }
442
+ throw Error('No provider able to get balances.');
443
+ });
444
+ }
445
+ /**
446
+ * Get transaction fees making a round robin over the providers.
447
+ *
448
+ * @param {TxParams} params - The transaction parameters.
449
+ * @returns {Fees} The average, fast, and fastest fees.
450
+ * @throws {Error} if there is no provider able to retrieve the fees
451
+ */
452
+ roundRobinGetFees(params) {
453
+ return __awaiter(this, void 0, void 0, function* () {
454
+ try {
455
+ for (const provider of this.providers) {
456
+ const sender = Keypair.generate();
457
+ const toPubkey = new PublicKey(params.recipient);
458
+ const transaction = new Transaction();
459
+ transaction.recentBlockhash = yield provider.solanaProvider
460
+ .getLatestBlockhash()
461
+ .then((block) => block.blockhash);
462
+ transaction.feePayer = sender.publicKey;
463
+ let createAccountTxFee = 0;
464
+ if (!params.asset || eqAsset(params.asset, this.getAssetInfo().asset)) {
465
+ // Native transfer
466
+ transaction.add(SystemProgram.transfer({
467
+ fromPubkey: sender.publicKey,
468
+ toPubkey,
469
+ lamports: params.amount.amount().toNumber(),
470
+ }));
471
+ }
472
+ else {
473
+ // Token transfer
474
+ const mintAddress = new PublicKey(getContractAddressFromAsset(params.asset));
475
+ const associatedTokenAddress = getAssociatedTokenAddressSync(mintAddress, toPubkey);
476
+ try {
477
+ yield getAccount(provider.solanaProvider, associatedTokenAddress, undefined, TOKEN_PROGRAM_ID);
478
+ transaction.add(createTransferInstruction(sender.publicKey, // Should be Token account, but as it new KeyPair for estimation, sender public key
479
+ associatedTokenAddress, sender.publicKey, params.amount.amount().toNumber()));
480
+ }
481
+ catch (error) {
482
+ if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
483
+ // recipient token account has to be created
484
+ const dataLength = 165; // Normally used for Token accounts
485
+ createAccountTxFee = yield provider.solanaProvider.getMinimumBalanceForRentExemption(dataLength);
486
+ transaction.add(createTransferInstruction(sender.publicKey, // Should be Token account, but as it new KeyPair for estimation, sender public key
487
+ toPubkey, // Should be Token account, but as recipient token account should be created, recipient public key
488
+ sender.publicKey, params.amount.amount().toNumber()));
489
+ }
490
+ }
491
+ }
492
+ if (params.memo) {
493
+ transaction.add(new TransactionInstruction({
494
+ keys: [{ pubkey: sender.publicKey, isSigner: true, isWritable: true }],
495
+ data: Buffer.from(params.memo, 'utf-8'),
496
+ programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
497
+ }));
498
+ }
499
+ if (params.priorityFee) {
500
+ transaction.add(ComputeBudgetProgram.setComputeUnitPrice({
501
+ microLamports: params.priorityFee.amount().toNumber() / Math.pow(10, 3),
502
+ }));
503
+ }
504
+ if (params.limit) {
505
+ transaction.add(ComputeBudgetProgram.setComputeUnitLimit({
506
+ units: params.limit,
507
+ }));
508
+ }
509
+ const fee = (yield transaction.getEstimatedFee(provider.solanaProvider)) || 0;
510
+ return {
511
+ type: FeeType.FlatFee,
512
+ [FeeOption.Average]: baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
513
+ [FeeOption.Fast]: baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
514
+ [FeeOption.Fastest]: baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
515
+ };
516
+ }
517
+ }
518
+ catch (_a) { }
519
+ throw Error('No provider able to get fees.');
520
+ });
521
+ }
522
+ /**
523
+ * Get the transaction details of a given transaction ID making a round robin over the providers.
524
+ *
525
+ * @param {string} txId The transaction ID.
526
+ * @returns {Tx} The transaction details.
527
+ * @throws {Error} if there is no provider able to retrieve the transaction data
528
+ */
529
+ roundRobinGetTransactionData(txId) {
530
+ return __awaiter(this, void 0, void 0, function* () {
531
+ try {
532
+ for (const provider of this.providers) {
533
+ const transaction = yield provider.solanaProvider.getParsedTransaction(txId);
534
+ return transaction;
535
+ }
536
+ }
537
+ catch (_a) { }
538
+ throw Error('No provider able to get transaction data.');
539
+ });
540
+ }
541
+ /**
542
+ * Get the transaction history of a given address with pagination options making a round robin over the providers.
543
+ *
544
+ * @param {TxHistoryParams} params The options to get transaction history.
545
+ * @returns {TxsPage} The transaction history.
546
+ * @throws {Error} if there is no provider able to retrieve the transactions
547
+ */
548
+ roundRobinGetTransactions(params) {
549
+ return __awaiter(this, void 0, void 0, function* () {
550
+ try {
551
+ for (const provider of this.providers) {
552
+ const signatures = yield provider.solanaProvider.getSignaturesForAddress(new PublicKey((params === null || params === void 0 ? void 0 : params.address) || (yield this.getAddressAsync())));
553
+ const transactions = yield provider.solanaProvider.getParsedTransactions(signatures.map(({ signature }) => signature));
554
+ const results = yield Promise.allSettled(transactions
555
+ .filter((transaction) => !!transaction)
556
+ .map((transaction) => this.parseTransaction(transaction)));
557
+ const txs = [];
558
+ results.forEach((result) => {
559
+ if (result.status === 'fulfilled') {
560
+ txs.push(result.value);
561
+ }
562
+ });
563
+ return {
564
+ txs,
565
+ total: txs.length,
566
+ };
567
+ }
568
+ }
569
+ catch (_a) { }
570
+ throw Error('No provider able to get transactions.');
571
+ });
572
+ }
573
+ /**
574
+ * Transfers SOL or Solana token making a round robin over the providers
575
+ *
576
+ * @param {TxParams} params The transfer options.
577
+ * @returns {TxHash} The transaction hash.
578
+ * @throws {Error} if there is no provider able to make the transfer
579
+ */
580
+ roundRobinTransfer({ walletIndex, recipient, asset, amount, memo, limit, priorityFee, }) {
581
+ return __awaiter(this, void 0, void 0, function* () {
582
+ try {
583
+ const senderKeyPair = this.getPrivateKeyPair(walletIndex || 0);
584
+ for (const provider of this.providers) {
585
+ if (asset && !eqAsset(asset, this.getAssetInfo().asset)) {
586
+ // Check if receipt token account is created, otherwise, create it
587
+ const mintAddress = new PublicKey(getContractAddressFromAsset(asset));
588
+ yield getOrCreateAssociatedTokenAccount(provider.solanaProvider, senderKeyPair, mintAddress, new PublicKey(recipient));
589
+ }
590
+ const { rawUnsignedTx } = yield this.prepareTx({
591
+ sender: senderKeyPair.publicKey.toBase58(),
592
+ recipient,
593
+ asset,
594
+ amount,
595
+ memo,
596
+ limit,
597
+ priorityFee,
598
+ });
599
+ const transaction = Transaction.from(bs58.decode(rawUnsignedTx));
600
+ transaction.sign(senderKeyPair);
601
+ return this.broadcastTx(bs58.encode(transaction.serialize()));
602
+ }
603
+ }
604
+ catch (_a) { }
605
+ throw Error('No provider able to transfer.');
606
+ });
607
+ }
608
+ /**
609
+ * Broadcast a transaction to the network making a round robin over the providers
610
+ *
611
+ * @param {string} txHex Raw transaction to broadcast
612
+ * @returns {TxHash} The hash of the transaction broadcasted
613
+ * @throws {Error} if there is no provider able to broadcast transaction
614
+ */
615
+ roundRobinBroadcastTx(txHex) {
616
+ return __awaiter(this, void 0, void 0, function* () {
617
+ try {
618
+ for (const provider of this.providers) {
619
+ try {
620
+ const transaction = Transaction.from(bs58.decode(txHex));
621
+ return yield provider.solanaProvider.sendRawTransaction(transaction.serialize());
622
+ }
623
+ catch (e) {
624
+ if (e instanceof SendTransactionError) {
625
+ console.log(yield e.getLogs(provider.solanaProvider));
626
+ }
627
+ throw Error('Can not broadcast transaction. Unknown error');
628
+ }
629
+ }
630
+ }
631
+ catch (_a) { }
632
+ throw Error('No provider able to broadcast transaction.');
633
+ });
634
+ }
635
+ /**
636
+ * Prepares a transaction for transfer making round robin over the providers.
637
+ *
638
+ * @param {TxParams&Address} params - The transfer options.
639
+ * @returns {Promise<PreparedTx>} The raw unsigned transaction.
640
+ * @throws {Error} if there is no provider able to prepare transaction
641
+ */
642
+ roundRobinPrepareTx({ sender, recipient, asset, amount, memo, limit, priorityFee, }) {
643
+ return __awaiter(this, void 0, void 0, function* () {
644
+ try {
645
+ for (const provider of this.providers) {
646
+ const transaction = new Transaction();
647
+ const fromPubkey = new PublicKey(sender);
648
+ const toPubkey = new PublicKey(recipient);
649
+ transaction.recentBlockhash = yield provider.solanaProvider
650
+ .getLatestBlockhash()
651
+ .then((block) => block.blockhash);
652
+ transaction.feePayer = new PublicKey(sender);
653
+ if (!asset || eqAsset(asset, this.getAssetInfo().asset)) {
654
+ // Native transfer
655
+ transaction.add(SystemProgram.transfer({
656
+ fromPubkey,
657
+ toPubkey,
658
+ lamports: amount.amount().toNumber(),
659
+ }));
660
+ }
661
+ else {
662
+ // Token transfer
663
+ const mintAddress = new PublicKey(getContractAddressFromAsset(asset));
664
+ const fromAssociatedAccount = getAssociatedTokenAddressSync(mintAddress, fromPubkey);
665
+ let fromTokenAccount;
666
+ try {
667
+ fromTokenAccount = yield getAccount(provider.solanaProvider, fromAssociatedAccount);
668
+ }
669
+ catch (error) {
670
+ if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
671
+ throw Error('Can not find sender Token account');
672
+ }
673
+ throw error;
674
+ }
675
+ const toAssociatedAccount = getAssociatedTokenAddressSync(mintAddress, toPubkey);
676
+ let toTokenAccount;
677
+ try {
678
+ toTokenAccount = yield getAccount(provider.solanaProvider, toAssociatedAccount);
679
+ }
680
+ catch (error) {
681
+ if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
682
+ throw Error('Can not find recipient Token account. Create it first');
683
+ }
684
+ throw error;
685
+ }
686
+ transaction.add(createTransferInstruction(fromTokenAccount.address, toTokenAccount.address, fromPubkey, amount.amount().toNumber()));
687
+ }
688
+ if (memo) {
689
+ transaction.add(new TransactionInstruction({
690
+ keys: [{ pubkey: fromPubkey, isSigner: true, isWritable: true }],
691
+ data: Buffer.from(memo, 'utf-8'),
692
+ programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
693
+ }));
694
+ }
695
+ if (priorityFee) {
696
+ transaction.add(ComputeBudgetProgram.setComputeUnitPrice({
697
+ microLamports: priorityFee.amount().toNumber() / Math.pow(10, 3),
698
+ }));
699
+ }
700
+ if (limit) {
701
+ transaction.add(ComputeBudgetProgram.setComputeUnitLimit({
702
+ units: limit,
703
+ }));
704
+ }
705
+ return { rawUnsignedTx: bs58.encode(transaction.serialize({ verifySignatures: false })) };
706
+ }
707
+ }
708
+ catch (_a) { }
709
+ throw Error('No provider able to prepare transaction');
710
+ });
711
+ }
506
712
  }
507
713
 
508
714
  export { Client, SOLAsset, SOLChain, SOL_DECIMALS, defaultSolanaParams };