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