@solana/kora 0.0.0 → 0.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/README.md +60 -0
- package/dist/src/client.d.ts +187 -0
- package/dist/src/client.js +286 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/types/index.d.ts +385 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/utils/transaction.d.ts +8 -0
- package/dist/src/utils/transaction.js +32 -0
- package/dist/test/auth-setup.d.ts +1 -0
- package/dist/test/auth-setup.js +51 -0
- package/dist/test/integration.test.d.ts +1 -0
- package/dist/test/integration.test.js +307 -0
- package/dist/test/setup.d.ts +26 -0
- package/dist/test/setup.js +231 -0
- package/dist/test/unit.test.d.ts +1 -0
- package/dist/test/unit.test.js +553 -0
- package/package.json +64 -7
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Kora TypeScript SDK
|
|
2
|
+
|
|
3
|
+
A TypeScript SDK for interacting with the Kora RPC server. This SDK provides a type-safe interface to all Kora RPC methods.
|
|
4
|
+
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
### Building from Source
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Install dependencies
|
|
11
|
+
pnpm install
|
|
12
|
+
|
|
13
|
+
# Build the SDK
|
|
14
|
+
pnpm run build
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Running Tests
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Start your local Kora RPC Server from the root project directory:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
kora --config tests/src/common/fixtures/kora-test.toml rpc start --signers-config tests/src/common/fixtures/signers.toml
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Tests rely on [Solana CLI's](https://solana.com/docs/intro/installation) local test validator.
|
|
27
|
+
|
|
28
|
+
Run:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm test:ci:integration
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This will start a local test validator and run all tests.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { KoraClient } from '@solana/kora';
|
|
41
|
+
|
|
42
|
+
// Initialize the client with your RPC endpoint
|
|
43
|
+
const client = new KoraClient({ rpcUrl: 'http://localhost:8080' });
|
|
44
|
+
|
|
45
|
+
// Example: Transfer tokens
|
|
46
|
+
const result = await client.transferTransaction({
|
|
47
|
+
amount: 1000000, // 1 USDC (6 decimals)
|
|
48
|
+
token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC mint
|
|
49
|
+
source: "sourceAddress",
|
|
50
|
+
destination: "destinationAddress"
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Access the base64 encoded transaction, base64 encoded message, and parsed instructions directly
|
|
54
|
+
console.log('Transaction:', result.transaction);
|
|
55
|
+
console.log('Message:', result.message);
|
|
56
|
+
console.log('Instructions:', result.instructions);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**[→ API Reference](https://launch.solana.com/docs/kora/json-rpc-api)**
|
|
60
|
+
**[→ Quick Start](https://launch.solana.com/docs/kora/getting-started/quick-start)**
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { Config, EstimateTransactionFeeRequest, EstimateTransactionFeeResponse, GetBlockhashResponse, GetSupportedTokensResponse, SignAndSendTransactionRequest, SignAndSendTransactionResponse, SignTransactionRequest, SignTransactionResponse, TransferTransactionRequest, TransferTransactionResponse, KoraClientOptions, GetPayerSignerResponse, GetPaymentInstructionRequest, GetPaymentInstructionResponse } from './types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Kora RPC client for interacting with the Kora paymaster service.
|
|
4
|
+
*
|
|
5
|
+
* Provides methods to estimate fees, sign transactions, and perform gasless transfers
|
|
6
|
+
* on Solana as specified by the Kora paymaster operator.
|
|
7
|
+
*
|
|
8
|
+
* @example Kora Initialization
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const client = new KoraClient({
|
|
11
|
+
* rpcUrl: 'http://localhost:8080',
|
|
12
|
+
* // apiKey may be required by some operators
|
|
13
|
+
* // apiKey: 'your-api-key',
|
|
14
|
+
* // hmacSecret may be required by some operators
|
|
15
|
+
* // hmacSecret: 'your-hmac-secret'
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Sample usage: Get config
|
|
19
|
+
* const config = await client.getConfig();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare class KoraClient {
|
|
23
|
+
private rpcUrl;
|
|
24
|
+
private apiKey?;
|
|
25
|
+
private hmacSecret?;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new Kora client instance.
|
|
28
|
+
* @param options - Client configuration options
|
|
29
|
+
* @param options.rpcUrl - The Kora RPC server URL
|
|
30
|
+
* @param options.apiKey - Optional API key for authentication
|
|
31
|
+
* @param options.hmacSecret - Optional HMAC secret for signature-based authentication
|
|
32
|
+
*/
|
|
33
|
+
constructor({ rpcUrl, apiKey, hmacSecret }: KoraClientOptions);
|
|
34
|
+
private getHmacSignature;
|
|
35
|
+
private getHeaders;
|
|
36
|
+
private rpcRequest;
|
|
37
|
+
/**
|
|
38
|
+
* Retrieves the current Kora server configuration.
|
|
39
|
+
* @returns The server configuration including fee payer address and validation rules
|
|
40
|
+
* @throws {Error} When the RPC call fails
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const config = await client.getConfig();
|
|
45
|
+
* console.log('Fee payer:', config.fee_payer);
|
|
46
|
+
* console.log('Validation config:', JSON.stringify(config.validation_config, null, 2));
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
getConfig(): Promise<Config>;
|
|
50
|
+
/**
|
|
51
|
+
* Retrieves the payer signer and payment destination from the Kora server.
|
|
52
|
+
* @returns Object containing the payer signer and payment destination
|
|
53
|
+
* @throws {Error} When the RPC call fails
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
*/
|
|
57
|
+
getPayerSigner(): Promise<GetPayerSignerResponse>;
|
|
58
|
+
/**
|
|
59
|
+
* Gets the latest blockhash from the Solana RPC that the Kora server is connected to.
|
|
60
|
+
* @returns Object containing the current blockhash
|
|
61
|
+
* @throws {Error} When the RPC call fails
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const { blockhash } = await client.getBlockhash();
|
|
66
|
+
* console.log('Current blockhash:', blockhash);
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
getBlockhash(): Promise<GetBlockhashResponse>;
|
|
70
|
+
/**
|
|
71
|
+
* Retrieves the list of tokens supported for fee payment.
|
|
72
|
+
* @returns Object containing an array of supported token mint addresses
|
|
73
|
+
* @throws {Error} When the RPC call fails
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const { tokens } = await client.getSupportedTokens();
|
|
78
|
+
* console.log('Supported tokens:', tokens);
|
|
79
|
+
* // Output: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', ...]
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
getSupportedTokens(): Promise<GetSupportedTokensResponse>;
|
|
83
|
+
/**
|
|
84
|
+
* Estimates the transaction fee in both lamports and the specified token.
|
|
85
|
+
* @param request - Fee estimation request parameters
|
|
86
|
+
* @param request.transaction - Base64-encoded transaction to estimate fees for
|
|
87
|
+
* @param request.fee_token - Mint address of the token to calculate fees in
|
|
88
|
+
* @returns Fee amounts in both lamports and the specified token
|
|
89
|
+
* @throws {Error} When the RPC call fails, the transaction is invalid, or the token is not supported
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const fees = await client.estimateTransactionFee({
|
|
94
|
+
* transaction: 'base64EncodedTransaction',
|
|
95
|
+
* fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
|
|
96
|
+
* });
|
|
97
|
+
* console.log('Fee in lamports:', fees.fee_in_lamports);
|
|
98
|
+
* console.log('Fee in USDC:', fees.fee_in_token);
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
estimateTransactionFee(request: EstimateTransactionFeeRequest): Promise<EstimateTransactionFeeResponse>;
|
|
102
|
+
/**
|
|
103
|
+
* Signs a transaction with the Kora fee payer without broadcasting it.
|
|
104
|
+
* @param request - Sign request parameters
|
|
105
|
+
* @param request.transaction - Base64-encoded transaction to sign
|
|
106
|
+
* @returns Signature and the signed transaction
|
|
107
|
+
* @throws {Error} When the RPC call fails or transaction validation fails
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const result = await client.signTransaction({
|
|
112
|
+
* transaction: 'base64EncodedTransaction'
|
|
113
|
+
* });
|
|
114
|
+
* console.log('Signature:', result.signature);
|
|
115
|
+
* console.log('Signed tx:', result.signed_transaction);
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
signTransaction(request: SignTransactionRequest): Promise<SignTransactionResponse>;
|
|
119
|
+
/**
|
|
120
|
+
* Signs a transaction and immediately broadcasts it to the Solana network.
|
|
121
|
+
* @param request - Sign and send request parameters
|
|
122
|
+
* @param request.transaction - Base64-encoded transaction to sign and send
|
|
123
|
+
* @returns Signature and the signed transaction
|
|
124
|
+
* @throws {Error} When the RPC call fails, validation fails, or broadcast fails
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const result = await client.signAndSendTransaction({
|
|
129
|
+
* transaction: 'base64EncodedTransaction'
|
|
130
|
+
* });
|
|
131
|
+
* console.log('Transaction signature:', result.signature);
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
signAndSendTransaction(request: SignAndSendTransactionRequest): Promise<SignAndSendTransactionResponse>;
|
|
135
|
+
/**
|
|
136
|
+
* Creates a token transfer transaction with Kora as the fee payer.
|
|
137
|
+
* @param request - Transfer request parameters
|
|
138
|
+
* @param request.amount - Amount to transfer (in token's smallest unit)
|
|
139
|
+
* @param request.token - Mint address of the token to transfer
|
|
140
|
+
* @param request.source - Source wallet public key
|
|
141
|
+
* @param request.destination - Destination wallet public key
|
|
142
|
+
* @returns Base64-encoded signed transaction, base64-encoded message, blockhash, and parsed instructions
|
|
143
|
+
* @throws {Error} When the RPC call fails or token is not supported
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const transfer = await client.transferTransaction({
|
|
148
|
+
* amount: 1000000, // 1 USDC (6 decimals)
|
|
149
|
+
* token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
150
|
+
* source: 'sourceWalletPublicKey',
|
|
151
|
+
* destination: 'destinationWalletPublicKey'
|
|
152
|
+
* });
|
|
153
|
+
* console.log('Transaction:', transfer.transaction);
|
|
154
|
+
* console.log('Message:', transfer.message);
|
|
155
|
+
* console.log('Instructions:', transfer.instructions);
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
transferTransaction(request: TransferTransactionRequest): Promise<TransferTransactionResponse>;
|
|
159
|
+
/**
|
|
160
|
+
* Creates a payment instruction to append to a transaction for fee payment to the Kora paymaster.
|
|
161
|
+
*
|
|
162
|
+
* This method estimates the required fee and generates a token transfer instruction
|
|
163
|
+
* from the source wallet to the Kora payment address. The server handles decimal
|
|
164
|
+
* conversion internally, so the raw token amount is used directly.
|
|
165
|
+
*
|
|
166
|
+
* @param request - Payment instruction request parameters
|
|
167
|
+
* @param request.transaction - Base64-encoded transaction to estimate fees for
|
|
168
|
+
* @param request.fee_token - Mint address of the token to use for payment
|
|
169
|
+
* @param request.source_wallet - Public key of the wallet paying the fees
|
|
170
|
+
* @param request.token_program_id - Optional token program ID (defaults to TOKEN_PROGRAM_ADDRESS)
|
|
171
|
+
* @param request.signer_key - Optional signer address for the transaction
|
|
172
|
+
* @param request.sig_verify - Optional signer verification during transaction simulation (defaults to false)
|
|
173
|
+
* @returns Payment instruction details including the instruction, amount, and addresses
|
|
174
|
+
* @throws {Error} When the token is not supported, payment is not required, or invalid addresses are provided
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* const paymentInfo = await client.getPaymentInstruction({
|
|
179
|
+
* transaction: 'base64EncodedTransaction',
|
|
180
|
+
* fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
181
|
+
* source_wallet: 'sourceWalletPublicKey'
|
|
182
|
+
* });
|
|
183
|
+
* // Append paymentInfo.payment_instruction to your transaction
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
getPaymentInstruction({ transaction, fee_token, source_wallet, token_program_id, signer_key, sig_verify, }: GetPaymentInstructionRequest): Promise<GetPaymentInstructionResponse>;
|
|
187
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { assertIsAddress, createNoopSigner } from '@solana/kit';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import { getInstructionsFromBase64Message } from './utils/transaction.js';
|
|
4
|
+
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS, getTransferInstruction } from '@solana-program/token';
|
|
5
|
+
/**
|
|
6
|
+
* Kora RPC client for interacting with the Kora paymaster service.
|
|
7
|
+
*
|
|
8
|
+
* Provides methods to estimate fees, sign transactions, and perform gasless transfers
|
|
9
|
+
* on Solana as specified by the Kora paymaster operator.
|
|
10
|
+
*
|
|
11
|
+
* @example Kora Initialization
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const client = new KoraClient({
|
|
14
|
+
* rpcUrl: 'http://localhost:8080',
|
|
15
|
+
* // apiKey may be required by some operators
|
|
16
|
+
* // apiKey: 'your-api-key',
|
|
17
|
+
* // hmacSecret may be required by some operators
|
|
18
|
+
* // hmacSecret: 'your-hmac-secret'
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Sample usage: Get config
|
|
22
|
+
* const config = await client.getConfig();
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export class KoraClient {
|
|
26
|
+
rpcUrl;
|
|
27
|
+
apiKey;
|
|
28
|
+
hmacSecret;
|
|
29
|
+
/**
|
|
30
|
+
* Creates a new Kora client instance.
|
|
31
|
+
* @param options - Client configuration options
|
|
32
|
+
* @param options.rpcUrl - The Kora RPC server URL
|
|
33
|
+
* @param options.apiKey - Optional API key for authentication
|
|
34
|
+
* @param options.hmacSecret - Optional HMAC secret for signature-based authentication
|
|
35
|
+
*/
|
|
36
|
+
constructor({ rpcUrl, apiKey, hmacSecret }) {
|
|
37
|
+
this.rpcUrl = rpcUrl;
|
|
38
|
+
this.apiKey = apiKey;
|
|
39
|
+
this.hmacSecret = hmacSecret;
|
|
40
|
+
}
|
|
41
|
+
getHmacSignature({ timestamp, body }) {
|
|
42
|
+
if (!this.hmacSecret) {
|
|
43
|
+
throw new Error('HMAC secret is not set');
|
|
44
|
+
}
|
|
45
|
+
const message = timestamp + body;
|
|
46
|
+
return crypto.createHmac('sha256', this.hmacSecret).update(message).digest('hex');
|
|
47
|
+
}
|
|
48
|
+
getHeaders({ body }) {
|
|
49
|
+
const headers = {};
|
|
50
|
+
if (this.apiKey) {
|
|
51
|
+
headers['x-api-key'] = this.apiKey;
|
|
52
|
+
}
|
|
53
|
+
if (this.hmacSecret) {
|
|
54
|
+
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
55
|
+
const signature = this.getHmacSignature({ timestamp, body });
|
|
56
|
+
headers['x-timestamp'] = timestamp;
|
|
57
|
+
headers['x-hmac-signature'] = signature;
|
|
58
|
+
}
|
|
59
|
+
return headers;
|
|
60
|
+
}
|
|
61
|
+
async rpcRequest(method, params) {
|
|
62
|
+
const body = JSON.stringify({
|
|
63
|
+
jsonrpc: '2.0',
|
|
64
|
+
id: 1,
|
|
65
|
+
method,
|
|
66
|
+
params,
|
|
67
|
+
});
|
|
68
|
+
const headers = this.getHeaders({ body });
|
|
69
|
+
const response = await fetch(this.rpcUrl, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: { ...headers, 'Content-Type': 'application/json' },
|
|
72
|
+
body,
|
|
73
|
+
});
|
|
74
|
+
const json = (await response.json());
|
|
75
|
+
if (json.error) {
|
|
76
|
+
const error = json.error;
|
|
77
|
+
throw new Error(`RPC Error ${error.code}: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
return json.result;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Retrieves the current Kora server configuration.
|
|
83
|
+
* @returns The server configuration including fee payer address and validation rules
|
|
84
|
+
* @throws {Error} When the RPC call fails
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const config = await client.getConfig();
|
|
89
|
+
* console.log('Fee payer:', config.fee_payer);
|
|
90
|
+
* console.log('Validation config:', JSON.stringify(config.validation_config, null, 2));
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
async getConfig() {
|
|
94
|
+
return this.rpcRequest('getConfig', undefined);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Retrieves the payer signer and payment destination from the Kora server.
|
|
98
|
+
* @returns Object containing the payer signer and payment destination
|
|
99
|
+
* @throws {Error} When the RPC call fails
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
*/
|
|
103
|
+
async getPayerSigner() {
|
|
104
|
+
return this.rpcRequest('getPayerSigner', undefined);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Gets the latest blockhash from the Solana RPC that the Kora server is connected to.
|
|
108
|
+
* @returns Object containing the current blockhash
|
|
109
|
+
* @throws {Error} When the RPC call fails
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const { blockhash } = await client.getBlockhash();
|
|
114
|
+
* console.log('Current blockhash:', blockhash);
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
async getBlockhash() {
|
|
118
|
+
return this.rpcRequest('getBlockhash', undefined);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Retrieves the list of tokens supported for fee payment.
|
|
122
|
+
* @returns Object containing an array of supported token mint addresses
|
|
123
|
+
* @throws {Error} When the RPC call fails
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const { tokens } = await client.getSupportedTokens();
|
|
128
|
+
* console.log('Supported tokens:', tokens);
|
|
129
|
+
* // Output: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', ...]
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
async getSupportedTokens() {
|
|
133
|
+
return this.rpcRequest('getSupportedTokens', undefined);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Estimates the transaction fee in both lamports and the specified token.
|
|
137
|
+
* @param request - Fee estimation request parameters
|
|
138
|
+
* @param request.transaction - Base64-encoded transaction to estimate fees for
|
|
139
|
+
* @param request.fee_token - Mint address of the token to calculate fees in
|
|
140
|
+
* @returns Fee amounts in both lamports and the specified token
|
|
141
|
+
* @throws {Error} When the RPC call fails, the transaction is invalid, or the token is not supported
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const fees = await client.estimateTransactionFee({
|
|
146
|
+
* transaction: 'base64EncodedTransaction',
|
|
147
|
+
* fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
|
|
148
|
+
* });
|
|
149
|
+
* console.log('Fee in lamports:', fees.fee_in_lamports);
|
|
150
|
+
* console.log('Fee in USDC:', fees.fee_in_token);
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
async estimateTransactionFee(request) {
|
|
154
|
+
return this.rpcRequest('estimateTransactionFee', request);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Signs a transaction with the Kora fee payer without broadcasting it.
|
|
158
|
+
* @param request - Sign request parameters
|
|
159
|
+
* @param request.transaction - Base64-encoded transaction to sign
|
|
160
|
+
* @returns Signature and the signed transaction
|
|
161
|
+
* @throws {Error} When the RPC call fails or transaction validation fails
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const result = await client.signTransaction({
|
|
166
|
+
* transaction: 'base64EncodedTransaction'
|
|
167
|
+
* });
|
|
168
|
+
* console.log('Signature:', result.signature);
|
|
169
|
+
* console.log('Signed tx:', result.signed_transaction);
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
async signTransaction(request) {
|
|
173
|
+
return this.rpcRequest('signTransaction', request);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Signs a transaction and immediately broadcasts it to the Solana network.
|
|
177
|
+
* @param request - Sign and send request parameters
|
|
178
|
+
* @param request.transaction - Base64-encoded transaction to sign and send
|
|
179
|
+
* @returns Signature and the signed transaction
|
|
180
|
+
* @throws {Error} When the RPC call fails, validation fails, or broadcast fails
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* const result = await client.signAndSendTransaction({
|
|
185
|
+
* transaction: 'base64EncodedTransaction'
|
|
186
|
+
* });
|
|
187
|
+
* console.log('Transaction signature:', result.signature);
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
async signAndSendTransaction(request) {
|
|
191
|
+
return this.rpcRequest('signAndSendTransaction', request);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Creates a token transfer transaction with Kora as the fee payer.
|
|
195
|
+
* @param request - Transfer request parameters
|
|
196
|
+
* @param request.amount - Amount to transfer (in token's smallest unit)
|
|
197
|
+
* @param request.token - Mint address of the token to transfer
|
|
198
|
+
* @param request.source - Source wallet public key
|
|
199
|
+
* @param request.destination - Destination wallet public key
|
|
200
|
+
* @returns Base64-encoded signed transaction, base64-encoded message, blockhash, and parsed instructions
|
|
201
|
+
* @throws {Error} When the RPC call fails or token is not supported
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* const transfer = await client.transferTransaction({
|
|
206
|
+
* amount: 1000000, // 1 USDC (6 decimals)
|
|
207
|
+
* token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
208
|
+
* source: 'sourceWalletPublicKey',
|
|
209
|
+
* destination: 'destinationWalletPublicKey'
|
|
210
|
+
* });
|
|
211
|
+
* console.log('Transaction:', transfer.transaction);
|
|
212
|
+
* console.log('Message:', transfer.message);
|
|
213
|
+
* console.log('Instructions:', transfer.instructions);
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
async transferTransaction(request) {
|
|
217
|
+
const response = await this.rpcRequest('transferTransaction', request);
|
|
218
|
+
// Parse instructions from the message to enhance developer experience
|
|
219
|
+
// Always set instructions, even for empty messages (for consistency)
|
|
220
|
+
response.instructions = getInstructionsFromBase64Message(response.message || '');
|
|
221
|
+
return response;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Creates a payment instruction to append to a transaction for fee payment to the Kora paymaster.
|
|
225
|
+
*
|
|
226
|
+
* This method estimates the required fee and generates a token transfer instruction
|
|
227
|
+
* from the source wallet to the Kora payment address. The server handles decimal
|
|
228
|
+
* conversion internally, so the raw token amount is used directly.
|
|
229
|
+
*
|
|
230
|
+
* @param request - Payment instruction request parameters
|
|
231
|
+
* @param request.transaction - Base64-encoded transaction to estimate fees for
|
|
232
|
+
* @param request.fee_token - Mint address of the token to use for payment
|
|
233
|
+
* @param request.source_wallet - Public key of the wallet paying the fees
|
|
234
|
+
* @param request.token_program_id - Optional token program ID (defaults to TOKEN_PROGRAM_ADDRESS)
|
|
235
|
+
* @param request.signer_key - Optional signer address for the transaction
|
|
236
|
+
* @param request.sig_verify - Optional signer verification during transaction simulation (defaults to false)
|
|
237
|
+
* @returns Payment instruction details including the instruction, amount, and addresses
|
|
238
|
+
* @throws {Error} When the token is not supported, payment is not required, or invalid addresses are provided
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* const paymentInfo = await client.getPaymentInstruction({
|
|
243
|
+
* transaction: 'base64EncodedTransaction',
|
|
244
|
+
* fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
245
|
+
* source_wallet: 'sourceWalletPublicKey'
|
|
246
|
+
* });
|
|
247
|
+
* // Append paymentInfo.payment_instruction to your transaction
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
async getPaymentInstruction({ transaction, fee_token, source_wallet, token_program_id = TOKEN_PROGRAM_ADDRESS, signer_key, sig_verify, }) {
|
|
251
|
+
assertIsAddress(source_wallet);
|
|
252
|
+
assertIsAddress(fee_token);
|
|
253
|
+
assertIsAddress(token_program_id);
|
|
254
|
+
const { fee_in_token, payment_address, signer_pubkey } = await this.estimateTransactionFee({
|
|
255
|
+
transaction,
|
|
256
|
+
fee_token,
|
|
257
|
+
sig_verify,
|
|
258
|
+
signer_key,
|
|
259
|
+
});
|
|
260
|
+
assertIsAddress(payment_address);
|
|
261
|
+
const [sourceTokenAccount] = await findAssociatedTokenPda({
|
|
262
|
+
owner: source_wallet,
|
|
263
|
+
tokenProgram: token_program_id,
|
|
264
|
+
mint: fee_token,
|
|
265
|
+
});
|
|
266
|
+
const [destinationTokenAccount] = await findAssociatedTokenPda({
|
|
267
|
+
owner: payment_address,
|
|
268
|
+
tokenProgram: token_program_id,
|
|
269
|
+
mint: fee_token,
|
|
270
|
+
});
|
|
271
|
+
const paymentInstruction = getTransferInstruction({
|
|
272
|
+
source: sourceTokenAccount,
|
|
273
|
+
destination: destinationTokenAccount,
|
|
274
|
+
authority: createNoopSigner(source_wallet),
|
|
275
|
+
amount: fee_in_token,
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
original_transaction: transaction,
|
|
279
|
+
payment_instruction: paymentInstruction,
|
|
280
|
+
payment_amount: fee_in_token,
|
|
281
|
+
payment_token: fee_token,
|
|
282
|
+
payment_address,
|
|
283
|
+
signer_address: signer_pubkey,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|