@xchainjs/xchain-solana 1.0.9 → 1.1.1

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/client.d.ts CHANGED
@@ -1,9 +1,15 @@
1
+ import { Umi } from '@metaplex-foundation/umi';
2
+ import { Connection } from '@solana/web3.js';
1
3
  import { AssetInfo, BaseXChainClient, Fees, Network, PreparedTx, TxHash, TxHistoryParams } from '@xchainjs/xchain-client';
2
4
  import { Address, TokenAsset } from '@xchainjs/xchain-util';
3
5
  import { Balance, SOLClientParams, Tx, TxParams, TxsPage } from './types';
6
+ type Providers = {
7
+ solanaProvider: Connection;
8
+ umiProvider: Umi;
9
+ };
4
10
  export declare class Client extends BaseXChainClient {
5
11
  private explorerProviders;
6
- private providers;
12
+ protected providers: Providers[];
7
13
  private clientUrls?;
8
14
  constructor(params?: SOLClientParams);
9
15
  /**
@@ -174,3 +180,4 @@ export declare class Client extends BaseXChainClient {
174
180
  */
175
181
  private roundRobinPrepareTx;
176
182
  }
183
+ export {};
@@ -0,0 +1,28 @@
1
+ import SolanaLedgerApp from '@ledgerhq/hw-app-solana';
2
+ import Transport from '@ledgerhq/hw-transport';
3
+ import { Address } from '@xchainjs/xchain-util';
4
+ import { Client } from './client';
5
+ import { SOLClientParams, TxParams } from './types';
6
+ export type SOLClientLedgerParams = SOLClientParams & {
7
+ transport: Transport;
8
+ };
9
+ /**
10
+ * Custom Tron Ledger client extending the base Tron client
11
+ */
12
+ declare class ClientLedger extends Client {
13
+ private transport;
14
+ private ledgerApp;
15
+ constructor(params: SOLClientLedgerParams);
16
+ getApp(): SolanaLedgerApp;
17
+ getAddress(): string;
18
+ getDerivationPath(index?: number): string;
19
+ getAddressAsync(index?: number, verify?: boolean): Promise<Address>;
20
+ /**
21
+ * Transfers SOL or Solana token using ledger
22
+ *
23
+ * @param {TxParams} params The transfer options.
24
+ * @returns {TxHash} The transaction hash.
25
+ */
26
+ transfer({ walletIndex, recipient, asset, amount, memo, limit, priorityFee, allowOwnerOffCurve, }: TxParams): Promise<string>;
27
+ }
28
+ export { ClientLedger };
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { Client } from './client';
2
+ export { ClientLedger } from './clientLedger';
2
3
  export * from './types';
3
4
  export * from './const';
package/lib/index.esm.js CHANGED
@@ -9,6 +9,7 @@ import { getSeed } from '@xchainjs/xchain-crypto';
9
9
  import { AssetType, baseAmount, assetToBase, assetAmount, assetFromStringEx, eqAsset, getContractAddressFromAsset } from '@xchainjs/xchain-util';
10
10
  import bs58 from 'bs58';
11
11
  import slip10 from 'micro-key-producer/slip10.js';
12
+ import SolanaLedgerApp from '@ledgerhq/hw-app-solana';
12
13
 
13
14
  /******************************************************************************
14
15
  Copyright (c) Microsoft Corporation.
@@ -708,10 +709,140 @@ class Client extends BaseXChainClient {
708
709
  return { rawUnsignedTx: bs58.encode(transaction.serialize({ verifySignatures: false })) };
709
710
  }
710
711
  }
711
- catch (_b) { }
712
+ catch (error) {
713
+ console.log('prepareTx error', error);
714
+ }
712
715
  throw Error('No provider able to prepare transaction');
713
716
  });
714
717
  }
715
718
  }
716
719
 
717
- export { Client, SOLAsset, SOLChain, SOL_DECIMALS, defaultSolanaParams };
720
+ /**
721
+ * Custom Tron Ledger client extending the base Tron client
722
+ */
723
+ class ClientLedger extends Client {
724
+ constructor(params) {
725
+ const clientParams = Object.assign(Object.assign({}, defaultSolanaParams), params);
726
+ super(clientParams);
727
+ this.transport = params.transport;
728
+ this.ledgerApp = new SolanaLedgerApp(this.transport);
729
+ }
730
+ getApp() {
731
+ if (this.ledgerApp)
732
+ return this.ledgerApp;
733
+ this.ledgerApp = new SolanaLedgerApp(this.transport);
734
+ return this.ledgerApp;
735
+ }
736
+ // Get the current address synchronously - not supported for Ledger Client
737
+ getAddress() {
738
+ throw Error('Sync method not supported for Ledger');
739
+ }
740
+ // get derivation path for ledger
741
+ getDerivationPath(index = 0) {
742
+ const derivationPath = this.getFullDerivationPath(index);
743
+ return derivationPath.replace(/^m\//, '');
744
+ }
745
+ // Get the current address asynchronously
746
+ getAddressAsync() {
747
+ return __awaiter(this, arguments, void 0, function* (index = 0, verify = false) {
748
+ const derivationPath = this.getDerivationPath(index);
749
+ const result = yield this.getApp().getAddress(derivationPath, verify);
750
+ const publicKey = new PublicKey(Buffer.from(result.address));
751
+ return publicKey.toBase58(); // return base58 solana address format
752
+ });
753
+ }
754
+ /**
755
+ * Transfers SOL or Solana token using ledger
756
+ *
757
+ * @param {TxParams} params The transfer options.
758
+ * @returns {TxHash} The transaction hash.
759
+ */
760
+ transfer(_a) {
761
+ return __awaiter(this, arguments, void 0, function* ({ walletIndex = 0, recipient, asset, amount, memo, limit, priorityFee, allowOwnerOffCurve, }) {
762
+ const sender = yield this.getAddressAsync(walletIndex);
763
+ try {
764
+ for (const provider of this.providers) {
765
+ const transaction = new Transaction();
766
+ const fromPubkey = new PublicKey(sender);
767
+ const toPubkey = new PublicKey(recipient);
768
+ transaction.recentBlockhash = yield provider.solanaProvider
769
+ .getLatestBlockhash()
770
+ .then((block) => block.blockhash);
771
+ transaction.feePayer = new PublicKey(sender);
772
+ if (!asset || eqAsset(asset, this.getAssetInfo().asset)) {
773
+ // Native transfer
774
+ transaction.add(SystemProgram.transfer({
775
+ fromPubkey,
776
+ toPubkey,
777
+ lamports: amount.amount().toNumber(),
778
+ }));
779
+ }
780
+ else {
781
+ // Token transfer
782
+ const mintAddress = new PublicKey(getContractAddressFromAsset(asset));
783
+ const fromAssociatedAccount = getAssociatedTokenAddressSync(mintAddress, fromPubkey);
784
+ let fromTokenAccount;
785
+ try {
786
+ fromTokenAccount = yield getAccount(provider.solanaProvider, fromAssociatedAccount);
787
+ }
788
+ catch (error) {
789
+ if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
790
+ throw Error('Can not find sender Token account');
791
+ }
792
+ throw error;
793
+ }
794
+ const toAssociatedAccount = getAssociatedTokenAddressSync(mintAddress, toPubkey, allowOwnerOffCurve);
795
+ let toTokenAccount;
796
+ try {
797
+ toTokenAccount = yield getAccount(provider.solanaProvider, toAssociatedAccount);
798
+ }
799
+ catch (error) {
800
+ if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
801
+ throw Error('Can not find recipient Token account. Create it first');
802
+ }
803
+ throw error;
804
+ }
805
+ transaction.add(createTransferInstruction(fromTokenAccount.address, toTokenAccount.address, fromPubkey, amount.amount().toNumber()));
806
+ }
807
+ if (memo) {
808
+ transaction.add(new TransactionInstruction({
809
+ keys: [{ pubkey: fromPubkey, isSigner: true, isWritable: true }],
810
+ data: Buffer.from(memo, 'utf-8'),
811
+ programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
812
+ }));
813
+ }
814
+ if (priorityFee) {
815
+ transaction.add(ComputeBudgetProgram.setComputeUnitPrice({
816
+ microLamports: priorityFee.amount().toNumber() / Math.pow(10, 3),
817
+ }));
818
+ }
819
+ if (limit) {
820
+ transaction.add(ComputeBudgetProgram.setComputeUnitLimit({
821
+ units: limit,
822
+ }));
823
+ }
824
+ const derivationPath = this.getDerivationPath(walletIndex);
825
+ const { signature } = yield this.getApp().signTransaction(derivationPath, transaction.serializeMessage());
826
+ if (!signature) {
827
+ throw new Error('failed signing tx by ledger');
828
+ }
829
+ // Attach Ledger signature
830
+ transaction.addSignature(new PublicKey(sender), Buffer.from(signature));
831
+ // Verify it's valid (optional, sanity check)
832
+ if (!transaction.verifySignatures()) {
833
+ throw new Error('Signature verification failed');
834
+ }
835
+ // Serialize the signed transaction
836
+ const signedTx = transaction.serialize();
837
+ return this.broadcastTx(bs58.encode(signedTx));
838
+ }
839
+ }
840
+ catch (error) {
841
+ console.log('Transfer error', error);
842
+ }
843
+ throw Error('No provider able to transfer.');
844
+ });
845
+ }
846
+ }
847
+
848
+ export { Client, ClientLedger, SOLAsset, SOLChain, SOL_DECIMALS, defaultSolanaParams };
package/lib/index.js CHANGED
@@ -11,11 +11,13 @@ var xchainCrypto = require('@xchainjs/xchain-crypto');
11
11
  var xchainUtil = require('@xchainjs/xchain-util');
12
12
  var bs58 = require('bs58');
13
13
  var slip10 = require('micro-key-producer/slip10.js');
14
+ var SolanaLedgerApp = require('@ledgerhq/hw-app-solana');
14
15
 
15
16
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
17
 
17
18
  var bs58__default = /*#__PURE__*/_interopDefault(bs58);
18
19
  var slip10__default = /*#__PURE__*/_interopDefault(slip10);
20
+ var SolanaLedgerApp__default = /*#__PURE__*/_interopDefault(SolanaLedgerApp);
19
21
 
20
22
  /******************************************************************************
21
23
  Copyright (c) Microsoft Corporation.
@@ -715,13 +717,144 @@ class Client extends xchainClient.BaseXChainClient {
715
717
  return { rawUnsignedTx: bs58__default.default.encode(transaction.serialize({ verifySignatures: false })) };
716
718
  }
717
719
  }
718
- catch (_b) { }
720
+ catch (error) {
721
+ console.log('prepareTx error', error);
722
+ }
719
723
  throw Error('No provider able to prepare transaction');
720
724
  });
721
725
  }
722
726
  }
723
727
 
728
+ /**
729
+ * Custom Tron Ledger client extending the base Tron client
730
+ */
731
+ class ClientLedger extends Client {
732
+ constructor(params) {
733
+ const clientParams = Object.assign(Object.assign({}, defaultSolanaParams), params);
734
+ super(clientParams);
735
+ this.transport = params.transport;
736
+ this.ledgerApp = new SolanaLedgerApp__default.default(this.transport);
737
+ }
738
+ getApp() {
739
+ if (this.ledgerApp)
740
+ return this.ledgerApp;
741
+ this.ledgerApp = new SolanaLedgerApp__default.default(this.transport);
742
+ return this.ledgerApp;
743
+ }
744
+ // Get the current address synchronously - not supported for Ledger Client
745
+ getAddress() {
746
+ throw Error('Sync method not supported for Ledger');
747
+ }
748
+ // get derivation path for ledger
749
+ getDerivationPath(index = 0) {
750
+ const derivationPath = this.getFullDerivationPath(index);
751
+ return derivationPath.replace(/^m\//, '');
752
+ }
753
+ // Get the current address asynchronously
754
+ getAddressAsync() {
755
+ return __awaiter(this, arguments, void 0, function* (index = 0, verify = false) {
756
+ const derivationPath = this.getDerivationPath(index);
757
+ const result = yield this.getApp().getAddress(derivationPath, verify);
758
+ const publicKey = new web3_js.PublicKey(Buffer.from(result.address));
759
+ return publicKey.toBase58(); // return base58 solana address format
760
+ });
761
+ }
762
+ /**
763
+ * Transfers SOL or Solana token using ledger
764
+ *
765
+ * @param {TxParams} params The transfer options.
766
+ * @returns {TxHash} The transaction hash.
767
+ */
768
+ transfer(_a) {
769
+ return __awaiter(this, arguments, void 0, function* ({ walletIndex = 0, recipient, asset, amount, memo, limit, priorityFee, allowOwnerOffCurve, }) {
770
+ const sender = yield this.getAddressAsync(walletIndex);
771
+ try {
772
+ for (const provider of this.providers) {
773
+ const transaction = new web3_js.Transaction();
774
+ const fromPubkey = new web3_js.PublicKey(sender);
775
+ const toPubkey = new web3_js.PublicKey(recipient);
776
+ transaction.recentBlockhash = yield provider.solanaProvider
777
+ .getLatestBlockhash()
778
+ .then((block) => block.blockhash);
779
+ transaction.feePayer = new web3_js.PublicKey(sender);
780
+ if (!asset || xchainUtil.eqAsset(asset, this.getAssetInfo().asset)) {
781
+ // Native transfer
782
+ transaction.add(web3_js.SystemProgram.transfer({
783
+ fromPubkey,
784
+ toPubkey,
785
+ lamports: amount.amount().toNumber(),
786
+ }));
787
+ }
788
+ else {
789
+ // Token transfer
790
+ const mintAddress = new web3_js.PublicKey(xchainUtil.getContractAddressFromAsset(asset));
791
+ const fromAssociatedAccount = splToken.getAssociatedTokenAddressSync(mintAddress, fromPubkey);
792
+ let fromTokenAccount;
793
+ try {
794
+ fromTokenAccount = yield splToken.getAccount(provider.solanaProvider, fromAssociatedAccount);
795
+ }
796
+ catch (error) {
797
+ if (error instanceof splToken.TokenAccountNotFoundError || error instanceof splToken.TokenInvalidAccountOwnerError) {
798
+ throw Error('Can not find sender Token account');
799
+ }
800
+ throw error;
801
+ }
802
+ const toAssociatedAccount = splToken.getAssociatedTokenAddressSync(mintAddress, toPubkey, allowOwnerOffCurve);
803
+ let toTokenAccount;
804
+ try {
805
+ toTokenAccount = yield splToken.getAccount(provider.solanaProvider, toAssociatedAccount);
806
+ }
807
+ catch (error) {
808
+ if (error instanceof splToken.TokenAccountNotFoundError || error instanceof splToken.TokenInvalidAccountOwnerError) {
809
+ throw Error('Can not find recipient Token account. Create it first');
810
+ }
811
+ throw error;
812
+ }
813
+ transaction.add(splToken.createTransferInstruction(fromTokenAccount.address, toTokenAccount.address, fromPubkey, amount.amount().toNumber()));
814
+ }
815
+ if (memo) {
816
+ transaction.add(new web3_js.TransactionInstruction({
817
+ keys: [{ pubkey: fromPubkey, isSigner: true, isWritable: true }],
818
+ data: Buffer.from(memo, 'utf-8'),
819
+ programId: new web3_js.PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
820
+ }));
821
+ }
822
+ if (priorityFee) {
823
+ transaction.add(web3_js.ComputeBudgetProgram.setComputeUnitPrice({
824
+ microLamports: priorityFee.amount().toNumber() / Math.pow(10, 3),
825
+ }));
826
+ }
827
+ if (limit) {
828
+ transaction.add(web3_js.ComputeBudgetProgram.setComputeUnitLimit({
829
+ units: limit,
830
+ }));
831
+ }
832
+ const derivationPath = this.getDerivationPath(walletIndex);
833
+ const { signature } = yield this.getApp().signTransaction(derivationPath, transaction.serializeMessage());
834
+ if (!signature) {
835
+ throw new Error('failed signing tx by ledger');
836
+ }
837
+ // Attach Ledger signature
838
+ transaction.addSignature(new web3_js.PublicKey(sender), Buffer.from(signature));
839
+ // Verify it's valid (optional, sanity check)
840
+ if (!transaction.verifySignatures()) {
841
+ throw new Error('Signature verification failed');
842
+ }
843
+ // Serialize the signed transaction
844
+ const signedTx = transaction.serialize();
845
+ return this.broadcastTx(bs58__default.default.encode(signedTx));
846
+ }
847
+ }
848
+ catch (error) {
849
+ console.log('Transfer error', error);
850
+ }
851
+ throw Error('No provider able to transfer.');
852
+ });
853
+ }
854
+ }
855
+
724
856
  exports.Client = Client;
857
+ exports.ClientLedger = ClientLedger;
725
858
  exports.SOLAsset = SOLAsset;
726
859
  exports.SOLChain = SOLChain;
727
860
  exports.SOL_DECIMALS = SOL_DECIMALS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xchainjs/xchain-solana",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "Solana client for XChainJS",
5
5
  "keywords": [
6
6
  "Solana",
@@ -32,13 +32,14 @@
32
32
  "lint": "eslint --config ../../eslint.config.mjs \"{src,__tests__}/**/*.ts\" --fix --max-warnings 0"
33
33
  },
34
34
  "dependencies": {
35
+ "@ledgerhq/hw-app-solana": "^7.5.5",
35
36
  "@metaplex-foundation/mpl-token-metadata": "^3.2.1",
36
37
  "@metaplex-foundation/umi": "0.9.2",
37
38
  "@metaplex-foundation/umi-bundle-defaults": "0.9.2",
38
39
  "@solana/addresses": "^2.1.1",
39
40
  "@solana/spl-token": "^0.4.13",
40
41
  "@solana/web3.js": "^1.98.2",
41
- "@xchainjs/xchain-client": "2.0.9",
42
+ "@xchainjs/xchain-client": "2.0.10",
42
43
  "@xchainjs/xchain-crypto": "1.0.6",
43
44
  "@xchainjs/xchain-util": "2.0.5",
44
45
  "bs58": "^6.0.0",