@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 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
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types/index.js';
2
+ export { KoraClient } from './client.js';
@@ -0,0 +1,2 @@
1
+ export * from './types/index.js';
2
+ export { KoraClient } from './client.js';