@atxp/solana 0.8.3

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/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # ATXP Solana
2
+
3
+ ATXP is a framework for building and running agents that can interact with the world. See [docs.atxp.ai](https://docs.atxp.ai) for documentation and examples.
4
+
5
+ ATXP Solana provides Solana blockchain support for `@atxp/client`, enabling payments and authentication using Solana wallets.
6
+
7
+ ## Support
8
+
9
+ For detailed API documentation, configuration options, and advanced usage patterns, please refer to our [complete documentation](https://docs.atxp.ai/).
10
+
11
+ Have questions or need help? Join our [Discord community](https://discord.gg/FuJXHhe9aW) - we're happy to help!
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @atxp/solana @solana/web3.js @solana/pay bs58
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```typescript
22
+ import { SolanaAccount } from '@atxp/solana';
23
+ import { atxpClient } from '@atxp/client';
24
+ import { Keypair } from '@solana/web3.js';
25
+
26
+ // Create or load a Solana keypair
27
+ const keypair = Keypair.generate();
28
+
29
+ // Create a Solana account
30
+ const account = new SolanaAccount({
31
+ keypair,
32
+ solanaRpcUrl: 'https://api.mainnet-beta.solana.com'
33
+ });
34
+
35
+ // Use with atxpClient
36
+ const client = await atxpClient({
37
+ account,
38
+ mcpServer: 'https://browse.mcp.atxp.ai/'
39
+ });
40
+
41
+ const res = await client.callTool({
42
+ name: 'atxp_browse',
43
+ arguments: { query: 'What is Solana?' }
44
+ });
45
+ ```
46
+
47
+ ## SolanaAccount
48
+
49
+ The `SolanaAccount` class provides Solana wallet integration for ATXP:
50
+
51
+ - **Authentication**: Signs JWTs using Solana keypair for MCP server authentication
52
+ - **Payments**: Makes USDC payments on Solana using SPL tokens
53
+ - **Wallet Integration**: Works with any Solana wallet that provides a `Keypair`
54
+
55
+ ### Configuration
56
+
57
+ ```typescript
58
+ const account = new SolanaAccount({
59
+ keypair: Keypair, // Your Solana keypair
60
+ solanaRpcUrl: string, // Solana RPC endpoint
61
+ logger?: Logger // Optional logger instance
62
+ });
63
+ ```
64
+
65
+ ## SolanaPaymentMaker
66
+
67
+ The `SolanaPaymentMaker` class handles USDC payments on Solana:
68
+
69
+ - Uses `@solana/pay` for payment transactions
70
+ - Supports USDC SPL token transfers
71
+ - Validates transactions before and after sending
72
+
73
+ You generally don't need to use this directly - `SolanaAccount` uses it internally.
package/dist/index.cjs ADDED
@@ -0,0 +1,140 @@
1
+ 'use strict';
2
+
3
+ var client = require('@atxp/client');
4
+ var web3_js = require('@solana/web3.js');
5
+ var pay = require('@solana/pay');
6
+ var splToken = require('@solana/spl-token');
7
+ var bs58 = require('bs58');
8
+ var BigNumber = require('bignumber.js');
9
+ var common = require('@atxp/common');
10
+ var jose = require('jose');
11
+
12
+ // this is a global public key for USDC on the solana mainnet
13
+ const USDC_MINT = new web3_js.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
14
+ const ValidateTransferError = pay.ValidateTransferError;
15
+ class SolanaPaymentMaker {
16
+ constructor(solanaEndpoint, sourceSecretKey, logger) {
17
+ this.generateJWT = async ({ paymentRequestId, codeChallenge, accountId }) => {
18
+ // Solana/Web3.js secretKey is 64 bytes:
19
+ // first 32 bytes are the private scalar, last 32 are the public key.
20
+ // JWK expects only the 32-byte private scalar for 'd'
21
+ const jwk = {
22
+ kty: 'OKP',
23
+ crv: 'Ed25519',
24
+ d: Buffer.from(this.source.secretKey.slice(0, 32)).toString('base64url'),
25
+ x: Buffer.from(this.source.publicKey.toBytes()).toString('base64url'),
26
+ };
27
+ const privateKey = await jose.importJWK(jwk, 'EdDSA');
28
+ if (!(privateKey instanceof CryptoKey)) {
29
+ throw new Error('Expected CryptoKey from importJWK');
30
+ }
31
+ return common.generateJWT(this.source.publicKey.toBase58(), privateKey, paymentRequestId || '', codeChallenge || '', accountId);
32
+ };
33
+ this.makePayment = async (destinations, memo, _paymentRequestId) => {
34
+ // Filter to solana chain destinations
35
+ const solanaDestinations = destinations.filter(d => d.chain === 'solana');
36
+ if (solanaDestinations.length === 0) {
37
+ this.logger.debug('SolanaPaymentMaker: No solana destinations found, cannot handle payment');
38
+ return null; // Cannot handle these destinations
39
+ }
40
+ // Pick first solana destination
41
+ const dest = solanaDestinations[0];
42
+ const amount = dest.amount;
43
+ const currency = dest.currency;
44
+ const receiver = dest.address;
45
+ if (currency.toUpperCase() !== 'USDC') {
46
+ throw new client.PaymentNetworkError('Only USDC currency is supported; received ' + currency);
47
+ }
48
+ const receiverKey = new web3_js.PublicKey(receiver);
49
+ this.logger.info(`Making payment of ${amount} ${currency} to ${receiver} on Solana from ${this.source.publicKey.toBase58()}`);
50
+ try {
51
+ // Check balance before attempting payment
52
+ const tokenAccountAddress = await splToken.getAssociatedTokenAddress(USDC_MINT, this.source.publicKey);
53
+ const tokenAccount = await splToken.getAccount(this.connection, tokenAccountAddress);
54
+ const balance = new BigNumber(tokenAccount.amount.toString()).dividedBy(10 ** 6); // USDC has 6 decimals
55
+ if (balance.lt(amount)) {
56
+ this.logger.warn(`Insufficient ${currency} balance for payment. Required: ${amount}, Available: ${balance}`);
57
+ throw new client.InsufficientFundsError(currency, amount, balance, 'solana');
58
+ }
59
+ // Get the destination token account address (this will be the transactionId)
60
+ const destinationTokenAccount = await splToken.getAssociatedTokenAddress(USDC_MINT, receiverKey);
61
+ // Increase compute units to handle both memo and token transfer
62
+ // Memo uses ~6000 CUs, token transfer needs ~6500 CUs
63
+ const modifyComputeUnits = web3_js.ComputeBudgetProgram.setComputeUnitLimit({
64
+ units: 50000,
65
+ });
66
+ const addPriorityFee = web3_js.ComputeBudgetProgram.setComputeUnitPrice({
67
+ microLamports: 20000,
68
+ });
69
+ const transaction = await pay.createTransfer(this.connection, this.source.publicKey, {
70
+ amount: amount,
71
+ recipient: receiverKey,
72
+ splToken: USDC_MINT,
73
+ memo,
74
+ });
75
+ transaction.add(modifyComputeUnits);
76
+ transaction.add(addPriorityFee);
77
+ const transactionSignature = await web3_js.sendAndConfirmTransaction(this.connection, transaction, [this.source]);
78
+ // Return transaction signature as transactionId and token account address as transactionSubId
79
+ return {
80
+ transactionId: transactionSignature,
81
+ transactionSubId: destinationTokenAccount.toBase58(),
82
+ chain: 'solana',
83
+ currency: 'USDC'
84
+ };
85
+ }
86
+ catch (error) {
87
+ if (error instanceof client.InsufficientFundsError || error instanceof client.PaymentNetworkError) {
88
+ throw error;
89
+ }
90
+ // Wrap other errors in PaymentNetworkError
91
+ throw new client.PaymentNetworkError(`Payment failed on Solana network: ${error.message}`, error);
92
+ }
93
+ };
94
+ if (!solanaEndpoint) {
95
+ throw new Error('Solana endpoint is required');
96
+ }
97
+ if (!sourceSecretKey) {
98
+ throw new Error('Source secret key is required');
99
+ }
100
+ this.connection = new web3_js.Connection(solanaEndpoint, { commitment: 'confirmed' });
101
+ this.source = web3_js.Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
102
+ this.logger = logger ?? new common.ConsoleLogger();
103
+ }
104
+ getSourceAddress(_params) {
105
+ return this.source.publicKey.toBase58();
106
+ }
107
+ }
108
+
109
+ class SolanaAccount {
110
+ constructor(solanaEndpoint, sourceSecretKey) {
111
+ if (!solanaEndpoint) {
112
+ throw new Error('Solana endpoint is required');
113
+ }
114
+ if (!sourceSecretKey) {
115
+ throw new Error('Source secret key is required');
116
+ }
117
+ const source = web3_js.Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
118
+ this.sourcePublicKey = source.publicKey.toBase58();
119
+ // Format accountId as network:address
120
+ this.accountId = `solana:${this.sourcePublicKey}`;
121
+ this.paymentMakers = [
122
+ new SolanaPaymentMaker(solanaEndpoint, sourceSecretKey)
123
+ ];
124
+ }
125
+ /**
126
+ * Get sources for this account
127
+ */
128
+ async getSources() {
129
+ return [{
130
+ address: this.sourcePublicKey,
131
+ chain: 'solana',
132
+ walletType: 'eoa'
133
+ }];
134
+ }
135
+ }
136
+
137
+ exports.SolanaAccount = SolanaAccount;
138
+ exports.SolanaPaymentMaker = SolanaPaymentMaker;
139
+ exports.ValidateTransferError = ValidateTransferError;
140
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/solanaPaymentMaker.ts","../src/solanaAccount.ts"],"sourcesContent":[null,null],"names":["PublicKey","_ValidateTransferError","importJWK","generateJWT","PaymentNetworkError","getAssociatedTokenAddress","getAccount","InsufficientFundsError","ComputeBudgetProgram","createTransfer","sendAndConfirmTransaction","Connection","Keypair","ConsoleLogger"],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { Account, PaymentMaker } from '@atxp/client';
2
+ import { AccountId, Source, Logger, Currency, Destination, PaymentIdentifier } from '@atxp/common';
3
+ import { ValidateTransferError as ValidateTransferError$1 } from '@solana/pay';
4
+ import BigNumber from 'bignumber.js';
5
+
6
+ declare class SolanaAccount implements Account {
7
+ accountId: AccountId;
8
+ paymentMakers: PaymentMaker[];
9
+ private sourcePublicKey;
10
+ constructor(solanaEndpoint: string, sourceSecretKey: string);
11
+ /**
12
+ * Get sources for this account
13
+ */
14
+ getSources(): Promise<Source[]>;
15
+ }
16
+
17
+ declare const ValidateTransferError: typeof ValidateTransferError$1;
18
+ declare class SolanaPaymentMaker implements PaymentMaker {
19
+ private connection;
20
+ private source;
21
+ private logger;
22
+ constructor(solanaEndpoint: string, sourceSecretKey: string, logger?: Logger);
23
+ getSourceAddress(_params: {
24
+ amount: BigNumber;
25
+ currency: Currency;
26
+ receiver: string;
27
+ memo: string;
28
+ }): string;
29
+ generateJWT: ({ paymentRequestId, codeChallenge, accountId }: {
30
+ paymentRequestId: string;
31
+ codeChallenge: string;
32
+ accountId?: AccountId | null;
33
+ }) => Promise<string>;
34
+ makePayment: (destinations: Destination[], memo: string, _paymentRequestId?: string) => Promise<PaymentIdentifier | null>;
35
+ }
36
+
37
+ export { SolanaAccount, SolanaPaymentMaker, ValidateTransferError };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,136 @@
1
+ import { PaymentNetworkError, InsufficientFundsError } from '@atxp/client';
2
+ import { PublicKey, ComputeBudgetProgram, sendAndConfirmTransaction, Connection, Keypair } from '@solana/web3.js';
3
+ import { ValidateTransferError as ValidateTransferError$1, createTransfer } from '@solana/pay';
4
+ import { getAssociatedTokenAddress, getAccount } from '@solana/spl-token';
5
+ import bs58 from 'bs58';
6
+ import BigNumber from 'bignumber.js';
7
+ import { generateJWT, ConsoleLogger } from '@atxp/common';
8
+ import { importJWK } from 'jose';
9
+
10
+ // this is a global public key for USDC on the solana mainnet
11
+ const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
12
+ const ValidateTransferError = ValidateTransferError$1;
13
+ class SolanaPaymentMaker {
14
+ constructor(solanaEndpoint, sourceSecretKey, logger) {
15
+ this.generateJWT = async ({ paymentRequestId, codeChallenge, accountId }) => {
16
+ // Solana/Web3.js secretKey is 64 bytes:
17
+ // first 32 bytes are the private scalar, last 32 are the public key.
18
+ // JWK expects only the 32-byte private scalar for 'd'
19
+ const jwk = {
20
+ kty: 'OKP',
21
+ crv: 'Ed25519',
22
+ d: Buffer.from(this.source.secretKey.slice(0, 32)).toString('base64url'),
23
+ x: Buffer.from(this.source.publicKey.toBytes()).toString('base64url'),
24
+ };
25
+ const privateKey = await importJWK(jwk, 'EdDSA');
26
+ if (!(privateKey instanceof CryptoKey)) {
27
+ throw new Error('Expected CryptoKey from importJWK');
28
+ }
29
+ return generateJWT(this.source.publicKey.toBase58(), privateKey, paymentRequestId || '', codeChallenge || '', accountId);
30
+ };
31
+ this.makePayment = async (destinations, memo, _paymentRequestId) => {
32
+ // Filter to solana chain destinations
33
+ const solanaDestinations = destinations.filter(d => d.chain === 'solana');
34
+ if (solanaDestinations.length === 0) {
35
+ this.logger.debug('SolanaPaymentMaker: No solana destinations found, cannot handle payment');
36
+ return null; // Cannot handle these destinations
37
+ }
38
+ // Pick first solana destination
39
+ const dest = solanaDestinations[0];
40
+ const amount = dest.amount;
41
+ const currency = dest.currency;
42
+ const receiver = dest.address;
43
+ if (currency.toUpperCase() !== 'USDC') {
44
+ throw new PaymentNetworkError('Only USDC currency is supported; received ' + currency);
45
+ }
46
+ const receiverKey = new PublicKey(receiver);
47
+ this.logger.info(`Making payment of ${amount} ${currency} to ${receiver} on Solana from ${this.source.publicKey.toBase58()}`);
48
+ try {
49
+ // Check balance before attempting payment
50
+ const tokenAccountAddress = await getAssociatedTokenAddress(USDC_MINT, this.source.publicKey);
51
+ const tokenAccount = await getAccount(this.connection, tokenAccountAddress);
52
+ const balance = new BigNumber(tokenAccount.amount.toString()).dividedBy(10 ** 6); // USDC has 6 decimals
53
+ if (balance.lt(amount)) {
54
+ this.logger.warn(`Insufficient ${currency} balance for payment. Required: ${amount}, Available: ${balance}`);
55
+ throw new InsufficientFundsError(currency, amount, balance, 'solana');
56
+ }
57
+ // Get the destination token account address (this will be the transactionId)
58
+ const destinationTokenAccount = await getAssociatedTokenAddress(USDC_MINT, receiverKey);
59
+ // Increase compute units to handle both memo and token transfer
60
+ // Memo uses ~6000 CUs, token transfer needs ~6500 CUs
61
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
62
+ units: 50000,
63
+ });
64
+ const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
65
+ microLamports: 20000,
66
+ });
67
+ const transaction = await createTransfer(this.connection, this.source.publicKey, {
68
+ amount: amount,
69
+ recipient: receiverKey,
70
+ splToken: USDC_MINT,
71
+ memo,
72
+ });
73
+ transaction.add(modifyComputeUnits);
74
+ transaction.add(addPriorityFee);
75
+ const transactionSignature = await sendAndConfirmTransaction(this.connection, transaction, [this.source]);
76
+ // Return transaction signature as transactionId and token account address as transactionSubId
77
+ return {
78
+ transactionId: transactionSignature,
79
+ transactionSubId: destinationTokenAccount.toBase58(),
80
+ chain: 'solana',
81
+ currency: 'USDC'
82
+ };
83
+ }
84
+ catch (error) {
85
+ if (error instanceof InsufficientFundsError || error instanceof PaymentNetworkError) {
86
+ throw error;
87
+ }
88
+ // Wrap other errors in PaymentNetworkError
89
+ throw new PaymentNetworkError(`Payment failed on Solana network: ${error.message}`, error);
90
+ }
91
+ };
92
+ if (!solanaEndpoint) {
93
+ throw new Error('Solana endpoint is required');
94
+ }
95
+ if (!sourceSecretKey) {
96
+ throw new Error('Source secret key is required');
97
+ }
98
+ this.connection = new Connection(solanaEndpoint, { commitment: 'confirmed' });
99
+ this.source = Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
100
+ this.logger = logger ?? new ConsoleLogger();
101
+ }
102
+ getSourceAddress(_params) {
103
+ return this.source.publicKey.toBase58();
104
+ }
105
+ }
106
+
107
+ class SolanaAccount {
108
+ constructor(solanaEndpoint, sourceSecretKey) {
109
+ if (!solanaEndpoint) {
110
+ throw new Error('Solana endpoint is required');
111
+ }
112
+ if (!sourceSecretKey) {
113
+ throw new Error('Source secret key is required');
114
+ }
115
+ const source = Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
116
+ this.sourcePublicKey = source.publicKey.toBase58();
117
+ // Format accountId as network:address
118
+ this.accountId = `solana:${this.sourcePublicKey}`;
119
+ this.paymentMakers = [
120
+ new SolanaPaymentMaker(solanaEndpoint, sourceSecretKey)
121
+ ];
122
+ }
123
+ /**
124
+ * Get sources for this account
125
+ */
126
+ async getSources() {
127
+ return [{
128
+ address: this.sourcePublicKey,
129
+ chain: 'solana',
130
+ walletType: 'eoa'
131
+ }];
132
+ }
133
+ }
134
+
135
+ export { SolanaAccount, SolanaPaymentMaker, ValidateTransferError };
136
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/solanaPaymentMaker.ts","../src/solanaAccount.ts"],"sourcesContent":[null,null],"names":["_ValidateTransferError"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Account, PaymentMaker } from '@atxp/client';
2
+ import type { AccountId, Source } from '@atxp/common';
3
+ export declare class SolanaAccount implements Account {
4
+ accountId: AccountId;
5
+ paymentMakers: PaymentMaker[];
6
+ private sourcePublicKey;
7
+ constructor(solanaEndpoint: string, sourceSecretKey: string);
8
+ /**
9
+ * Get sources for this account
10
+ */
11
+ getSources(): Promise<Source[]>;
12
+ }
13
+ //# sourceMappingURL=solanaAccount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solanaAccount.d.ts","sourceRoot":"","sources":["../src/solanaAccount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAKtD,qBAAa,aAAc,YAAW,OAAO;IAC3C,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,OAAO,CAAC,eAAe,CAAS;gBAEpB,cAAc,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM;IAiB3D;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAOtC"}
@@ -0,0 +1,34 @@
1
+ import { SolanaPaymentMaker } from './solanaPaymentMaker.js';
2
+ import { Keypair } from '@solana/web3.js';
3
+ import bs58 from 'bs58';
4
+
5
+ class SolanaAccount {
6
+ constructor(solanaEndpoint, sourceSecretKey) {
7
+ if (!solanaEndpoint) {
8
+ throw new Error('Solana endpoint is required');
9
+ }
10
+ if (!sourceSecretKey) {
11
+ throw new Error('Source secret key is required');
12
+ }
13
+ const source = Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
14
+ this.sourcePublicKey = source.publicKey.toBase58();
15
+ // Format accountId as network:address
16
+ this.accountId = `solana:${this.sourcePublicKey}`;
17
+ this.paymentMakers = [
18
+ new SolanaPaymentMaker(solanaEndpoint, sourceSecretKey)
19
+ ];
20
+ }
21
+ /**
22
+ * Get sources for this account
23
+ */
24
+ async getSources() {
25
+ return [{
26
+ address: this.sourcePublicKey,
27
+ chain: 'solana',
28
+ walletType: 'eoa'
29
+ }];
30
+ }
31
+ }
32
+
33
+ export { SolanaAccount };
34
+ //# sourceMappingURL=solanaAccount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solanaAccount.js","sources":["../src/solanaAccount.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;MAMa,aAAa,CAAA;IAKxB,WAAA,CAAY,cAAsB,EAAE,eAAuB,EAAA;QACzD,IAAI,CAAC,cAAc,EAAE;AACnB,YAAA,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC;QAChD;QACA,IAAI,CAAC,eAAe,EAAE;AACpB,YAAA,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC;QAClD;AACA,QAAA,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAClE,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE;;QAGlD,IAAI,CAAC,SAAS,GAAG,CAAA,OAAA,EAAU,IAAI,CAAC,eAAe,EAAe;QAC9D,IAAI,CAAC,aAAa,GAAG;AACnB,YAAA,IAAI,kBAAkB,CAAC,cAAc,EAAE,eAAe;SACvD;IACH;AAEA;;AAEG;AACH,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,OAAO,CAAC;gBACN,OAAO,EAAE,IAAI,CAAC,eAAe;AAC7B,gBAAA,KAAK,EAAE,QAAQ;AACf,gBAAA,UAAU,EAAE;AACb,aAAA,CAAC;IACJ;AACD;;;;"}
@@ -0,0 +1,25 @@
1
+ import type { PaymentMaker } from '@atxp/client';
2
+ import { ValidateTransferError as _ValidateTransferError } from "@solana/pay";
3
+ import BigNumber from "bignumber.js";
4
+ import { Currency, AccountId, PaymentIdentifier, Destination } from '@atxp/common';
5
+ import { Logger } from '@atxp/common';
6
+ export declare const ValidateTransferError: typeof _ValidateTransferError;
7
+ export declare class SolanaPaymentMaker implements PaymentMaker {
8
+ private connection;
9
+ private source;
10
+ private logger;
11
+ constructor(solanaEndpoint: string, sourceSecretKey: string, logger?: Logger);
12
+ getSourceAddress(_params: {
13
+ amount: BigNumber;
14
+ currency: Currency;
15
+ receiver: string;
16
+ memo: string;
17
+ }): string;
18
+ generateJWT: ({ paymentRequestId, codeChallenge, accountId }: {
19
+ paymentRequestId: string;
20
+ codeChallenge: string;
21
+ accountId?: AccountId | null;
22
+ }) => Promise<string>;
23
+ makePayment: (destinations: Destination[], memo: string, _paymentRequestId?: string) => Promise<PaymentIdentifier | null>;
24
+ }
25
+ //# sourceMappingURL=solanaPaymentMaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solanaPaymentMaker.d.ts","sourceRoot":"","sources":["../src/solanaPaymentMaker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,OAAO,EAAkB,qBAAqB,IAAI,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG9F,OAAO,SAAS,MAAM,cAAc,CAAC;AACrC,OAAO,EAAe,QAAQ,EAAE,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhG,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAMtC,eAAO,MAAM,qBAAqB,+BAAyB,CAAC;AAE5D,qBAAa,kBAAmB,YAAW,YAAY;IACrD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,cAAc,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAY5E,gBAAgB,CAAC,OAAO,EAAE;QAAC,MAAM,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,GAAG,MAAM;IAI1G,WAAW,GAAS,gDAA8C;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;KAAC,KAAG,OAAO,CAAC,MAAM,CAAC,CAelK;IAED,WAAW,GAAU,cAAc,WAAW,EAAE,EAAE,MAAM,MAAM,EAAE,oBAAoB,MAAM,KAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAyF7H;CACF"}
@@ -0,0 +1,108 @@
1
+ import { PaymentNetworkError, InsufficientFundsError } from '@atxp/client';
2
+ import { PublicKey, ComputeBudgetProgram, sendAndConfirmTransaction, Connection, Keypair } from '@solana/web3.js';
3
+ import { ValidateTransferError as ValidateTransferError$1, createTransfer } from '@solana/pay';
4
+ import { getAssociatedTokenAddress, getAccount } from '@solana/spl-token';
5
+ import bs58 from 'bs58';
6
+ import BigNumber from 'bignumber.js';
7
+ import { generateJWT, ConsoleLogger } from '@atxp/common';
8
+ import { importJWK } from 'jose';
9
+
10
+ // this is a global public key for USDC on the solana mainnet
11
+ const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
12
+ const ValidateTransferError = ValidateTransferError$1;
13
+ class SolanaPaymentMaker {
14
+ constructor(solanaEndpoint, sourceSecretKey, logger) {
15
+ this.generateJWT = async ({ paymentRequestId, codeChallenge, accountId }) => {
16
+ // Solana/Web3.js secretKey is 64 bytes:
17
+ // first 32 bytes are the private scalar, last 32 are the public key.
18
+ // JWK expects only the 32-byte private scalar for 'd'
19
+ const jwk = {
20
+ kty: 'OKP',
21
+ crv: 'Ed25519',
22
+ d: Buffer.from(this.source.secretKey.slice(0, 32)).toString('base64url'),
23
+ x: Buffer.from(this.source.publicKey.toBytes()).toString('base64url'),
24
+ };
25
+ const privateKey = await importJWK(jwk, 'EdDSA');
26
+ if (!(privateKey instanceof CryptoKey)) {
27
+ throw new Error('Expected CryptoKey from importJWK');
28
+ }
29
+ return generateJWT(this.source.publicKey.toBase58(), privateKey, paymentRequestId || '', codeChallenge || '', accountId);
30
+ };
31
+ this.makePayment = async (destinations, memo, _paymentRequestId) => {
32
+ // Filter to solana chain destinations
33
+ const solanaDestinations = destinations.filter(d => d.chain === 'solana');
34
+ if (solanaDestinations.length === 0) {
35
+ this.logger.debug('SolanaPaymentMaker: No solana destinations found, cannot handle payment');
36
+ return null; // Cannot handle these destinations
37
+ }
38
+ // Pick first solana destination
39
+ const dest = solanaDestinations[0];
40
+ const amount = dest.amount;
41
+ const currency = dest.currency;
42
+ const receiver = dest.address;
43
+ if (currency.toUpperCase() !== 'USDC') {
44
+ throw new PaymentNetworkError('Only USDC currency is supported; received ' + currency);
45
+ }
46
+ const receiverKey = new PublicKey(receiver);
47
+ this.logger.info(`Making payment of ${amount} ${currency} to ${receiver} on Solana from ${this.source.publicKey.toBase58()}`);
48
+ try {
49
+ // Check balance before attempting payment
50
+ const tokenAccountAddress = await getAssociatedTokenAddress(USDC_MINT, this.source.publicKey);
51
+ const tokenAccount = await getAccount(this.connection, tokenAccountAddress);
52
+ const balance = new BigNumber(tokenAccount.amount.toString()).dividedBy(10 ** 6); // USDC has 6 decimals
53
+ if (balance.lt(amount)) {
54
+ this.logger.warn(`Insufficient ${currency} balance for payment. Required: ${amount}, Available: ${balance}`);
55
+ throw new InsufficientFundsError(currency, amount, balance, 'solana');
56
+ }
57
+ // Get the destination token account address (this will be the transactionId)
58
+ const destinationTokenAccount = await getAssociatedTokenAddress(USDC_MINT, receiverKey);
59
+ // Increase compute units to handle both memo and token transfer
60
+ // Memo uses ~6000 CUs, token transfer needs ~6500 CUs
61
+ const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
62
+ units: 50000,
63
+ });
64
+ const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
65
+ microLamports: 20000,
66
+ });
67
+ const transaction = await createTransfer(this.connection, this.source.publicKey, {
68
+ amount: amount,
69
+ recipient: receiverKey,
70
+ splToken: USDC_MINT,
71
+ memo,
72
+ });
73
+ transaction.add(modifyComputeUnits);
74
+ transaction.add(addPriorityFee);
75
+ const transactionSignature = await sendAndConfirmTransaction(this.connection, transaction, [this.source]);
76
+ // Return transaction signature as transactionId and token account address as transactionSubId
77
+ return {
78
+ transactionId: transactionSignature,
79
+ transactionSubId: destinationTokenAccount.toBase58(),
80
+ chain: 'solana',
81
+ currency: 'USDC'
82
+ };
83
+ }
84
+ catch (error) {
85
+ if (error instanceof InsufficientFundsError || error instanceof PaymentNetworkError) {
86
+ throw error;
87
+ }
88
+ // Wrap other errors in PaymentNetworkError
89
+ throw new PaymentNetworkError(`Payment failed on Solana network: ${error.message}`, error);
90
+ }
91
+ };
92
+ if (!solanaEndpoint) {
93
+ throw new Error('Solana endpoint is required');
94
+ }
95
+ if (!sourceSecretKey) {
96
+ throw new Error('Source secret key is required');
97
+ }
98
+ this.connection = new Connection(solanaEndpoint, { commitment: 'confirmed' });
99
+ this.source = Keypair.fromSecretKey(bs58.decode(sourceSecretKey));
100
+ this.logger = logger ?? new ConsoleLogger();
101
+ }
102
+ getSourceAddress(_params) {
103
+ return this.source.publicKey.toBase58();
104
+ }
105
+ }
106
+
107
+ export { SolanaPaymentMaker, ValidateTransferError };
108
+ //# sourceMappingURL=solanaPaymentMaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solanaPaymentMaker.js","sources":["../src/solanaPaymentMaker.ts"],"sourcesContent":[null],"names":["_ValidateTransferError"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@atxp/solana",
3
+ "version": "0.8.3",
4
+ "description": "ATXP Solana - Solana blockchain support for ATXP",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/atxp-dev/sdk.git",
9
+ "directory": "packages/atxp-solana"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": false,
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "scripts": {
27
+ "build": "rollup -c",
28
+ "typecheck": "tsc --noEmit",
29
+ "lint": "eslint . --ext .ts",
30
+ "lint:fix": "eslint . --ext .ts --fix",
31
+ "test": "vitest run",
32
+ "prepack": "npm run build && npm run typecheck",
33
+ "pack:dry": "npm pack --dry-run"
34
+ },
35
+ "dependencies": {
36
+ "@atxp/client": "0.8.3",
37
+ "@atxp/common": "0.8.3",
38
+ "@solana/pay": "^0.2.5",
39
+ "@solana/web3.js": "^1.98.1",
40
+ "bignumber.js": "^9.3.0",
41
+ "bs58": "^6.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^22.13.0",
45
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
46
+ "@typescript-eslint/parser": "^8.38.0",
47
+ "eslint": "^9.32.0",
48
+ "typescript": "^5.7.3",
49
+ "vitest": "^3.0.9"
50
+ }
51
+ }