@solana/kora 0.2.1 → 0.3.0-beta.2

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,5 +1,6 @@
1
- import { createKitKoraClient } from '../src/kit/index.js';
2
- import { address, createNoopSigner } from '@solana/kit';
1
+ import { createKitKoraClient, kora } from '../src/kit/index.js';
2
+ import { address, createClient, createNoopSigner } from '@solana/kit';
3
+ import { identity, signer } from '@solana/kit-plugin-signer';
3
4
  // Mock fetch globally
4
5
  const mockFetch = jest.fn();
5
6
  global.fetch = mockFetch;
@@ -80,7 +81,7 @@ describe('createKitKoraClient', () => {
80
81
  rpcUrl: MOCK_RPC_URL,
81
82
  feeToken: MOCK_FEE_TOKEN,
82
83
  feePayerWallet: MOCK_WALLET,
83
- })).rejects.toThrow('RPC Error -32000: Server error');
84
+ })).rejects.toThrow('Kora Error -32000: Server error');
84
85
  });
85
86
  it('should expose kora namespace for raw RPC access', async () => {
86
87
  mockRpcResponse({
@@ -324,6 +325,24 @@ describe('createKitKoraClient', () => {
324
325
  const headers = mockFetch.mock.calls[0][1].headers;
325
326
  expect(headers['x-api-key']).toBe('test-api-key');
326
327
  });
328
+ it('should pass getRecaptchaToken to underlying KoraClient', async () => {
329
+ const mockGetToken = jest.fn().mockResolvedValue('test-recaptcha-token');
330
+ mockRpcResponse({
331
+ signer_address: MOCK_PAYER_ADDRESS,
332
+ payment_address: MOCK_PAYMENT_ADDRESS,
333
+ });
334
+ await createKitKoraClient({
335
+ endpoint: MOCK_ENDPOINT,
336
+ rpcUrl: MOCK_RPC_URL,
337
+ feeToken: MOCK_FEE_TOKEN,
338
+ feePayerWallet: MOCK_WALLET,
339
+ getRecaptchaToken: mockGetToken,
340
+ });
341
+ // The init call should include the reCAPTCHA token header
342
+ expect(mockGetToken).toHaveBeenCalledTimes(1);
343
+ const headers = mockFetch.mock.calls[0][1].headers;
344
+ expect(headers['x-recaptcha-token']).toBe('test-recaptcha-token');
345
+ });
327
346
  });
328
347
  describe('Token-2022 support', () => {
329
348
  it('should accept tokenProgramId in config', async () => {
@@ -471,3 +490,47 @@ describe('createKitKoraClient', () => {
471
490
  });
472
491
  });
473
492
  });
493
+ describe('kora() bundle plugin', () => {
494
+ beforeEach(() => {
495
+ mockFetch.mockClear();
496
+ });
497
+ afterEach(() => {
498
+ jest.resetAllMocks();
499
+ });
500
+ it('composes with identity() and exposes the same surface as the wrapper', async () => {
501
+ mockRpcResponse({
502
+ signer_address: MOCK_PAYER_ADDRESS,
503
+ payment_address: MOCK_PAYMENT_ADDRESS,
504
+ });
505
+ const client = await createClient()
506
+ .use(identity(MOCK_WALLET))
507
+ .use(kora({
508
+ endpoint: MOCK_ENDPOINT,
509
+ rpcUrl: MOCK_RPC_URL,
510
+ feeToken: MOCK_FEE_TOKEN,
511
+ }));
512
+ expect(client.identity.address).toBe(MOCK_WALLET_ADDRESS);
513
+ expect(client.payer.address).toBe(MOCK_PAYER_ADDRESS);
514
+ expect(client.paymentAddress).toBe(MOCK_PAYMENT_ADDRESS);
515
+ expect(typeof client.sendTransaction).toBe('function');
516
+ expect(typeof client.planTransaction).toBe('function');
517
+ expect(client.kora).toBeDefined();
518
+ });
519
+ // TODO: re-enable once @solana/kit supports overriding an already-set field on an
520
+ // extended client.
521
+ it.skip('overrides client.payer when the caller used signer() (sets identity + payer)', async () => {
522
+ mockRpcResponse({
523
+ signer_address: MOCK_PAYER_ADDRESS,
524
+ payment_address: MOCK_PAYMENT_ADDRESS,
525
+ });
526
+ const client = await createClient()
527
+ .use(signer(MOCK_WALLET))
528
+ .use(kora({
529
+ endpoint: MOCK_ENDPOINT,
530
+ rpcUrl: MOCK_RPC_URL,
531
+ feeToken: MOCK_FEE_TOKEN,
532
+ }));
533
+ expect(client.identity.address).toBe(MOCK_WALLET_ADDRESS);
534
+ expect(client.payer.address).toBe(MOCK_PAYER_ADDRESS);
535
+ });
536
+ });
@@ -1,5 +1,5 @@
1
- import { createEmptyClient } from '@solana/kit';
2
- import { koraPlugin } from '../src/kit/plugin.js';
1
+ import { createClient } from '@solana/kit';
2
+ import { koraPlugin } from '../src/plugin.js';
3
3
  // Mock fetch globally
4
4
  const mockFetch = jest.fn();
5
5
  global.fetch = mockFetch;
@@ -12,8 +12,8 @@ describe('Kora Kit Plugin', () => {
12
12
  const mockSuccessfulResponse = (result) => {
13
13
  mockFetch.mockResolvedValueOnce({
14
14
  json: jest.fn().mockResolvedValueOnce({
15
- jsonrpc: '2.0',
16
15
  id: 1,
16
+ jsonrpc: '2.0',
17
17
  result,
18
18
  }),
19
19
  });
@@ -22,9 +22,9 @@ describe('Kora Kit Plugin', () => {
22
22
  const mockErrorResponse = (error) => {
23
23
  mockFetch.mockResolvedValueOnce({
24
24
  json: jest.fn().mockResolvedValueOnce({
25
- jsonrpc: '2.0',
26
- id: 1,
27
25
  error,
26
+ id: 1,
27
+ jsonrpc: '2.0',
28
28
  }),
29
29
  });
30
30
  };
@@ -41,11 +41,14 @@ describe('Kora Kit Plugin', () => {
41
41
  expect(typeof enhanced.kora.getConfig).toBe('function');
42
42
  expect(typeof enhanced.kora.getPayerSigner).toBe('function');
43
43
  expect(typeof enhanced.kora.getBlockhash).toBe('function');
44
+ expect(typeof enhanced.kora.getVersion).toBe('function');
44
45
  expect(typeof enhanced.kora.getSupportedTokens).toBe('function');
45
46
  expect(typeof enhanced.kora.estimateTransactionFee).toBe('function');
47
+ expect(typeof enhanced.kora.estimateBundleFee).toBe('function');
46
48
  expect(typeof enhanced.kora.signTransaction).toBe('function');
47
49
  expect(typeof enhanced.kora.signAndSendTransaction).toBe('function');
48
- expect(typeof enhanced.kora.transferTransaction).toBe('function');
50
+ expect(typeof enhanced.kora.signBundle).toBe('function');
51
+ expect(typeof enhanced.kora.signAndSendBundle).toBe('function');
49
52
  expect(typeof enhanced.kora.getPaymentInstruction).toBe('function');
50
53
  });
51
54
  it('should work with empty client object', () => {
@@ -55,8 +58,8 @@ describe('Kora Kit Plugin', () => {
55
58
  });
56
59
  it('should support authentication options', () => {
57
60
  const authConfig = {
58
- endpoint: mockEndpoint,
59
61
  apiKey: 'test-api-key',
62
+ endpoint: mockEndpoint,
60
63
  hmacSecret: 'test-hmac-secret',
61
64
  };
62
65
  const plugin = koraPlugin(authConfig);
@@ -74,67 +77,67 @@ describe('Kora Kit Plugin', () => {
74
77
  describe('getConfig', () => {
75
78
  it('should return Kit-typed Address arrays', async () => {
76
79
  const rawResponse = {
80
+ enabled_methods: {
81
+ estimate_transaction_fee: true,
82
+ get_blockhash: true,
83
+ get_config: true,
84
+ get_supported_tokens: true,
85
+ liveness: true,
86
+ sign_and_send_transaction: true,
87
+ sign_transaction: true,
88
+ transfer_transaction: true,
89
+ },
77
90
  fee_payers: ['DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7'],
78
91
  validation_config: {
79
- max_allowed_lamports: 1000000,
80
- max_signatures: 10,
81
- price_source: 'Jupiter',
82
92
  allowed_programs: ['11111111111111111111111111111111'],
83
- allowed_tokens: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'],
84
93
  allowed_spl_paid_tokens: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'],
94
+ allowed_tokens: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'],
85
95
  disallowed_accounts: [],
86
96
  fee_payer_policy: {
87
- system: {
97
+ spl_token: {
98
+ allow_approve: true,
99
+ allow_burn: true,
100
+ allow_close_account: true,
101
+ allow_freeze_account: true,
102
+ allow_mint_to: true,
103
+ allow_revoke: true,
104
+ allow_set_authority: true,
105
+ allow_thaw_account: true,
88
106
  allow_transfer: true,
107
+ },
108
+ system: {
109
+ allow_allocate: true,
89
110
  allow_assign: true,
90
111
  allow_create_account: true,
91
- allow_allocate: true,
112
+ allow_transfer: true,
92
113
  nonce: {
93
- allow_initialize: true,
94
114
  allow_advance: true,
95
115
  allow_authorize: true,
116
+ allow_initialize: true,
96
117
  allow_withdraw: true,
97
118
  },
98
119
  },
99
- spl_token: {
100
- allow_transfer: true,
101
- allow_burn: true,
102
- allow_close_account: true,
103
- allow_approve: true,
104
- allow_revoke: true,
105
- allow_set_authority: true,
106
- allow_mint_to: true,
107
- allow_freeze_account: true,
108
- allow_thaw_account: true,
109
- },
110
120
  token_2022: {
111
- allow_transfer: true,
121
+ allow_approve: true,
112
122
  allow_burn: true,
113
123
  allow_close_account: true,
114
- allow_approve: true,
124
+ allow_freeze_account: true,
125
+ allow_mint_to: true,
115
126
  allow_revoke: true,
116
127
  allow_set_authority: true,
117
- allow_mint_to: true,
118
- allow_freeze_account: true,
119
128
  allow_thaw_account: true,
129
+ allow_transfer: true,
120
130
  },
121
131
  },
122
- price: { type: 'margin', margin: 0.1 },
132
+ max_allowed_lamports: 1000000,
133
+ max_signatures: 10,
134
+ price: { margin: 0.1, type: 'margin' },
135
+ price_source: 'Jupiter',
123
136
  token2022: {
124
- blocked_mint_extensions: [],
125
137
  blocked_account_extensions: [],
138
+ blocked_mint_extensions: [],
126
139
  },
127
140
  },
128
- enabled_methods: {
129
- liveness: true,
130
- estimate_transaction_fee: true,
131
- get_supported_tokens: true,
132
- sign_transaction: true,
133
- sign_and_send_transaction: true,
134
- transfer_transaction: true,
135
- get_blockhash: true,
136
- get_config: true,
137
- },
138
141
  };
139
142
  mockSuccessfulResponse(rawResponse);
140
143
  const result = await kora.getConfig();
@@ -146,12 +149,47 @@ describe('Kora Kit Plugin', () => {
146
149
  expect(result.validation_config.allowed_tokens).toHaveLength(1);
147
150
  expect(result.validation_config.allowed_tokens[0]).toBe('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
148
151
  });
152
+ it('should pass through the "All" wildcard for allowed_programs without crashing', async () => {
153
+ // Regression: ProgramsConfig::All serializes as the string "All" on the wire,
154
+ // which would crash the kit wrapper if it called .map() unconditionally.
155
+ const rawResponse = {
156
+ enabled_methods: {
157
+ estimate_transaction_fee: true,
158
+ get_blockhash: true,
159
+ get_config: true,
160
+ get_supported_tokens: true,
161
+ liveness: true,
162
+ sign_and_send_transaction: true,
163
+ sign_transaction: true,
164
+ transfer_transaction: true,
165
+ },
166
+ fee_payers: ['DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7'],
167
+ validation_config: {
168
+ allowed_programs: 'All',
169
+ allowed_spl_paid_tokens: [],
170
+ allowed_tokens: [],
171
+ disallowed_accounts: [],
172
+ fee_payer_policy: {},
173
+ max_allowed_lamports: 1000000,
174
+ max_signatures: 10,
175
+ price: { margin: 0.1, type: 'margin' },
176
+ price_source: 'Jupiter',
177
+ token2022: {
178
+ blocked_account_extensions: [],
179
+ blocked_mint_extensions: [],
180
+ },
181
+ },
182
+ };
183
+ mockSuccessfulResponse(rawResponse);
184
+ const result = await kora.getConfig();
185
+ expect(result.validation_config.allowed_programs).toBe('All');
186
+ });
149
187
  });
150
188
  describe('getPayerSigner', () => {
151
189
  it('should return Kit-typed Address fields', async () => {
152
190
  const rawResponse = {
153
- signer_address: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
154
191
  payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
192
+ signer_address: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
155
193
  };
156
194
  mockSuccessfulResponse(rawResponse);
157
195
  const result = await kora.getPayerSigner();
@@ -174,6 +212,16 @@ describe('Kora Kit Plugin', () => {
174
212
  expect(hash).toBe('4NxM2D4kQcipkzMWBWQME5YSVnj5kT8QKA7rvb3rKLvE');
175
213
  });
176
214
  });
215
+ describe('getVersion', () => {
216
+ it('should return version string', async () => {
217
+ const rawResponse = {
218
+ version: '2.0.0',
219
+ };
220
+ mockSuccessfulResponse(rawResponse);
221
+ const result = await kora.getVersion();
222
+ expect(result.version).toBe('2.0.0');
223
+ });
224
+ });
177
225
  describe('getSupportedTokens', () => {
178
226
  it('should return Kit-typed Address array', async () => {
179
227
  const rawResponse = {
@@ -197,13 +245,13 @@ describe('Kora Kit Plugin', () => {
197
245
  const rawResponse = {
198
246
  fee_in_lamports: 5000,
199
247
  fee_in_token: 50,
200
- signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
201
248
  payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
249
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
202
250
  };
203
251
  mockSuccessfulResponse(rawResponse);
204
252
  const result = await kora.estimateTransactionFee({
205
- transaction: 'base64EncodedTransaction',
206
253
  fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
254
+ transaction: 'base64EncodedTransaction',
207
255
  });
208
256
  // Type assertions
209
257
  const signerPubkey = result.signer_pubkey;
@@ -214,6 +262,28 @@ describe('Kora Kit Plugin', () => {
214
262
  expect(paymentAddr).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
215
263
  });
216
264
  });
265
+ describe('estimateBundleFee', () => {
266
+ it('should return Kit-typed Address fields for bundle', async () => {
267
+ const rawResponse = {
268
+ fee_in_lamports: 15000,
269
+ fee_in_token: 150,
270
+ payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
271
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
272
+ };
273
+ mockSuccessfulResponse(rawResponse);
274
+ const result = await kora.estimateBundleFee({
275
+ fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
276
+ transactions: ['base64Tx1', 'base64Tx2', 'base64Tx3'],
277
+ });
278
+ // Type assertions
279
+ const signerPubkey = result.signer_pubkey;
280
+ const paymentAddr = result.payment_address;
281
+ expect(result.fee_in_lamports).toBe(15000);
282
+ expect(result.fee_in_token).toBe(150);
283
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
284
+ expect(paymentAddr).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
285
+ });
286
+ });
217
287
  describe('signTransaction', () => {
218
288
  it('should return Kit-typed response with Base64EncodedWireTransaction', async () => {
219
289
  const rawResponse = {
@@ -232,9 +302,11 @@ describe('Kora Kit Plugin', () => {
232
302
  });
233
303
  });
234
304
  describe('signAndSendTransaction', () => {
235
- it('should return Kit-typed response with Base64EncodedWireTransaction', async () => {
305
+ it('should return Kit-typed response with Signature and Base64EncodedWireTransaction', async () => {
306
+ // Use a valid base58 signature (88 characters, valid base58 alphabet)
307
+ const mockSignature = '5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW';
236
308
  const rawResponse = {
237
- signature: '5wBzExmp8yR5M6m4KjV8WT9T6B1NMQkaMbsFWqBoDPBMYWxDx6EuSGxNqKfXnBhDhAkEqMiGRjEwKnGhSN3pi3n',
309
+ signature: mockSignature,
238
310
  signed_transaction: 'base64SignedTransaction',
239
311
  signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
240
312
  };
@@ -243,37 +315,52 @@ describe('Kora Kit Plugin', () => {
243
315
  transaction: 'base64EncodedTransaction',
244
316
  });
245
317
  // Type assertions - verify Kit types
318
+ const sig = result.signature;
246
319
  const signedTx = result.signed_transaction;
247
320
  const signerPubkey = result.signer_pubkey;
321
+ expect(sig).toBe(mockSignature);
248
322
  expect(signedTx).toBe('base64SignedTransaction');
249
323
  expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
250
324
  });
251
325
  });
252
- describe('transferTransaction', () => {
253
- it('should return Kit-typed response with Base64EncodedWireTransaction and Blockhash', async () => {
326
+ describe('signBundle', () => {
327
+ it('should return Kit-typed response with Base64EncodedWireTransaction array', async () => {
254
328
  const rawResponse = {
255
- transaction: 'base64Transaction',
256
- message: 'base64Message',
257
- blockhash: '4NxM2D4kQcipkzMWBWQME5YSVnj5kT8QKA7rvb3rKLvE',
329
+ signed_transactions: ['base64SignedTx1', 'base64SignedTx2'],
258
330
  signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
259
- instructions: [],
260
331
  };
261
332
  mockSuccessfulResponse(rawResponse);
262
- const result = await kora.transferTransaction({
263
- amount: 1000000,
264
- token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
265
- source: 'sourceWallet',
266
- destination: 'destWallet',
333
+ const result = await kora.signBundle({
334
+ transactions: ['base64Tx1', 'base64Tx2'],
267
335
  });
268
336
  // Type assertions - verify Kit types
269
- const tx = result.transaction;
270
- const hash = result.blockhash;
337
+ const signedTxs = result.signed_transactions;
271
338
  const signerPubkey = result.signer_pubkey;
272
- expect(tx).toBe('base64Transaction');
273
- expect(result.message).toBe('base64Message');
274
- expect(hash).toBe('4NxM2D4kQcipkzMWBWQME5YSVnj5kT8QKA7rvb3rKLvE');
339
+ expect(signedTxs).toHaveLength(2);
340
+ expect(signedTxs[0]).toBe('base64SignedTx1');
341
+ expect(signedTxs[1]).toBe('base64SignedTx2');
275
342
  expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
276
- expect(result.instructions).toEqual([]);
343
+ });
344
+ });
345
+ describe('signAndSendBundle', () => {
346
+ it('should return Kit-typed response with Base64EncodedWireTransaction array and bundle UUID', async () => {
347
+ const rawResponse = {
348
+ bundle_uuid: 'jito-bundle-uuid-12345',
349
+ signed_transactions: ['base64SignedTx1', 'base64SignedTx2'],
350
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
351
+ };
352
+ mockSuccessfulResponse(rawResponse);
353
+ const result = await kora.signAndSendBundle({
354
+ transactions: ['base64Tx1', 'base64Tx2'],
355
+ });
356
+ // Type assertions - verify Kit types
357
+ const signedTxs = result.signed_transactions;
358
+ const signerPubkey = result.signer_pubkey;
359
+ expect(signedTxs).toHaveLength(2);
360
+ expect(signedTxs[0]).toBe('base64SignedTx1');
361
+ expect(signedTxs[1]).toBe('base64SignedTx2');
362
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
363
+ expect(result.bundle_uuid).toBe('jito-bundle-uuid-12345');
277
364
  });
278
365
  });
279
366
  describe('getPaymentInstruction', () => {
@@ -281,16 +368,16 @@ describe('Kora Kit Plugin', () => {
281
368
  const mockFeeEstimate = {
282
369
  fee_in_lamports: 5000,
283
370
  fee_in_token: 50000,
284
- signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
285
371
  payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
372
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
286
373
  };
287
374
  const testTx = 'Aoq7ymA5OGP+gmDXiY5m3cYXlY2Rz/a/gFjOgt9ZuoCS7UzuiGGaEnW2OOtvHvMQHkkD7Z4LRF5B63ftu+1oZwIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgECB1urjQEjgFgzqYhJ8IXJeSg4cJP1j1g2CJstOQTDchOKUzqH3PxgGW3c4V3vZV05A5Y30/MggOBs0Kd00s1JEwg5TaEeaV4+KL2y7fXIAuf6cN0ZQitbhY+G9ExtBSChspOXPgNcy9pYpETe4bmB+fg4bfZx1tnicA/kIyyubczAmbcIKIuniNOOQYG2ggKCz8NjEsHVezrWMatndu1wk6J5miGP26J6Vwp31AljiAajAFuP0D9mWJwSeFuA7J5rPwbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpd/O36SW02zRtNtqk6GFeip2+yBQsVTeSbLL4rWJRkd4CBgQCBQQBCgxAQg8AAAAAAAYGBAIFAwEKDBAnAAAAAAAABg==';
288
375
  mockSuccessfulResponse(mockFeeEstimate);
289
376
  const result = await kora.getPaymentInstruction({
290
- transaction: testTx,
291
377
  fee_token: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',
292
378
  source_wallet: '11111111111111111111111111111111',
293
379
  token_program_id: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
380
+ transaction: testTx,
294
381
  });
295
382
  // Type assertions - verify Kit types
296
383
  const originalTx = result.original_transaction;
@@ -314,48 +401,51 @@ describe('Kora Kit Plugin', () => {
314
401
  });
315
402
  it('should propagate RPC errors', async () => {
316
403
  mockErrorResponse({ code: -32601, message: 'Method not found' });
317
- await expect(kora.getConfig()).rejects.toThrow('RPC Error -32601: Method not found');
404
+ await expect(kora.getConfig()).rejects.toThrow('Kora Error -32601: Method not found');
318
405
  });
319
406
  it('should propagate network errors', async () => {
320
407
  mockFetch.mockRejectedValueOnce(new Error('Network error'));
321
408
  await expect(kora.getConfig()).rejects.toThrow('Network error');
322
409
  });
323
410
  });
324
- describe('KoraPlugin Type Export', () => {
325
- it('should export KoraPlugin type correctly', () => {
326
- // This test verifies the KoraPlugin type is correctly exported
411
+ describe('KoraApi Type Export', () => {
412
+ it('should export KoraApi type correctly', () => {
413
+ // This test verifies the KoraApi type is correctly exported
327
414
  const plugin = koraPlugin(mockConfig);
328
415
  const client = plugin({});
329
- // Type check - assign to KoraPlugin type
416
+ // Type check - assign to KoraApi type
330
417
  const api = client.kora;
331
418
  expect(api).toBeDefined();
332
419
  });
333
420
  });
334
- describe('createEmptyClient Integration', () => {
421
+ describe('createClient Integration', () => {
335
422
  it('should initialize kora property on Kit client', () => {
336
- const client = createEmptyClient().use(koraPlugin(mockConfig));
423
+ const client = createClient().use(koraPlugin(mockConfig));
337
424
  expect(client).toHaveProperty('kora');
338
425
  expect(client.kora).toBeDefined();
339
426
  });
340
427
  it('should expose all Kora RPC methods', () => {
341
- const client = createEmptyClient().use(koraPlugin(mockConfig));
428
+ const client = createClient().use(koraPlugin(mockConfig));
342
429
  expect(typeof client.kora.getConfig).toBe('function');
343
430
  expect(typeof client.kora.getPayerSigner).toBe('function');
344
431
  expect(typeof client.kora.getBlockhash).toBe('function');
432
+ expect(typeof client.kora.getVersion).toBe('function');
345
433
  expect(typeof client.kora.getSupportedTokens).toBe('function');
346
434
  expect(typeof client.kora.estimateTransactionFee).toBe('function');
435
+ expect(typeof client.kora.estimateBundleFee).toBe('function');
347
436
  expect(typeof client.kora.signTransaction).toBe('function');
348
437
  expect(typeof client.kora.signAndSendTransaction).toBe('function');
349
- expect(typeof client.kora.transferTransaction).toBe('function');
438
+ expect(typeof client.kora.signBundle).toBe('function');
439
+ expect(typeof client.kora.signAndSendBundle).toBe('function');
350
440
  expect(typeof client.kora.getPaymentInstruction).toBe('function');
351
441
  });
352
442
  it('should work with authentication config', () => {
353
443
  const authConfig = {
354
- endpoint: mockEndpoint,
355
444
  apiKey: 'test-api-key',
445
+ endpoint: mockEndpoint,
356
446
  hmacSecret: 'test-hmac-secret',
357
447
  };
358
- const client = createEmptyClient().use(koraPlugin(authConfig));
448
+ const client = createClient().use(koraPlugin(authConfig));
359
449
  expect(client.kora).toBeDefined();
360
450
  expect(typeof client.kora.getConfig).toBe('function');
361
451
  });
@@ -365,7 +455,7 @@ describe('Kora Kit Plugin', () => {
365
455
  ...c,
366
456
  other: { foo: () => 'bar' },
367
457
  });
368
- const client = createEmptyClient().use(koraPlugin(mockConfig)).use(otherPlugin);
458
+ const client = createClient().use(koraPlugin(mockConfig)).use(otherPlugin);
369
459
  // Both plugins should be available
370
460
  expect(client.kora).toBeDefined();
371
461
  expect(client.other).toBeDefined();
@@ -374,11 +464,11 @@ describe('Kora Kit Plugin', () => {
374
464
  });
375
465
  it('should call RPC methods correctly', async () => {
376
466
  const mockResponse = {
377
- signer_address: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
378
467
  payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
468
+ signer_address: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
379
469
  };
380
470
  mockSuccessfulResponse(mockResponse);
381
- const client = createEmptyClient().use(koraPlugin(mockConfig));
471
+ const client = createClient().use(koraPlugin(mockConfig));
382
472
  const result = await client.kora.getPayerSigner();
383
473
  expect(result.signer_address).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
384
474
  expect(result.payment_address).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
@@ -1,30 +1,23 @@
1
- import { Commitment, KeyPairSigner, Address } from '@solana/kit';
1
+ import { Address, KeyPairSigner } from '@solana/kit';
2
2
  import { KoraClient } from '../src/index.js';
3
3
  interface TestSuite {
4
+ destinationAddress: Address<string>;
5
+ koraAddress: Address<string>;
4
6
  koraClient: KoraClient;
5
7
  koraRpcUrl: string;
6
8
  testWallet: KeyPairSigner<string>;
7
9
  usdcMint: Address<string>;
8
- destinationAddress: Address<string>;
9
- koraAddress: Address<string>;
10
- authConfig?: {
11
- apiKey: string;
12
- hmacSecret: string;
13
- };
14
10
  }
15
11
  export declare function loadEnvironmentVariables(): {
16
- koraRpcUrl: string;
12
+ destinationAddress: Address<string>;
17
13
  koraAddress: Address<string>;
14
+ koraRpcUrl: string;
18
15
  koraSignerType: string;
19
- commitment: Commitment;
20
- tokenDecimals: number;
21
- tokenDropAmount: number;
22
16
  solDropAmount: bigint;
23
- solanaRpcUrl: string;
24
- solanaWsUrl: string;
25
- testWalletSecret: string;
26
17
  testUsdcMintSecret: string;
27
- destinationAddress: Address<string>;
18
+ testWalletSecret: string;
19
+ tokenDecimals: number;
20
+ tokenDropAmount: number;
28
21
  };
29
22
  declare function setupTestSuite(): Promise<TestSuite>;
30
23
  export default setupTestSuite;