@solana/kora 0.2.0-beta.1 → 0.2.0-beta.3

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.
@@ -0,0 +1,442 @@
1
+ import { createEmptyClient } from '@solana/kit';
2
+ import { koraPlugin } from '../src/plugin.js';
3
+ // Mock fetch globally
4
+ const mockFetch = jest.fn();
5
+ global.fetch = mockFetch;
6
+ describe('Kora Kit Plugin', () => {
7
+ const mockEndpoint = 'http://localhost:8080';
8
+ const mockConfig = {
9
+ endpoint: mockEndpoint,
10
+ };
11
+ // Helper to mock successful RPC response
12
+ const mockSuccessfulResponse = (result) => {
13
+ mockFetch.mockResolvedValueOnce({
14
+ json: jest.fn().mockResolvedValueOnce({
15
+ id: 1,
16
+ jsonrpc: '2.0',
17
+ result,
18
+ }),
19
+ });
20
+ };
21
+ // Helper to mock error response
22
+ const mockErrorResponse = (error) => {
23
+ mockFetch.mockResolvedValueOnce({
24
+ json: jest.fn().mockResolvedValueOnce({
25
+ error,
26
+ id: 1,
27
+ jsonrpc: '2.0',
28
+ }),
29
+ });
30
+ };
31
+ beforeEach(() => {
32
+ mockFetch.mockClear();
33
+ });
34
+ describe('Plugin Composition', () => {
35
+ it('should add kora property to client', () => {
36
+ const baseClient = { existing: 'property' };
37
+ const plugin = koraPlugin(mockConfig);
38
+ const enhanced = plugin(baseClient);
39
+ expect(enhanced.existing).toBe('property');
40
+ expect(enhanced.kora).toBeDefined();
41
+ expect(typeof enhanced.kora.getConfig).toBe('function');
42
+ expect(typeof enhanced.kora.getPayerSigner).toBe('function');
43
+ expect(typeof enhanced.kora.getBlockhash).toBe('function');
44
+ expect(typeof enhanced.kora.getVersion).toBe('function');
45
+ expect(typeof enhanced.kora.getSupportedTokens).toBe('function');
46
+ expect(typeof enhanced.kora.estimateTransactionFee).toBe('function');
47
+ expect(typeof enhanced.kora.estimateBundleFee).toBe('function');
48
+ expect(typeof enhanced.kora.signTransaction).toBe('function');
49
+ expect(typeof enhanced.kora.signAndSendTransaction).toBe('function');
50
+ expect(typeof enhanced.kora.signBundle).toBe('function');
51
+ expect(typeof enhanced.kora.signAndSendBundle).toBe('function');
52
+ expect(typeof enhanced.kora.getPaymentInstruction).toBe('function');
53
+ });
54
+ it('should work with empty client object', () => {
55
+ const plugin = koraPlugin(mockConfig);
56
+ const enhanced = plugin({});
57
+ expect(enhanced.kora).toBeDefined();
58
+ });
59
+ it('should support authentication options', () => {
60
+ const authConfig = {
61
+ apiKey: 'test-api-key',
62
+ endpoint: mockEndpoint,
63
+ hmacSecret: 'test-hmac-secret',
64
+ };
65
+ const plugin = koraPlugin(authConfig);
66
+ const enhanced = plugin({});
67
+ expect(enhanced.kora).toBeDefined();
68
+ });
69
+ });
70
+ describe('Type Casting', () => {
71
+ let kora;
72
+ beforeEach(() => {
73
+ const plugin = koraPlugin(mockConfig);
74
+ const client = plugin({});
75
+ kora = client.kora;
76
+ });
77
+ describe('getConfig', () => {
78
+ it('should return Kit-typed Address arrays', async () => {
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
+ },
90
+ fee_payers: ['DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7'],
91
+ validation_config: {
92
+ allowed_programs: ['11111111111111111111111111111111'],
93
+ allowed_spl_paid_tokens: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'],
94
+ allowed_tokens: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'],
95
+ disallowed_accounts: [],
96
+ fee_payer_policy: {
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,
106
+ allow_transfer: true,
107
+ },
108
+ system: {
109
+ allow_allocate: true,
110
+ allow_assign: true,
111
+ allow_create_account: true,
112
+ allow_transfer: true,
113
+ nonce: {
114
+ allow_advance: true,
115
+ allow_authorize: true,
116
+ allow_initialize: true,
117
+ allow_withdraw: true,
118
+ },
119
+ },
120
+ token_2022: {
121
+ allow_approve: true,
122
+ allow_burn: true,
123
+ allow_close_account: true,
124
+ allow_freeze_account: true,
125
+ allow_mint_to: true,
126
+ allow_revoke: true,
127
+ allow_set_authority: true,
128
+ allow_thaw_account: true,
129
+ allow_transfer: true,
130
+ },
131
+ },
132
+ max_allowed_lamports: 1000000,
133
+ max_signatures: 10,
134
+ price: { margin: 0.1, type: 'margin' },
135
+ price_source: 'Jupiter',
136
+ token2022: {
137
+ blocked_account_extensions: [],
138
+ blocked_mint_extensions: [],
139
+ },
140
+ },
141
+ };
142
+ mockSuccessfulResponse(rawResponse);
143
+ const result = await kora.getConfig();
144
+ // Verify type casting - these should be Address types
145
+ expect(result.fee_payers).toHaveLength(1);
146
+ expect(result.fee_payers[0]).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
147
+ expect(result.validation_config.allowed_programs).toHaveLength(1);
148
+ expect(result.validation_config.allowed_programs[0]).toBe('11111111111111111111111111111111');
149
+ expect(result.validation_config.allowed_tokens).toHaveLength(1);
150
+ expect(result.validation_config.allowed_tokens[0]).toBe('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
151
+ });
152
+ });
153
+ describe('getPayerSigner', () => {
154
+ it('should return Kit-typed Address fields', async () => {
155
+ const rawResponse = {
156
+ payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
157
+ signer_address: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
158
+ };
159
+ mockSuccessfulResponse(rawResponse);
160
+ const result = await kora.getPayerSigner();
161
+ // Type assertion - these should be Address types
162
+ const signerAddr = result.signer_address;
163
+ const paymentAddr = result.payment_address;
164
+ expect(signerAddr).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
165
+ expect(paymentAddr).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
166
+ });
167
+ });
168
+ describe('getBlockhash', () => {
169
+ it('should return Kit-typed Blockhash field', async () => {
170
+ const rawResponse = {
171
+ blockhash: '4NxM2D4kQcipkzMWBWQME5YSVnj5kT8QKA7rvb3rKLvE',
172
+ };
173
+ mockSuccessfulResponse(rawResponse);
174
+ const result = await kora.getBlockhash();
175
+ // Type assertion - should be Blockhash type
176
+ const hash = result.blockhash;
177
+ expect(hash).toBe('4NxM2D4kQcipkzMWBWQME5YSVnj5kT8QKA7rvb3rKLvE');
178
+ });
179
+ });
180
+ describe('getVersion', () => {
181
+ it('should return version string', async () => {
182
+ const rawResponse = {
183
+ version: '2.0.0',
184
+ };
185
+ mockSuccessfulResponse(rawResponse);
186
+ const result = await kora.getVersion();
187
+ expect(result.version).toBe('2.0.0');
188
+ });
189
+ });
190
+ describe('getSupportedTokens', () => {
191
+ it('should return Kit-typed Address array', async () => {
192
+ const rawResponse = {
193
+ tokens: [
194
+ 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
195
+ 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
196
+ ],
197
+ };
198
+ mockSuccessfulResponse(rawResponse);
199
+ const result = await kora.getSupportedTokens();
200
+ // Type assertion - these should be Address types
201
+ expect(result.tokens).toHaveLength(2);
202
+ const token0 = result.tokens[0];
203
+ const token1 = result.tokens[1];
204
+ expect(token0).toBe('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
205
+ expect(token1).toBe('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB');
206
+ });
207
+ });
208
+ describe('estimateTransactionFee', () => {
209
+ it('should return Kit-typed Address fields', async () => {
210
+ const rawResponse = {
211
+ fee_in_lamports: 5000,
212
+ fee_in_token: 50,
213
+ payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
214
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
215
+ };
216
+ mockSuccessfulResponse(rawResponse);
217
+ const result = await kora.estimateTransactionFee({
218
+ fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
219
+ transaction: 'base64EncodedTransaction',
220
+ });
221
+ // Type assertions
222
+ const signerPubkey = result.signer_pubkey;
223
+ const paymentAddr = result.payment_address;
224
+ expect(result.fee_in_lamports).toBe(5000);
225
+ expect(result.fee_in_token).toBe(50);
226
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
227
+ expect(paymentAddr).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
228
+ });
229
+ });
230
+ describe('estimateBundleFee', () => {
231
+ it('should return Kit-typed Address fields for bundle', async () => {
232
+ const rawResponse = {
233
+ fee_in_lamports: 15000,
234
+ fee_in_token: 150,
235
+ payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
236
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
237
+ };
238
+ mockSuccessfulResponse(rawResponse);
239
+ const result = await kora.estimateBundleFee({
240
+ fee_token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
241
+ transactions: ['base64Tx1', 'base64Tx2', 'base64Tx3'],
242
+ });
243
+ // Type assertions
244
+ const signerPubkey = result.signer_pubkey;
245
+ const paymentAddr = result.payment_address;
246
+ expect(result.fee_in_lamports).toBe(15000);
247
+ expect(result.fee_in_token).toBe(150);
248
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
249
+ expect(paymentAddr).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
250
+ });
251
+ });
252
+ describe('signTransaction', () => {
253
+ it('should return Kit-typed response with Base64EncodedWireTransaction', async () => {
254
+ const rawResponse = {
255
+ signed_transaction: 'base64SignedTransaction',
256
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
257
+ };
258
+ mockSuccessfulResponse(rawResponse);
259
+ const result = await kora.signTransaction({
260
+ transaction: 'base64EncodedTransaction',
261
+ });
262
+ // Type assertions - verify Kit types
263
+ const signedTx = result.signed_transaction;
264
+ const signerPubkey = result.signer_pubkey;
265
+ expect(signedTx).toBe('base64SignedTransaction');
266
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
267
+ });
268
+ });
269
+ describe('signAndSendTransaction', () => {
270
+ it('should return Kit-typed response with Signature and Base64EncodedWireTransaction', async () => {
271
+ // Use a valid base58 signature (88 characters, valid base58 alphabet)
272
+ const mockSignature = '5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW';
273
+ const rawResponse = {
274
+ signature: mockSignature,
275
+ signed_transaction: 'base64SignedTransaction',
276
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
277
+ };
278
+ mockSuccessfulResponse(rawResponse);
279
+ const result = await kora.signAndSendTransaction({
280
+ transaction: 'base64EncodedTransaction',
281
+ });
282
+ // Type assertions - verify Kit types
283
+ const sig = result.signature;
284
+ const signedTx = result.signed_transaction;
285
+ const signerPubkey = result.signer_pubkey;
286
+ expect(sig).toBe(mockSignature);
287
+ expect(signedTx).toBe('base64SignedTransaction');
288
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
289
+ });
290
+ });
291
+ describe('signBundle', () => {
292
+ it('should return Kit-typed response with Base64EncodedWireTransaction array', async () => {
293
+ const rawResponse = {
294
+ signed_transactions: ['base64SignedTx1', 'base64SignedTx2'],
295
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
296
+ };
297
+ mockSuccessfulResponse(rawResponse);
298
+ const result = await kora.signBundle({
299
+ transactions: ['base64Tx1', 'base64Tx2'],
300
+ });
301
+ // Type assertions - verify Kit types
302
+ const signedTxs = result.signed_transactions;
303
+ const signerPubkey = result.signer_pubkey;
304
+ expect(signedTxs).toHaveLength(2);
305
+ expect(signedTxs[0]).toBe('base64SignedTx1');
306
+ expect(signedTxs[1]).toBe('base64SignedTx2');
307
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
308
+ });
309
+ });
310
+ describe('signAndSendBundle', () => {
311
+ it('should return Kit-typed response with Base64EncodedWireTransaction array and bundle UUID', async () => {
312
+ const rawResponse = {
313
+ bundle_uuid: 'jito-bundle-uuid-12345',
314
+ signed_transactions: ['base64SignedTx1', 'base64SignedTx2'],
315
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
316
+ };
317
+ mockSuccessfulResponse(rawResponse);
318
+ const result = await kora.signAndSendBundle({
319
+ transactions: ['base64Tx1', 'base64Tx2'],
320
+ });
321
+ // Type assertions - verify Kit types
322
+ const signedTxs = result.signed_transactions;
323
+ const signerPubkey = result.signer_pubkey;
324
+ expect(signedTxs).toHaveLength(2);
325
+ expect(signedTxs[0]).toBe('base64SignedTx1');
326
+ expect(signedTxs[1]).toBe('base64SignedTx2');
327
+ expect(signerPubkey).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
328
+ expect(result.bundle_uuid).toBe('jito-bundle-uuid-12345');
329
+ });
330
+ });
331
+ describe('getPaymentInstruction', () => {
332
+ it('should return Kit-typed response with Base64EncodedWireTransaction and Address fields', async () => {
333
+ const mockFeeEstimate = {
334
+ fee_in_lamports: 5000,
335
+ fee_in_token: 50000,
336
+ payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
337
+ signer_pubkey: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
338
+ };
339
+ const testTx = 'Aoq7ymA5OGP+gmDXiY5m3cYXlY2Rz/a/gFjOgt9ZuoCS7UzuiGGaEnW2OOtvHvMQHkkD7Z4LRF5B63ftu+1oZwIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgECB1urjQEjgFgzqYhJ8IXJeSg4cJP1j1g2CJstOQTDchOKUzqH3PxgGW3c4V3vZV05A5Y30/MggOBs0Kd00s1JEwg5TaEeaV4+KL2y7fXIAuf6cN0ZQitbhY+G9ExtBSChspOXPgNcy9pYpETe4bmB+fg4bfZx1tnicA/kIyyubczAmbcIKIuniNOOQYG2ggKCz8NjEsHVezrWMatndu1wk6J5miGP26J6Vwp31AljiAajAFuP0D9mWJwSeFuA7J5rPwbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpd/O36SW02zRtNtqk6GFeip2+yBQsVTeSbLL4rWJRkd4CBgQCBQQBCgxAQg8AAAAAAAYGBAIFAwEKDBAnAAAAAAAABg==';
340
+ mockSuccessfulResponse(mockFeeEstimate);
341
+ const result = await kora.getPaymentInstruction({
342
+ fee_token: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',
343
+ source_wallet: '11111111111111111111111111111111',
344
+ token_program_id: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
345
+ transaction: testTx,
346
+ });
347
+ // Type assertions - verify Kit types
348
+ const originalTx = result.original_transaction;
349
+ const paymentToken = result.payment_token;
350
+ const paymentAddr = result.payment_address;
351
+ const signerAddr = result.signer_address;
352
+ expect(originalTx).toBe(testTx);
353
+ expect(paymentToken).toBe('4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU');
354
+ expect(paymentAddr).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
355
+ expect(signerAddr).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
356
+ expect(result.payment_amount).toBe(50000);
357
+ });
358
+ });
359
+ });
360
+ describe('Error Handling', () => {
361
+ let kora;
362
+ beforeEach(() => {
363
+ const plugin = koraPlugin(mockConfig);
364
+ const client = plugin({});
365
+ kora = client.kora;
366
+ });
367
+ it('should propagate RPC errors', async () => {
368
+ mockErrorResponse({ code: -32601, message: 'Method not found' });
369
+ await expect(kora.getConfig()).rejects.toThrow('RPC Error -32601: Method not found');
370
+ });
371
+ it('should propagate network errors', async () => {
372
+ mockFetch.mockRejectedValueOnce(new Error('Network error'));
373
+ await expect(kora.getConfig()).rejects.toThrow('Network error');
374
+ });
375
+ });
376
+ describe('KoraApi Type Export', () => {
377
+ it('should export KoraApi type correctly', () => {
378
+ // This test verifies the KoraApi type is correctly exported
379
+ const plugin = koraPlugin(mockConfig);
380
+ const client = plugin({});
381
+ // Type check - assign to KoraApi type
382
+ const api = client.kora;
383
+ expect(api).toBeDefined();
384
+ });
385
+ });
386
+ describe('createEmptyClient Integration', () => {
387
+ it('should initialize kora property on Kit client', () => {
388
+ const client = createEmptyClient().use(koraPlugin(mockConfig));
389
+ expect(client).toHaveProperty('kora');
390
+ expect(client.kora).toBeDefined();
391
+ });
392
+ it('should expose all Kora RPC methods', () => {
393
+ const client = createEmptyClient().use(koraPlugin(mockConfig));
394
+ expect(typeof client.kora.getConfig).toBe('function');
395
+ expect(typeof client.kora.getPayerSigner).toBe('function');
396
+ expect(typeof client.kora.getBlockhash).toBe('function');
397
+ expect(typeof client.kora.getVersion).toBe('function');
398
+ expect(typeof client.kora.getSupportedTokens).toBe('function');
399
+ expect(typeof client.kora.estimateTransactionFee).toBe('function');
400
+ expect(typeof client.kora.estimateBundleFee).toBe('function');
401
+ expect(typeof client.kora.signTransaction).toBe('function');
402
+ expect(typeof client.kora.signAndSendTransaction).toBe('function');
403
+ expect(typeof client.kora.signBundle).toBe('function');
404
+ expect(typeof client.kora.signAndSendBundle).toBe('function');
405
+ expect(typeof client.kora.getPaymentInstruction).toBe('function');
406
+ });
407
+ it('should work with authentication config', () => {
408
+ const authConfig = {
409
+ apiKey: 'test-api-key',
410
+ endpoint: mockEndpoint,
411
+ hmacSecret: 'test-hmac-secret',
412
+ };
413
+ const client = createEmptyClient().use(koraPlugin(authConfig));
414
+ expect(client.kora).toBeDefined();
415
+ expect(typeof client.kora.getConfig).toBe('function');
416
+ });
417
+ it('should compose with other plugins', () => {
418
+ // Simulate another plugin that adds a different property
419
+ const otherPlugin = (c) => ({
420
+ ...c,
421
+ other: { foo: () => 'bar' },
422
+ });
423
+ const client = createEmptyClient().use(koraPlugin(mockConfig)).use(otherPlugin);
424
+ // Both plugins should be available
425
+ expect(client.kora).toBeDefined();
426
+ expect(client.other).toBeDefined();
427
+ expect(typeof client.kora.getConfig).toBe('function');
428
+ expect(client.other.foo()).toBe('bar');
429
+ });
430
+ it('should call RPC methods correctly', async () => {
431
+ const mockResponse = {
432
+ payment_address: 'PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
433
+ signer_address: 'DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7',
434
+ };
435
+ mockSuccessfulResponse(mockResponse);
436
+ const client = createEmptyClient().use(koraPlugin(mockConfig));
437
+ const result = await client.kora.getPayerSigner();
438
+ expect(result.signer_address).toBe('DemoKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
439
+ expect(result.payment_address).toBe('PayKMZWkk483QoFPLRPQ2XVKB7bWnuXwSjvDE1JsWk7');
440
+ });
441
+ });
442
+ });
@@ -1,26 +1,26 @@
1
- import { Commitment, KeyPairSigner, Address } from '@solana/kit';
1
+ import { Address, Commitment, 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
10
  }
11
11
  export declare function loadEnvironmentVariables(): {
12
- koraRpcUrl: string;
12
+ commitment: Commitment;
13
+ destinationAddress: Address<string>;
13
14
  koraAddress: Address<string>;
15
+ koraRpcUrl: string;
14
16
  koraSignerType: string;
15
- commitment: Commitment;
16
- tokenDecimals: number;
17
- tokenDropAmount: number;
18
17
  solDropAmount: bigint;
19
18
  solanaRpcUrl: string;
20
19
  solanaWsUrl: string;
21
- testWalletSecret: string;
22
20
  testUsdcMintSecret: string;
23
- destinationAddress: Address<string>;
21
+ testWalletSecret: string;
22
+ tokenDecimals: number;
23
+ tokenDropAmount: number;
24
24
  };
25
25
  declare function setupTestSuite(): Promise<TestSuite>;
26
26
  export default setupTestSuite;
@@ -1,25 +1,28 @@
1
+ import { airdropFactory, appendTransactionMessageInstructions, assertIsAddress, assertIsSendableTransaction, assertIsTransactionWithBlockhashLifetime, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, createTransactionMessage, getBase58Encoder, getSignatureFromTransaction, lamports, pipe, sendAndConfirmTransactionFactory, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from '@solana/kit';
2
+ import { MAX_COMPUTE_UNIT_LIMIT, updateOrAppendSetComputeUnitLimitInstruction, updateOrAppendSetComputeUnitPriceInstruction, } from '@solana-program/compute-budget';
1
3
  import { getCreateAccountInstruction } from '@solana-program/system';
2
4
  import { findAssociatedTokenPda, getCreateAssociatedTokenIdempotentInstructionAsync, getInitializeMintInstruction, getMintSize, getMintToInstruction, TOKEN_PROGRAM_ADDRESS, } from '@solana-program/token';
3
- import { airdropFactory, createSolanaRpc, createSolanaRpcSubscriptions, lamports, sendAndConfirmTransactionFactory, pipe, createTransactionMessage, setTransactionMessageLifetimeUsingBlockhash, setTransactionMessageFeePayerSigner, appendTransactionMessageInstructions, signTransactionMessageWithSigners, getSignatureFromTransaction, assertIsAddress, createKeyPairSignerFromBytes, getBase58Encoder, assertIsSendableTransaction, assertIsTransactionWithBlockhashLifetime, } from '@solana/kit';
4
- import { updateOrAppendSetComputeUnitLimitInstruction, updateOrAppendSetComputeUnitPriceInstruction, MAX_COMPUTE_UNIT_LIMIT, } from '@solana-program/compute-budget';
5
5
  import { config } from 'dotenv';
6
6
  import path from 'path';
7
7
  import { KoraClient } from '../src/index.js';
8
8
  config({ path: path.resolve(process.cwd(), '.env') });
9
9
  const DEFAULTS = {
10
+ COMMITMENT: 'processed',
10
11
  DECIMALS: 6,
11
- TOKEN_DROP_AMOUNT: 100_000,
12
+ // Make sure this matches the USDC mint in kora.toml (9BgeTKqmFsPVnfYscfM6NvsgmZxei7XfdciShQ6D3bxJ)
13
+ DESTINATION_ADDRESS: 'AVmDft8deQEo78bRKcGN5ZMf3hyjeLBK4Rd4xGB46yQM',
14
+ // DO NOT USE THESE KEYPAIRS IN PRODUCTION, TESTING KEYPAIRS ONLY
15
+ KORA_ADDRESS: '7AqpcUvgJ7Kh1VmJZ44rWp2XDow33vswo9VK9VqpPU2d',
12
16
  KORA_RPC_URL: 'http://localhost:8080/',
17
+ KORA_SIGNER_TYPE: 'memory',
18
+ // Make sure this matches the kora-rpc signer address on launch (root .env)
19
+ SENDER_SECRET: 'tzgfgSWTE3KUA6qfRoFYLaSfJm59uUeZRDy4ybMrLn1JV2drA1mftiaEcVFvq1Lok6h6EX2C4Y9kSKLvQWyMpS5',
13
20
  SOLANA_RPC_URL: 'http://127.0.0.1:8899',
14
21
  SOLANA_WS_URL: 'ws://127.0.0.1:8900',
15
- COMMITMENT: 'processed',
16
22
  SOL_DROP_AMOUNT: 1_000_000_000,
17
- // DO NOT USE THESE KEYPAIRS IN PRODUCTION, TESTING KEYPAIRS ONLY
18
- KORA_ADDRESS: '7AqpcUvgJ7Kh1VmJZ44rWp2XDow33vswo9VK9VqpPU2d', // Make sure this matches the kora-rpc signer address on launch (root .env)
19
- SENDER_SECRET: 'tzgfgSWTE3KUA6qfRoFYLaSfJm59uUeZRDy4ybMrLn1JV2drA1mftiaEcVFvq1Lok6h6EX2C4Y9kSKLvQWyMpS5', // HhA5j2rRiPbMrpF2ZD36r69FyZf3zWmEHRNSZbbNdVjf
20
- TEST_USDC_MINT_SECRET: '59kKmXphL5UJANqpFFjtH17emEq3oRNmYsx6a3P3vSGJRmhMgVdzH77bkNEi9bArRViT45e8L2TsuPxKNFoc3Qfg', // Make sure this matches the USDC mint in kora.toml (9BgeTKqmFsPVnfYscfM6NvsgmZxei7XfdciShQ6D3bxJ)
21
- DESTINATION_ADDRESS: 'AVmDft8deQEo78bRKcGN5ZMf3hyjeLBK4Rd4xGB46yQM',
22
- KORA_SIGNER_TYPE: 'memory', // Default signer type
23
+ // HhA5j2rRiPbMrpF2ZD36r69FyZf3zWmEHRNSZbbNdVjf
24
+ TEST_USDC_MINT_SECRET: '59kKmXphL5UJANqpFFjtH17emEq3oRNmYsx6a3P3vSGJRmhMgVdzH77bkNEi9bArRViT45e8L2TsuPxKNFoc3Qfg',
25
+ TOKEN_DROP_AMOUNT: 100_000, // Default signer type
23
26
  };
24
27
  const createKeyPairSignerFromB58Secret = async (b58Secret) => {
25
28
  const base58Encoder = getBase58Encoder();
@@ -63,18 +66,18 @@ export function loadEnvironmentVariables() {
63
66
  assertIsAddress(destinationAddress);
64
67
  assertIsAddress(koraAddress);
65
68
  return {
66
- koraRpcUrl,
69
+ commitment,
70
+ destinationAddress,
67
71
  koraAddress,
72
+ koraRpcUrl,
68
73
  koraSignerType,
69
- commitment,
70
- tokenDecimals,
71
- tokenDropAmount,
72
74
  solDropAmount,
73
75
  solanaRpcUrl,
74
76
  solanaWsUrl,
75
- testWalletSecret,
76
77
  testUsdcMintSecret,
77
- destinationAddress,
78
+ testWalletSecret,
79
+ tokenDecimals,
80
+ tokenDropAmount,
78
81
  };
79
82
  }
80
83
  async function createKeyPairSigners() {
@@ -82,9 +85,9 @@ async function createKeyPairSigners() {
82
85
  const testWallet = await createKeyPairSignerFromB58Secret(testWalletSecret);
83
86
  const usdcMint = await createKeyPairSignerFromB58Secret(testUsdcMintSecret);
84
87
  return {
88
+ destinationAddress,
85
89
  testWallet,
86
90
  usdcMint,
87
- destinationAddress,
88
91
  };
89
92
  }
90
93
  const createDefaultTransaction = async (client, feePayer, computeLimit = MAX_COMPUTE_UNIT_LIMIT, feeMicroLamports = 1n) => {
@@ -131,46 +134,46 @@ async function initializeToken({ client, mintAuthority, payer, owner, mint, drop
131
134
  const baseInstructions = [
132
135
  // Create the Mint Account
133
136
  getCreateAccountInstruction({
134
- payer,
135
- newAccount: mint,
136
137
  lamports: mintRent,
137
- space: mintSpace,
138
+ newAccount: mint,
139
+ payer,
138
140
  programAddress: TOKEN_PROGRAM_ADDRESS,
141
+ space: mintSpace,
139
142
  }),
140
143
  // Initialize the Mint
141
144
  getInitializeMintInstruction({
142
- mint: mint.address,
143
145
  decimals,
146
+ mint: mint.address,
144
147
  mintAuthority: mintAuthority.address,
145
148
  }),
146
149
  // Create Associated Token Account
147
150
  await getCreateAssociatedTokenIdempotentInstructionAsync({
148
151
  mint: mint.address,
149
- payer,
150
152
  owner: owner.address,
153
+ payer,
151
154
  }),
152
155
  // Mint To the Destination Associated Token Account
153
156
  getMintToInstruction({
154
- mint: mint.address,
155
- token: ata,
156
157
  amount: BigInt(dropAmount * 10 ** decimals),
158
+ mint: mint.address,
157
159
  mintAuthority,
160
+ token: ata,
158
161
  }),
159
162
  ];
160
163
  // Generate Create ATA instructions for other token accounts we wish to add
161
164
  const otherAtaInstructions = otherAtaWallets
162
165
  ? await Promise.all(otherAtaWallets.map(async (wallet) => await getCreateAssociatedTokenIdempotentInstructionAsync({
163
166
  mint: mint.address,
164
- payer,
165
167
  owner: wallet,
168
+ payer,
166
169
  })))
167
170
  : [];
168
171
  const alreadyExists = await mintExists(client, mint.address);
169
- let instructions = alreadyExists ? [...otherAtaInstructions] : [...baseInstructions, ...otherAtaInstructions];
172
+ const instructions = alreadyExists ? [...otherAtaInstructions] : [...baseInstructions, ...otherAtaInstructions];
170
173
  await sendAndConfirmInstructions(client, payer, instructions, 'Initialize token and ATAs', 'finalized');
171
174
  }
172
175
  async function setupTestSuite() {
173
- const { koraAddress, koraRpcUrl, commitment, tokenDecimals, tokenDropAmount, solDropAmount, solanaRpcUrl, solanaWsUrl, } = await loadEnvironmentVariables();
176
+ const { koraAddress, koraRpcUrl, tokenDecimals, tokenDropAmount, solDropAmount, solanaRpcUrl, solanaWsUrl } = loadEnvironmentVariables();
174
177
  // Load auth config from environment if not provided
175
178
  const authConfig = process.env.ENABLE_AUTH === 'true'
176
179
  ? {
@@ -202,21 +205,21 @@ async function setupTestSuite() {
202
205
  // Initialize token and ATAs
203
206
  await initializeToken({
204
207
  client,
205
- mintAuthority,
206
- payer: mintAuthority,
207
- owner: testWallet,
208
- mint: usdcMint,
209
- dropAmount: tokenDropAmount,
210
208
  decimals: tokenDecimals,
209
+ dropAmount: tokenDropAmount,
210
+ mint: usdcMint,
211
+ mintAuthority,
211
212
  otherAtaWallets: [testWallet.address, koraAddress, destinationAddress],
213
+ owner: testWallet,
214
+ payer: mintAuthority,
212
215
  });
213
216
  return {
217
+ destinationAddress,
218
+ koraAddress,
214
219
  koraClient: new KoraClient({ rpcUrl: koraRpcUrl, ...authConfig }),
215
220
  koraRpcUrl,
216
221
  testWallet,
217
222
  usdcMint: usdcMint.address,
218
- destinationAddress,
219
- koraAddress,
220
223
  };
221
224
  }
222
225
  const mintExists = async (client, mint) => {
@@ -224,7 +227,7 @@ const mintExists = async (client, mint) => {
224
227
  const mintAccount = await client.rpc.getAccountInfo(mint).send();
225
228
  return mintAccount.value !== null;
226
229
  }
227
- catch (error) {
230
+ catch {
228
231
  return false;
229
232
  }
230
233
  };