@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.js CHANGED
@@ -12,6 +12,10 @@ var xchainUtil = require('@xchainjs/xchain-util');
12
12
  var bs58 = require('bs58');
13
13
  var microEd25519Hdkey = require('micro-ed25519-hdkey');
14
14
 
15
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
+
17
+ var bs58__default = /*#__PURE__*/_interopDefault(bs58);
18
+
15
19
  /******************************************************************************
16
20
  Copyright (c) Microsoft Corporation.
17
21
 
@@ -26,7 +30,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
26
30
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
27
31
  PERFORMANCE OF THIS SOFTWARE.
28
32
  ***************************************************************************** */
29
- /* global Reflect, Promise, SuppressedError, Symbol */
33
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
30
34
 
31
35
 
32
36
  function __awaiter(thisArg, _arguments, P, generator) {
@@ -89,8 +93,55 @@ class Client extends xchainClient.BaseXChainClient {
89
93
  constructor(params = defaultSolanaParams) {
90
94
  super(SOLChain, Object.assign(Object.assign({}, defaultSolanaParams), params));
91
95
  this.explorerProviders = params.explorerProviders;
92
- this.connection = new web3_js.Connection(web3_js.clusterApiUrl(getSolanaNetwork(this.getNetwork())));
93
- 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
+ }
94
145
  }
95
146
  /**
96
147
  * Get information about the native asset of the Solana.
@@ -175,39 +226,7 @@ class Client extends xchainClient.BaseXChainClient {
175
226
  */
176
227
  getBalance(address, assets) {
177
228
  return __awaiter(this, void 0, void 0, function* () {
178
- const balances = [];
179
- const nativeBalance = yield this.connection.getBalance(new web3_js.PublicKey(address));
180
- balances.push({
181
- asset: SOLAsset,
182
- amount: xchainUtil.baseAmount(nativeBalance, SOL_DECIMALS),
183
- });
184
- const tokenBalances = yield this.connection.getParsedTokenAccountsByOwner(new web3_js.PublicKey(address), {
185
- programId: splToken.TOKEN_PROGRAM_ID,
186
- });
187
- const tokensToRequest = !assets
188
- ? tokenBalances.value
189
- : tokenBalances.value.filter((tokenBalance) => {
190
- const tokenData = tokenBalance.account.data.parsed;
191
- return (assets.findIndex((asset) => {
192
- return asset.symbol.toLowerCase().includes(tokenData.info.mint.toLowerCase());
193
- }) !== -1);
194
- });
195
- const mintPublicKeys = tokensToRequest.map((tokenBalance) => {
196
- const tokenData = tokenBalance.account.data.parsed;
197
- return umi.publicKey(tokenData.info.mint);
198
- });
199
- const assetsData = yield mplTokenMetadata.fetchAllDigitalAsset(this.umi, mintPublicKeys);
200
- tokenBalances.value.forEach((balance) => {
201
- const parsedData = balance.account.data.parsed;
202
- const assetData = assetsData.find((assetData) => assetData.publicKey.toString() === parsedData.info.mint);
203
- if (assetData) {
204
- balances.push({
205
- amount: xchainUtil.baseAmount(parsedData.info.tokenAmount.amount, parsedData.info.tokenAmount.decimals),
206
- asset: xchainUtil.assetFromStringEx(`SOL.${assetData.metadata.symbol.trim()}-${parsedData.info.mint}`),
207
- });
208
- }
209
- });
210
- return balances;
229
+ return this.roundRobinGetBalance(address, assets);
211
230
  });
212
231
  }
213
232
  /**
@@ -221,64 +240,7 @@ class Client extends xchainClient.BaseXChainClient {
221
240
  return __awaiter(this, void 0, void 0, function* () {
222
241
  if (!params)
223
242
  throw new Error('Params need to be passed');
224
- const sender = web3_js.Keypair.generate();
225
- const toPubkey = new web3_js.PublicKey(params.recipient);
226
- const transaction = new web3_js.Transaction();
227
- transaction.recentBlockhash = yield this.connection.getLatestBlockhash().then((block) => block.blockhash);
228
- transaction.feePayer = sender.publicKey;
229
- let createAccountTxFee = 0;
230
- if (!params.asset || xchainUtil.eqAsset(params.asset, this.getAssetInfo().asset)) {
231
- // Native transfer
232
- transaction.add(web3_js.SystemProgram.transfer({
233
- fromPubkey: sender.publicKey,
234
- toPubkey,
235
- lamports: params.amount.amount().toNumber(),
236
- }));
237
- }
238
- else {
239
- // Token transfer
240
- const mintAddress = new web3_js.PublicKey(xchainUtil.getContractAddressFromAsset(params.asset));
241
- const associatedTokenAddress = splToken.getAssociatedTokenAddressSync(mintAddress, toPubkey);
242
- try {
243
- yield splToken.getAccount(this.connection, associatedTokenAddress, undefined, splToken.TOKEN_PROGRAM_ID);
244
- transaction.add(splToken.createTransferInstruction(sender.publicKey, // Should be Token account, but as it new KeyPair for estimation, sender public key
245
- associatedTokenAddress, sender.publicKey, params.amount.amount().toNumber()));
246
- }
247
- catch (error) {
248
- if (error instanceof splToken.TokenAccountNotFoundError || error instanceof splToken.TokenInvalidAccountOwnerError) {
249
- // recipient token account has to be created
250
- const dataLength = 165; // Normally used for Token accounts
251
- createAccountTxFee = yield this.connection.getMinimumBalanceForRentExemption(dataLength);
252
- transaction.add(splToken.createTransferInstruction(sender.publicKey, // Should be Token account, but as it new KeyPair for estimation, sender public key
253
- toPubkey, // Should be Token account, but as recipient token account should be created, recipient public key
254
- sender.publicKey, params.amount.amount().toNumber()));
255
- }
256
- }
257
- }
258
- if (params.memo) {
259
- transaction.add(new web3_js.TransactionInstruction({
260
- keys: [{ pubkey: sender.publicKey, isSigner: true, isWritable: true }],
261
- data: Buffer.from(params.memo, 'utf-8'),
262
- programId: new web3_js.PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
263
- }));
264
- }
265
- if (params.priorityFee) {
266
- transaction.add(web3_js.ComputeBudgetProgram.setComputeUnitPrice({
267
- microLamports: params.priorityFee.amount().toNumber() / Math.pow(10, 3),
268
- }));
269
- }
270
- if (params.limit) {
271
- transaction.add(web3_js.ComputeBudgetProgram.setComputeUnitLimit({
272
- units: params.limit,
273
- }));
274
- }
275
- const fee = (yield transaction.getEstimatedFee(this.connection)) || 0;
276
- return {
277
- type: xchainClient.FeeType.FlatFee,
278
- [xchainClient.FeeOption.Average]: xchainUtil.baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
279
- [xchainClient.FeeOption.Fast]: xchainUtil.baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
280
- [xchainClient.FeeOption.Fastest]: xchainUtil.baseAmount(fee + createAccountTxFee, SOL_DECIMALS),
281
- };
243
+ return this.roundRobinGetFees(params);
282
244
  });
283
245
  }
284
246
  /**
@@ -289,29 +251,21 @@ class Client extends xchainClient.BaseXChainClient {
289
251
  */
290
252
  getTransactionData(txId) {
291
253
  return __awaiter(this, void 0, void 0, function* () {
292
- const transaction = yield this.connection.getParsedTransaction(txId);
254
+ const transaction = yield this.roundRobinGetTransactionData(txId);
293
255
  if (!transaction)
294
256
  throw Error('Can not find transaction');
295
257
  return this.parseTransaction(transaction);
296
258
  });
297
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
+ */
298
266
  getTransactions(params) {
299
267
  return __awaiter(this, void 0, void 0, function* () {
300
- const signatures = yield this.connection.getSignaturesForAddress(new web3_js.PublicKey((params === null || params === void 0 ? void 0 : params.address) || (yield this.getAddressAsync())));
301
- const transactions = yield this.connection.getParsedTransactions(signatures.map(({ signature }) => signature));
302
- const results = yield Promise.allSettled(transactions
303
- .filter((transaction) => !!transaction)
304
- .map((transaction) => this.parseTransaction(transaction)));
305
- const txs = [];
306
- results.forEach((result) => {
307
- if (result.status === 'fulfilled') {
308
- txs.push(result.value);
309
- }
310
- });
311
- return {
312
- txs,
313
- total: txs.length,
314
- };
268
+ return this.roundRobinGetTransactions(params);
315
269
  });
316
270
  }
317
271
  /**
@@ -322,14 +276,8 @@ class Client extends xchainClient.BaseXChainClient {
322
276
  */
323
277
  transfer({ walletIndex, recipient, asset, amount, memo, limit, priorityFee, }) {
324
278
  return __awaiter(this, void 0, void 0, function* () {
325
- const senderKeyPair = this.getPrivateKeyPair(walletIndex || 0);
326
- if (asset && !xchainUtil.eqAsset(asset, this.getAssetInfo().asset)) {
327
- // Check if receipt token account is created, otherwise, create it
328
- const mintAddress = new web3_js.PublicKey(xchainUtil.getContractAddressFromAsset(asset));
329
- yield splToken.getOrCreateAssociatedTokenAccount(this.connection, senderKeyPair, mintAddress, new web3_js.PublicKey(recipient));
330
- }
331
- const { rawUnsignedTx } = yield this.prepareTx({
332
- sender: senderKeyPair.publicKey.toBase58(),
279
+ return this.roundRobinTransfer({
280
+ walletIndex,
333
281
  recipient,
334
282
  asset,
335
283
  amount,
@@ -337,9 +285,6 @@ class Client extends xchainClient.BaseXChainClient {
337
285
  limit,
338
286
  priorityFee,
339
287
  });
340
- const transaction = web3_js.Transaction.from(bs58.decode(rawUnsignedTx));
341
- transaction.sign(senderKeyPair);
342
- return this.broadcastTx(bs58.encode(transaction.serialize()));
343
288
  });
344
289
  }
345
290
  /**
@@ -349,16 +294,7 @@ class Client extends xchainClient.BaseXChainClient {
349
294
  */
350
295
  broadcastTx(txHex) {
351
296
  return __awaiter(this, void 0, void 0, function* () {
352
- try {
353
- const transaction = web3_js.Transaction.from(bs58.decode(txHex));
354
- return yield this.connection.sendRawTransaction(transaction.serialize());
355
- }
356
- catch (e) {
357
- if (e instanceof web3_js.SendTransactionError) {
358
- console.log(yield e.getLogs(this.connection));
359
- }
360
- throw Error('Can not broadcast transaction. Unknown error');
361
- }
297
+ return this.roundRobinBroadcastTx(txHex);
362
298
  });
363
299
  }
364
300
  /**
@@ -369,64 +305,15 @@ class Client extends xchainClient.BaseXChainClient {
369
305
  */
370
306
  prepareTx({ sender, recipient, asset, amount, memo, limit, priorityFee, }) {
371
307
  return __awaiter(this, void 0, void 0, function* () {
372
- const transaction = new web3_js.Transaction();
373
- const fromPubkey = new web3_js.PublicKey(sender);
374
- const toPubkey = new web3_js.PublicKey(recipient);
375
- transaction.recentBlockhash = yield this.connection.getLatestBlockhash().then((block) => block.blockhash);
376
- transaction.feePayer = new web3_js.PublicKey(sender);
377
- if (!asset || xchainUtil.eqAsset(asset, this.getAssetInfo().asset)) {
378
- // Native transfer
379
- transaction.add(web3_js.SystemProgram.transfer({
380
- fromPubkey,
381
- toPubkey,
382
- lamports: amount.amount().toNumber(),
383
- }));
384
- }
385
- else {
386
- // Token transfer
387
- const mintAddress = new web3_js.PublicKey(xchainUtil.getContractAddressFromAsset(asset));
388
- const fromAssociatedAccount = splToken.getAssociatedTokenAddressSync(mintAddress, fromPubkey);
389
- let fromTokenAccount;
390
- try {
391
- fromTokenAccount = yield splToken.getAccount(this.connection, fromAssociatedAccount);
392
- }
393
- catch (error) {
394
- if (error instanceof splToken.TokenAccountNotFoundError || error instanceof splToken.TokenInvalidAccountOwnerError) {
395
- throw Error('Can not find sender Token account');
396
- }
397
- throw error;
398
- }
399
- const toAssociatedAccount = splToken.getAssociatedTokenAddressSync(mintAddress, toPubkey);
400
- let toTokenAccount;
401
- try {
402
- toTokenAccount = yield splToken.getAccount(this.connection, toAssociatedAccount);
403
- }
404
- catch (error) {
405
- if (error instanceof splToken.TokenAccountNotFoundError || error instanceof splToken.TokenInvalidAccountOwnerError) {
406
- throw Error('Can not find recipient Token account. Create it first');
407
- }
408
- throw error;
409
- }
410
- transaction.add(splToken.createTransferInstruction(fromTokenAccount.address, toTokenAccount.address, fromPubkey, amount.amount().toNumber()));
411
- }
412
- if (memo) {
413
- transaction.add(new web3_js.TransactionInstruction({
414
- keys: [{ pubkey: fromPubkey, isSigner: true, isWritable: true }],
415
- data: Buffer.from(memo, 'utf-8'),
416
- programId: new web3_js.PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
417
- }));
418
- }
419
- if (priorityFee) {
420
- transaction.add(web3_js.ComputeBudgetProgram.setComputeUnitPrice({
421
- microLamports: priorityFee.amount().toNumber() / Math.pow(10, 3),
422
- }));
423
- }
424
- if (limit) {
425
- transaction.add(web3_js.ComputeBudgetProgram.setComputeUnitLimit({
426
- units: limit,
427
- }));
428
- }
429
- return { rawUnsignedTx: bs58.encode(transaction.serialize({ verifySignatures: false })) };
308
+ return this.roundRobinPrepareTx({
309
+ sender,
310
+ recipient,
311
+ asset,
312
+ amount,
313
+ memo,
314
+ limit,
315
+ priorityFee,
316
+ });
430
317
  });
431
318
  }
432
319
  getPrivateKeyPair(index) {
@@ -475,23 +362,28 @@ class Client extends xchainClient.BaseXChainClient {
475
362
  const assetDecimals = tx.meta.postTokenBalances[i].uiTokenAmount.decimals;
476
363
  const mintAddress = tx.meta.postTokenBalances[i].mint;
477
364
  const owner = tx.meta.postTokenBalances[i].owner;
478
- const tokenMetadata = yield mplTokenMetadata.fetchDigitalAsset(this.umi, umi.publicKey(mintAddress));
479
- if (owner) {
480
- if (postBalanceAmount > preBalanceAmount) {
481
- to.push({
482
- amount: xchainUtil.assetToBase(xchainUtil.assetAmount(postBalanceAmount - preBalanceAmount, assetDecimals)),
483
- asset: xchainUtil.assetFromStringEx(`SOL.${tokenMetadata.metadata.symbol.trim()}-${mintAddress}`),
484
- to: owner,
485
- });
486
- }
487
- else if (preBalanceAmount > postBalanceAmount) {
488
- from.push({
489
- amount: xchainUtil.assetToBase(xchainUtil.assetAmount(preBalanceAmount - postBalanceAmount, assetDecimals)),
490
- asset: xchainUtil.assetFromStringEx(`SOL.${tokenMetadata.metadata.symbol.trim()}-${mintAddress}`),
491
- from: owner,
492
- });
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
+ }
493
384
  }
494
385
  }
386
+ catch (_d) { }
495
387
  }
496
388
  }
497
389
  }
@@ -505,6 +397,324 @@ class Client extends xchainClient.BaseXChainClient {
505
397
  };
506
398
  });
507
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
+ }
508
718
  }
509
719
 
510
720
  exports.Client = Client;