@solana/kora 0.2.0 → 0.3.0-beta.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.
@@ -1,10 +1,21 @@
1
- import { Config, EstimateTransactionFeeRequest, EstimateTransactionFeeResponse, GetBlockhashResponse, GetPayerSignerResponse, GetPaymentInstructionRequest, GetPaymentInstructionResponse, GetSupportedTokensResponse, KoraClientOptions, SignAndSendTransactionRequest, SignAndSendTransactionResponse, SignTransactionRequest, SignTransactionResponse, TransferTransactionRequest, TransferTransactionResponse } from './types/index.js';
1
+ import { Config, EstimateBundleFeeRequest, EstimateBundleFeeResponse, EstimateTransactionFeeRequest, EstimateTransactionFeeResponse, GetBlockhashResponse, GetPayerSignerResponse, GetPaymentInstructionRequest, GetPaymentInstructionResponse, GetSupportedTokensResponse, GetVersionResponse, KoraClientOptions, SignAndSendBundleRequest, SignAndSendBundleResponse, SignAndSendTransactionRequest, SignAndSendTransactionResponse, SignBundleRequest, SignBundleResponse, SignTransactionRequest, SignTransactionResponse } from './types/index.js';
2
2
  /**
3
3
  * Kora RPC client for interacting with the Kora paymaster service.
4
4
  *
5
- * @example
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
6
9
  * ```typescript
7
- * const client = new KoraClient({ rpcUrl: 'http://localhost:8080' });
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
8
19
  * const config = await client.getConfig();
9
20
  * ```
10
21
  */
@@ -12,21 +23,204 @@ export declare class KoraClient {
12
23
  private rpcUrl;
13
24
  private apiKey?;
14
25
  private hmacSecret?;
15
- constructor({ rpcUrl, apiKey, hmacSecret }: KoraClientOptions);
26
+ private getRecaptchaToken?;
27
+ /**
28
+ * Creates a new Kora client instance.
29
+ * @param options - Client configuration options
30
+ * @param options.rpcUrl - The Kora RPC server URL
31
+ * @param options.apiKey - Optional API key for authentication
32
+ * @param options.hmacSecret - Optional HMAC secret for signature-based authentication
33
+ * @param options.getRecaptchaToken - Optional callback to get reCAPTCHA token for bot protection
34
+ */
35
+ constructor({ rpcUrl, apiKey, hmacSecret, getRecaptchaToken }: KoraClientOptions);
16
36
  private getHmacSignature;
17
37
  private getHeaders;
18
38
  private rpcRequest;
39
+ /**
40
+ * Retrieves the current Kora server configuration.
41
+ * @returns The server configuration including fee payer address and validation rules
42
+ * @throws {Error} When the RPC call fails
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const config = await client.getConfig();
47
+ * console.log('Fee payer:', config.fee_payer);
48
+ * console.log('Validation config:', JSON.stringify(config.validation_config, null, 2));
49
+ * ```
50
+ */
19
51
  getConfig(): Promise<Config>;
52
+ /**
53
+ * Retrieves the payer signer and payment destination from the Kora server.
54
+ * @returns Object containing the payer signer and payment destination
55
+ * @throws {Error} When the RPC call fails
56
+ *
57
+ * @example
58
+ */
20
59
  getPayerSigner(): Promise<GetPayerSignerResponse>;
60
+ /**
61
+ * Gets the latest blockhash from the Solana RPC that the Kora server is connected to.
62
+ * @returns Object containing the current blockhash
63
+ * @throws {Error} When the RPC call fails
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const { blockhash } = await client.getBlockhash();
68
+ * console.log('Current blockhash:', blockhash);
69
+ * ```
70
+ */
21
71
  getBlockhash(): Promise<GetBlockhashResponse>;
72
+ /**
73
+ * Gets the version of the Kora server.
74
+ * @returns Object containing the server version
75
+ * @throws {Error} When the RPC call fails
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const { version } = await client.getVersion();
80
+ * console.log('Server version:', version);
81
+ * ```
82
+ */
83
+ getVersion(): Promise<GetVersionResponse>;
84
+ /**
85
+ * Retrieves the list of tokens supported for fee payment.
86
+ * @returns Object containing an array of supported token mint addresses
87
+ * @throws {Error} When the RPC call fails
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const { tokens } = await client.getSupportedTokens();
92
+ * console.log('Supported tokens:', tokens);
93
+ * // Output: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', ...]
94
+ * ```
95
+ */
22
96
  getSupportedTokens(): Promise<GetSupportedTokensResponse>;
97
+ /**
98
+ * Estimates the transaction fee in both lamports and the specified token.
99
+ * @param request - Fee estimation request parameters
100
+ * @param request.transaction - Base64-encoded transaction to estimate fees for
101
+ * @param request.fee_token - Mint address of the token to calculate fees in
102
+ * @returns Fee amounts in both lamports and the specified token
103
+ * @throws {Error} When the RPC call fails, the transaction is invalid, or the token is not supported
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const fees = await client.estimateTransactionFee({
108
+ * transaction: 'base64EncodedTransaction',
109
+ * fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
110
+ * });
111
+ * console.log('Fee in lamports:', fees.fee_in_lamports);
112
+ * console.log('Fee in USDC:', fees.fee_in_token);
113
+ * ```
114
+ */
23
115
  estimateTransactionFee(request: EstimateTransactionFeeRequest): Promise<EstimateTransactionFeeResponse>;
116
+ /**
117
+ * Estimates the bundle fee in both lamports and the specified token.
118
+ * @param request - Bundle fee estimation request parameters
119
+ * @param request.transactions - Array of base64-encoded transactions to estimate fees for
120
+ * @param request.fee_token - Mint address of the token to calculate fees in
121
+ * @returns Total fee amounts across all transactions in both lamports and the specified token
122
+ * @throws {Error} When the RPC call fails, the bundle is invalid, or the token is not supported
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const fees = await client.estimateBundleFee({
127
+ * transactions: ['base64EncodedTransaction1', 'base64EncodedTransaction2'],
128
+ * fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
129
+ * });
130
+ * console.log('Total fee in lamports:', fees.fee_in_lamports);
131
+ * console.log('Total fee in USDC:', fees.fee_in_token);
132
+ * ```
133
+ */
134
+ estimateBundleFee(request: EstimateBundleFeeRequest): Promise<EstimateBundleFeeResponse>;
135
+ /**
136
+ * Signs a transaction with the Kora fee payer without broadcasting it.
137
+ * @param request - Sign request parameters
138
+ * @param request.transaction - Base64-encoded transaction to sign
139
+ * @returns Signature and the signed transaction
140
+ * @throws {Error} When the RPC call fails or transaction validation fails
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * const result = await client.signTransaction({
145
+ * transaction: 'base64EncodedTransaction'
146
+ * });
147
+ * console.log('Signature:', result.signature);
148
+ * console.log('Signed tx:', result.signed_transaction);
149
+ * ```
150
+ */
24
151
  signTransaction(request: SignTransactionRequest): Promise<SignTransactionResponse>;
152
+ /**
153
+ * Signs a transaction and immediately broadcasts it to the Solana network.
154
+ * @param request - Sign and send request parameters
155
+ * @param request.transaction - Base64-encoded transaction to sign and send
156
+ * @returns Signature and the signed transaction
157
+ * @throws {Error} When the RPC call fails, validation fails, or broadcast fails
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const result = await client.signAndSendTransaction({
162
+ * transaction: 'base64EncodedTransaction'
163
+ * });
164
+ * console.log('Transaction signature:', result.signature);
165
+ * ```
166
+ */
25
167
  signAndSendTransaction(request: SignAndSendTransactionRequest): Promise<SignAndSendTransactionResponse>;
26
- transferTransaction(request: TransferTransactionRequest): Promise<TransferTransactionResponse>;
27
168
  /**
28
- * Estimates the fee and builds a payment transfer instruction from the source wallet
29
- * to the Kora payment address. The server handles decimal conversion internally.
169
+ * Signs a bundle of transactions with the Kora fee payer without broadcasting.
170
+ * @param request - Sign bundle request parameters
171
+ * @param request.transactions - Array of base64-encoded transactions to sign
172
+ * @param request.signer_key - Optional signer address for the transactions
173
+ * @param request.sig_verify - Optional signature verification (defaults to false)
174
+ * @param request.sign_only_indices - Optional indices of transactions to sign (defaults to all)
175
+ * @returns Array of signed transactions and signer public key
176
+ * @throws {Error} When the RPC call fails or validation fails
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * const result = await client.signBundle({
181
+ * transactions: ['base64Tx1', 'base64Tx2']
182
+ * });
183
+ * console.log('Signed transactions:', result.signed_transactions);
184
+ * console.log('Signer:', result.signer_pubkey);
185
+ * ```
186
+ */
187
+ signBundle(request: SignBundleRequest): Promise<SignBundleResponse>;
188
+ /**
189
+ * Signs a bundle of transactions and sends them to Jito block engine.
190
+ * @param request - Sign and send bundle request parameters
191
+ * @param request.transactions - Array of base64-encoded transactions to sign and send
192
+ * @param request.signer_key - Optional signer address for the transactions
193
+ * @param request.sig_verify - Optional signature verification (defaults to false)
194
+ * @param request.sign_only_indices - Optional indices of transactions to sign (defaults to all)
195
+ * @returns Array of signed transactions, signer public key, and Jito bundle UUID
196
+ * @throws {Error} When the RPC call fails, validation fails, or Jito submission fails
197
+ *
198
+ * @example
199
+ * ```typescript
200
+ * const result = await client.signAndSendBundle({
201
+ * transactions: ['base64Tx1', 'base64Tx2']
202
+ * });
203
+ * console.log('Bundle UUID:', result.bundle_uuid);
204
+ * console.log('Signed transactions:', result.signed_transactions);
205
+ * ```
206
+ */
207
+ signAndSendBundle(request: SignAndSendBundleRequest): Promise<SignAndSendBundleResponse>;
208
+ /**
209
+ * Creates a payment instruction to append to a transaction for fee payment to the Kora paymaster.
210
+ *
211
+ * This method estimates the required fee and generates a token transfer instruction
212
+ * from the source wallet to the Kora payment address. The server handles decimal
213
+ * conversion internally, so the raw token amount is used directly.
214
+ *
215
+ * @param request - Payment instruction request parameters
216
+ * @param request.transaction - Base64-encoded transaction to estimate fees for
217
+ * @param request.fee_token - Mint address of the token to use for payment
218
+ * @param request.source_wallet - Public key of the wallet paying the fees
219
+ * @param request.token_program_id - Optional token program ID (defaults to TOKEN_PROGRAM_ADDRESS)
220
+ * @param request.signer_key - Optional signer address for the transaction
221
+ * @param request.sig_verify - Optional signer verification during transaction simulation (defaults to false)
222
+ * @returns Payment instruction details including the instruction, amount, and addresses
223
+ * @throws {Error} When the token is not supported, payment is not required, or invalid addresses are provided
30
224
  *
31
225
  * @example
32
226
  * ```typescript
@@ -1,13 +1,23 @@
1
- import { assertIsAddress, createNoopSigner } from '@solana/kit';
1
+ import { assertIsAddress, isTransactionSigner } from '@solana/kit';
2
2
  import { findAssociatedTokenPda, getTransferInstruction, TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
3
3
  import crypto from 'crypto';
4
- import { getInstructionsFromBase64Message } from './utils/transaction.js';
5
4
  /**
6
5
  * Kora RPC client for interacting with the Kora paymaster service.
7
6
  *
8
- * @example
7
+ * Provides methods to estimate fees, sign transactions, and perform gasless transfers
8
+ * on Solana as specified by the Kora paymaster operator.
9
+ *
10
+ * @example Kora Initialization
9
11
  * ```typescript
10
- * const client = new KoraClient({ rpcUrl: 'http://localhost:8080' });
12
+ * const client = new KoraClient({
13
+ * rpcUrl: 'http://localhost:8080',
14
+ * // apiKey may be required by some operators
15
+ * // apiKey: 'your-api-key',
16
+ * // hmacSecret may be required by some operators
17
+ * // hmacSecret: 'your-hmac-secret'
18
+ * });
19
+ *
20
+ * // Sample usage: Get config
11
21
  * const config = await client.getConfig();
12
22
  * ```
13
23
  */
@@ -15,10 +25,20 @@ export class KoraClient {
15
25
  rpcUrl;
16
26
  apiKey;
17
27
  hmacSecret;
18
- constructor({ rpcUrl, apiKey, hmacSecret }) {
28
+ getRecaptchaToken;
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
+ * @param options.getRecaptchaToken - Optional callback to get reCAPTCHA token for bot protection
36
+ */
37
+ constructor({ rpcUrl, apiKey, hmacSecret, getRecaptchaToken }) {
19
38
  this.rpcUrl = rpcUrl;
20
39
  this.apiKey = apiKey;
21
40
  this.hmacSecret = hmacSecret;
41
+ this.getRecaptchaToken = getRecaptchaToken;
22
42
  }
23
43
  getHmacSignature({ timestamp, body }) {
24
44
  if (!this.hmacSecret) {
@@ -27,7 +47,7 @@ export class KoraClient {
27
47
  const message = timestamp + body;
28
48
  return crypto.createHmac('sha256', this.hmacSecret).update(message).digest('hex');
29
49
  }
30
- getHeaders({ body }) {
50
+ async getHeaders({ body }) {
31
51
  const headers = {};
32
52
  if (this.apiKey) {
33
53
  headers['x-api-key'] = this.apiKey;
@@ -38,6 +58,10 @@ export class KoraClient {
38
58
  headers['x-timestamp'] = timestamp;
39
59
  headers['x-hmac-signature'] = signature;
40
60
  }
61
+ if (this.getRecaptchaToken) {
62
+ const token = await Promise.resolve(this.getRecaptchaToken());
63
+ headers['x-recaptcha-token'] = token;
64
+ }
41
65
  return headers;
42
66
  }
43
67
  async rpcRequest(method, params) {
@@ -47,7 +71,7 @@ export class KoraClient {
47
71
  method,
48
72
  params,
49
73
  });
50
- const headers = this.getHeaders({ body });
74
+ const headers = await this.getHeaders({ body });
51
75
  const response = await fetch(this.rpcUrl, {
52
76
  body,
53
77
  headers: { ...headers, 'Content-Type': 'application/json' },
@@ -60,35 +84,213 @@ export class KoraClient {
60
84
  }
61
85
  return json.result;
62
86
  }
87
+ /**
88
+ * Retrieves the current Kora server configuration.
89
+ * @returns The server configuration including fee payer address and validation rules
90
+ * @throws {Error} When the RPC call fails
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const config = await client.getConfig();
95
+ * console.log('Fee payer:', config.fee_payer);
96
+ * console.log('Validation config:', JSON.stringify(config.validation_config, null, 2));
97
+ * ```
98
+ */
63
99
  async getConfig() {
64
100
  return await this.rpcRequest('getConfig', undefined);
65
101
  }
102
+ /**
103
+ * Retrieves the payer signer and payment destination from the Kora server.
104
+ * @returns Object containing the payer signer and payment destination
105
+ * @throws {Error} When the RPC call fails
106
+ *
107
+ * @example
108
+ */
66
109
  async getPayerSigner() {
67
110
  return await this.rpcRequest('getPayerSigner', undefined);
68
111
  }
112
+ /**
113
+ * Gets the latest blockhash from the Solana RPC that the Kora server is connected to.
114
+ * @returns Object containing the current blockhash
115
+ * @throws {Error} When the RPC call fails
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * const { blockhash } = await client.getBlockhash();
120
+ * console.log('Current blockhash:', blockhash);
121
+ * ```
122
+ */
69
123
  async getBlockhash() {
70
124
  return await this.rpcRequest('getBlockhash', undefined);
71
125
  }
126
+ /**
127
+ * Gets the version of the Kora server.
128
+ * @returns Object containing the server version
129
+ * @throws {Error} When the RPC call fails
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const { version } = await client.getVersion();
134
+ * console.log('Server version:', version);
135
+ * ```
136
+ */
137
+ async getVersion() {
138
+ return await this.rpcRequest('getVersion', undefined);
139
+ }
140
+ /**
141
+ * Retrieves the list of tokens supported for fee payment.
142
+ * @returns Object containing an array of supported token mint addresses
143
+ * @throws {Error} When the RPC call fails
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const { tokens } = await client.getSupportedTokens();
148
+ * console.log('Supported tokens:', tokens);
149
+ * // Output: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', ...]
150
+ * ```
151
+ */
72
152
  async getSupportedTokens() {
73
153
  return await this.rpcRequest('getSupportedTokens', undefined);
74
154
  }
155
+ /**
156
+ * Estimates the transaction fee in both lamports and the specified token.
157
+ * @param request - Fee estimation request parameters
158
+ * @param request.transaction - Base64-encoded transaction to estimate fees for
159
+ * @param request.fee_token - Mint address of the token to calculate fees in
160
+ * @returns Fee amounts in both lamports and the specified token
161
+ * @throws {Error} When the RPC call fails, the transaction is invalid, or the token is not supported
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const fees = await client.estimateTransactionFee({
166
+ * transaction: 'base64EncodedTransaction',
167
+ * fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
168
+ * });
169
+ * console.log('Fee in lamports:', fees.fee_in_lamports);
170
+ * console.log('Fee in USDC:', fees.fee_in_token);
171
+ * ```
172
+ */
75
173
  async estimateTransactionFee(request) {
76
174
  return await this.rpcRequest('estimateTransactionFee', request);
77
175
  }
176
+ /**
177
+ * Estimates the bundle fee in both lamports and the specified token.
178
+ * @param request - Bundle fee estimation request parameters
179
+ * @param request.transactions - Array of base64-encoded transactions to estimate fees for
180
+ * @param request.fee_token - Mint address of the token to calculate fees in
181
+ * @returns Total fee amounts across all transactions in both lamports and the specified token
182
+ * @throws {Error} When the RPC call fails, the bundle is invalid, or the token is not supported
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * const fees = await client.estimateBundleFee({
187
+ * transactions: ['base64EncodedTransaction1', 'base64EncodedTransaction2'],
188
+ * fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
189
+ * });
190
+ * console.log('Total fee in lamports:', fees.fee_in_lamports);
191
+ * console.log('Total fee in USDC:', fees.fee_in_token);
192
+ * ```
193
+ */
194
+ async estimateBundleFee(request) {
195
+ return await this.rpcRequest('estimateBundleFee', request);
196
+ }
197
+ /**
198
+ * Signs a transaction with the Kora fee payer without broadcasting it.
199
+ * @param request - Sign request parameters
200
+ * @param request.transaction - Base64-encoded transaction to sign
201
+ * @returns Signature and the signed transaction
202
+ * @throws {Error} When the RPC call fails or transaction validation fails
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * const result = await client.signTransaction({
207
+ * transaction: 'base64EncodedTransaction'
208
+ * });
209
+ * console.log('Signature:', result.signature);
210
+ * console.log('Signed tx:', result.signed_transaction);
211
+ * ```
212
+ */
78
213
  async signTransaction(request) {
79
214
  return await this.rpcRequest('signTransaction', request);
80
215
  }
216
+ /**
217
+ * Signs a transaction and immediately broadcasts it to the Solana network.
218
+ * @param request - Sign and send request parameters
219
+ * @param request.transaction - Base64-encoded transaction to sign and send
220
+ * @returns Signature and the signed transaction
221
+ * @throws {Error} When the RPC call fails, validation fails, or broadcast fails
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * const result = await client.signAndSendTransaction({
226
+ * transaction: 'base64EncodedTransaction'
227
+ * });
228
+ * console.log('Transaction signature:', result.signature);
229
+ * ```
230
+ */
81
231
  async signAndSendTransaction(request) {
82
232
  return await this.rpcRequest('signAndSendTransaction', request);
83
233
  }
84
- async transferTransaction(request) {
85
- const response = await this.rpcRequest('transferTransaction', request);
86
- response.instructions = getInstructionsFromBase64Message(response.message || '');
87
- return response;
234
+ /**
235
+ * Signs a bundle of transactions with the Kora fee payer without broadcasting.
236
+ * @param request - Sign bundle request parameters
237
+ * @param request.transactions - Array of base64-encoded transactions to sign
238
+ * @param request.signer_key - Optional signer address for the transactions
239
+ * @param request.sig_verify - Optional signature verification (defaults to false)
240
+ * @param request.sign_only_indices - Optional indices of transactions to sign (defaults to all)
241
+ * @returns Array of signed transactions and signer public key
242
+ * @throws {Error} When the RPC call fails or validation fails
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const result = await client.signBundle({
247
+ * transactions: ['base64Tx1', 'base64Tx2']
248
+ * });
249
+ * console.log('Signed transactions:', result.signed_transactions);
250
+ * console.log('Signer:', result.signer_pubkey);
251
+ * ```
252
+ */
253
+ async signBundle(request) {
254
+ return await this.rpcRequest('signBundle', request);
255
+ }
256
+ /**
257
+ * Signs a bundle of transactions and sends them to Jito block engine.
258
+ * @param request - Sign and send bundle request parameters
259
+ * @param request.transactions - Array of base64-encoded transactions to sign and send
260
+ * @param request.signer_key - Optional signer address for the transactions
261
+ * @param request.sig_verify - Optional signature verification (defaults to false)
262
+ * @param request.sign_only_indices - Optional indices of transactions to sign (defaults to all)
263
+ * @returns Array of signed transactions, signer public key, and Jito bundle UUID
264
+ * @throws {Error} When the RPC call fails, validation fails, or Jito submission fails
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * const result = await client.signAndSendBundle({
269
+ * transactions: ['base64Tx1', 'base64Tx2']
270
+ * });
271
+ * console.log('Bundle UUID:', result.bundle_uuid);
272
+ * console.log('Signed transactions:', result.signed_transactions);
273
+ * ```
274
+ */
275
+ async signAndSendBundle(request) {
276
+ return await this.rpcRequest('signAndSendBundle', request);
88
277
  }
89
278
  /**
90
- * Estimates the fee and builds a payment transfer instruction from the source wallet
91
- * to the Kora payment address. The server handles decimal conversion internally.
279
+ * Creates a payment instruction to append to a transaction for fee payment to the Kora paymaster.
280
+ *
281
+ * This method estimates the required fee and generates a token transfer instruction
282
+ * from the source wallet to the Kora payment address. The server handles decimal
283
+ * conversion internally, so the raw token amount is used directly.
284
+ *
285
+ * @param request - Payment instruction request parameters
286
+ * @param request.transaction - Base64-encoded transaction to estimate fees for
287
+ * @param request.fee_token - Mint address of the token to use for payment
288
+ * @param request.source_wallet - Public key of the wallet paying the fees
289
+ * @param request.token_program_id - Optional token program ID (defaults to TOKEN_PROGRAM_ADDRESS)
290
+ * @param request.signer_key - Optional signer address for the transaction
291
+ * @param request.sig_verify - Optional signer verification during transaction simulation (defaults to false)
292
+ * @returns Payment instruction details including the instruction, amount, and addresses
293
+ * @throws {Error} When the token is not supported, payment is not required, or invalid addresses are provided
92
294
  *
93
295
  * @example
94
296
  * ```typescript
@@ -101,7 +303,9 @@ export class KoraClient {
101
303
  * ```
102
304
  */
103
305
  async getPaymentInstruction({ transaction, fee_token, source_wallet, token_program_id = TOKEN_PROGRAM_ADDRESS, signer_key, sig_verify, }) {
104
- assertIsAddress(source_wallet);
306
+ const isSigner = typeof source_wallet !== 'string' && isTransactionSigner(source_wallet);
307
+ const walletAddress = isSigner ? source_wallet.address : source_wallet;
308
+ assertIsAddress(walletAddress);
105
309
  assertIsAddress(fee_token);
106
310
  assertIsAddress(token_program_id);
107
311
  const { fee_in_token, payment_address, signer_pubkey } = await this.estimateTransactionFee({
@@ -113,7 +317,7 @@ export class KoraClient {
113
317
  assertIsAddress(payment_address);
114
318
  const [sourceTokenAccount] = await findAssociatedTokenPda({
115
319
  mint: fee_token,
116
- owner: source_wallet,
320
+ owner: walletAddress,
117
321
  tokenProgram: token_program_id,
118
322
  });
119
323
  const [destinationTokenAccount] = await findAssociatedTokenPda({
@@ -121,10 +325,12 @@ export class KoraClient {
121
325
  owner: payment_address,
122
326
  tokenProgram: token_program_id,
123
327
  });
124
- const signer = createNoopSigner(source_wallet);
328
+ if (fee_in_token === undefined) {
329
+ throw new Error('Fee token was specified but fee_in_token was not returned from server');
330
+ }
125
331
  const paymentInstruction = getTransferInstruction({
126
332
  amount: fee_in_token,
127
- authority: signer,
333
+ authority: isSigner ? source_wallet : walletAddress,
128
334
  destination: destinationTokenAccount,
129
335
  source: sourceTokenAccount,
130
336
  });
@@ -134,7 +340,6 @@ export class KoraClient {
134
340
  payment_amount: fee_in_token,
135
341
  payment_instruction: paymentInstruction,
136
342
  payment_token: fee_token,
137
- signer,
138
343
  signer_address: signer_pubkey,
139
344
  };
140
345
  }
@@ -1,4 +1,4 @@
1
1
  export * from './types/index.js';
2
2
  export { KoraClient } from './client.js';
3
- export { koraPlugin, type KoraPlugin } from './kit/plugin.js';
4
3
  export { createKitKoraClient, type KoraKitClient } from './kit/index.js';
4
+ export { koraPlugin, type KoraApi } from './plugin.js';
package/dist/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './types/index.js';
2
2
  export { KoraClient } from './client.js';
3
- export { koraPlugin } from './kit/plugin.js';
4
3
  export { createKitKoraClient } from './kit/index.js';
4
+ export { koraPlugin } from './plugin.js';
@@ -1,5 +1,10 @@
1
1
  import { appendTransactionMessageInstructions, blockhash, createTransactionMessage, createTransactionPlanExecutor, getBase64EncodedWireTransaction, getBase64Encoder, getSignatureFromTransaction, getTransactionDecoder, partiallySignTransactionMessageWithSigners, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signature, } from '@solana/kit';
2
2
  import { removePaymentInstruction, updatePaymentInstructionAmount } from './payment.js';
3
+ // TODO: Create a bundle-aware executor (e.g. `createKoraBundlePlanExecutor`) that collects
4
+ // multiple planned transaction messages into a single `signAndSendBundle` call instead of
5
+ // submitting each one individually via `signAndSendTransaction`. This would let users
6
+ // compose Jito bundles through the Kit plan/execute pipeline rather than manually encoding
7
+ // transactions and calling `client.kora.signAndSendBundle()`.
3
8
  export function createKoraTransactionPlanExecutor(koraClient, config, payerSigner, payment, resolveProvisoryComputeUnitLimit) {
4
9
  return createTransactionPlanExecutor({
5
10
  async executeTransactionMessage(_context, transactionMessage) {
@@ -20,8 +25,13 @@ export function createKoraTransactionPlanExecutor(koraClient, config, payerSigne
20
25
  fee_token: config.feeToken,
21
26
  transaction: prePaymentTx,
22
27
  });
23
- if (fee_in_token < 0) {
24
- throw new Error(`Kora fee estimation returned a negative fee (${fee_in_token}). This indicates a server-side error.`);
28
+ if (fee_in_token == null) {
29
+ console.warn('[kora] fee_in_token is undefined defaulting to 0. ' +
30
+ 'If paid pricing is expected, check that the fee token is correctly configured on the server.');
31
+ }
32
+ const feeInToken = fee_in_token ?? 0;
33
+ if (feeInToken < 0) {
34
+ throw new Error(`Kora fee estimation returned a negative fee (${feeInToken}). This indicates a server-side error.`);
25
35
  }
26
36
  const currentIxs = 'instructions' in msgForEstimation
27
37
  ? msgForEstimation.instructions
@@ -31,8 +41,8 @@ export function createKoraTransactionPlanExecutor(koraClient, config, payerSigne
31
41
  'The message structure may be incompatible with this version of the Kora SDK.');
32
42
  }
33
43
  // Replace placeholder with real fee amount, or strip it if fee is 0
34
- const finalIxs = fee_in_token > 0
35
- ? updatePaymentInstructionAmount(currentIxs, config.feePayerWallet, sourceTokenAccount, destinationTokenAccount, fee_in_token, config.tokenProgramId)
44
+ const finalIxs = feeInToken > 0
45
+ ? updatePaymentInstructionAmount(currentIxs, config.feePayerWallet, sourceTokenAccount, destinationTokenAccount, feeInToken, config.tokenProgramId)
36
46
  : removePaymentInstruction(currentIxs, sourceTokenAccount, destinationTokenAccount, config.feePayerWallet, config.tokenProgramId);
37
47
  const resolvedMsg = pipe(createTransactionMessage({ version: 0 }), m => setTransactionMessageFeePayerSigner(payerSigner, m), m => setTransactionMessageLifetimeUsingBlockhash({
38
48
  blockhash: blockhash(bh),
@@ -43,7 +53,10 @@ export function createKoraTransactionPlanExecutor(koraClient, config, payerSigne
43
53
  else {
44
54
  finalTx = prePaymentTx;
45
55
  }
46
- const result = await koraClient.signAndSendTransaction({ transaction: finalTx });
56
+ const result = await koraClient.signAndSendTransaction({
57
+ transaction: finalTx,
58
+ user_id: config.userId,
59
+ });
47
60
  if (result.signature) {
48
61
  return signature(result.signature);
49
62
  }
@@ -29,15 +29,18 @@ export declare function createKitKoraClient(config: KoraKitClientConfig): Promis
29
29
  rpcSubscriptions: import("@solana/kit").RpcSubscriptions<import("@solana/kit").SolanaRpcSubscriptionsApi>;
30
30
  } & {
31
31
  kora: {
32
+ estimateBundleFee(request: import("../types/index.js").EstimateBundleFeeRequest): Promise<import("../types/index.js").KitEstimateBundleFeeResponse>;
32
33
  estimateTransactionFee(request: import("../types/index.js").EstimateTransactionFeeRequest): Promise<import("../types/index.js").KitEstimateFeeResponse>;
33
34
  getBlockhash(): Promise<import("../types/index.js").KitBlockhashResponse>;
34
35
  getConfig(): Promise<import("../types/index.js").KitConfigResponse>;
35
36
  getPayerSigner(): Promise<import("../types/index.js").KitPayerSignerResponse>;
36
37
  getPaymentInstruction(request: import("../types/index.js").GetPaymentInstructionRequest): Promise<import("../types/index.js").KitPaymentInstructionResponse>;
37
38
  getSupportedTokens(): Promise<import("../types/index.js").KitSupportedTokensResponse>;
39
+ getVersion(): Promise<import("../types/index.js").GetVersionResponse>;
40
+ signAndSendBundle(request: import("../types/index.js").SignAndSendBundleRequest): Promise<import("../types/index.js").KitSignAndSendBundleResponse>;
38
41
  signAndSendTransaction(request: import("../types/index.js").SignAndSendTransactionRequest): Promise<import("../types/index.js").KitSignAndSendTransactionResponse>;
42
+ signBundle(request: import("../types/index.js").SignBundleRequest): Promise<import("../types/index.js").KitSignBundleResponse>;
39
43
  signTransaction(request: import("../types/index.js").SignTransactionRequest): Promise<import("../types/index.js").KitSignTransactionResponse>;
40
- transferTransaction(request: import("../types/index.js").TransferTransactionRequest): Promise<import("../types/index.js").KitTransferTransactionResponse>;
41
44
  };
42
45
  } & {
43
46
  payer: import("@solana/kit").TransactionSigner;