@xchainjs/xchain-solana 1.0.9 → 1.1.0
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 +8 -1
- package/lib/clientLedger.d.ts +28 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.esm.js +133 -2
- package/lib/index.js +134 -1
- package/package.json +2 -1
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
|
-
|
|
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
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Solana client for XChainJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Solana",
|
|
@@ -32,6 +32,7 @@
|
|
|
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",
|