@solana/kora 0.0.0 → 0.1.0

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