@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.
@@ -5,34 +5,34 @@ export function runAuthenticationTests() {
5
5
  describe('Authentication', () => {
6
6
  it('should fail with incorrect API key', async () => {
7
7
  const client = new KoraClient({
8
- rpcUrl: koraRpcUrl,
9
8
  apiKey: 'WRONG-API-KEY',
9
+ rpcUrl: koraRpcUrl,
10
10
  });
11
11
  // Auth failure should result in an error (empty response body causes JSON parse error)
12
12
  await expect(client.getConfig()).rejects.toThrow();
13
13
  });
14
14
  it('should fail with incorrect HMAC secret', async () => {
15
15
  const client = new KoraClient({
16
- rpcUrl: koraRpcUrl,
17
16
  hmacSecret: 'WRONG-HMAC-SECRET',
17
+ rpcUrl: koraRpcUrl,
18
18
  });
19
19
  // Auth failure should result in an error
20
20
  await expect(client.getConfig()).rejects.toThrow();
21
21
  });
22
22
  it('should fail with both incorrect credentials', async () => {
23
23
  const client = new KoraClient({
24
- rpcUrl: koraRpcUrl,
25
24
  apiKey: 'WRONG-API-KEY',
26
25
  hmacSecret: 'WRONG-HMAC-SECRET',
26
+ rpcUrl: koraRpcUrl,
27
27
  });
28
28
  // Auth failure should result in an error
29
29
  await expect(client.getConfig()).rejects.toThrow();
30
30
  });
31
31
  it('should succeed with correct credentials', async () => {
32
32
  const client = new KoraClient({
33
- rpcUrl: koraRpcUrl,
34
33
  apiKey: 'test-api-key-123',
35
34
  hmacSecret: 'test-hmac-secret-456',
35
+ rpcUrl: koraRpcUrl,
36
36
  });
37
37
  const config = await client.getConfig();
38
38
  expect(config).toBeDefined();
@@ -1,7 +1,7 @@
1
- import setupTestSuite from './setup.js';
1
+ import { appendTransactionMessageInstruction, compileTransaction, createTransactionMessage, getBase64Decoder, getBase64EncodedWireTransaction, getBase64Encoder, getTransactionDecoder, getTransactionEncoder, partiallySignTransaction, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, } from '@solana/kit';
2
+ import { findAssociatedTokenPda, getTransferInstruction, TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
2
3
  import { runAuthenticationTests } from './auth-setup.js';
3
- import { getBase64Decoder, getBase64Encoder, getTransactionDecoder, getTransactionEncoder, partiallySignTransaction, } from '@solana/kit';
4
- import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
4
+ import setupTestSuite from './setup.js';
5
5
  function transactionFromBase64(base64) {
6
6
  const encoder = getBase64Encoder();
7
7
  const decoder = getTransactionDecoder();
@@ -14,6 +14,45 @@ function transactionToBase64(transaction) {
14
14
  const base64Decoder = getBase64Decoder();
15
15
  return base64Decoder.decode(txBytes);
16
16
  }
17
+ /**
18
+ * Helper to build a SPL token transfer transaction.
19
+ * This replaces the deprecated transferTransaction endpoint.
20
+ */
21
+ async function buildTokenTransferTransaction(params) {
22
+ const { client, amount, mint, sourceWallet, destinationWallet } = params;
23
+ // Get the payer signer from Kora (fee payer)
24
+ const { signer_address } = await client.getPayerSigner();
25
+ // Get blockhash
26
+ const { blockhash } = await client.getBlockhash();
27
+ // Find source and destination ATAs
28
+ const [sourceAta] = await findAssociatedTokenPda({
29
+ mint,
30
+ owner: sourceWallet.address,
31
+ tokenProgram: TOKEN_PROGRAM_ADDRESS,
32
+ });
33
+ const [destinationAta] = await findAssociatedTokenPda({
34
+ mint,
35
+ owner: destinationWallet,
36
+ tokenProgram: TOKEN_PROGRAM_ADDRESS,
37
+ });
38
+ // Build transfer instruction
39
+ const transferIx = getTransferInstruction({
40
+ amount,
41
+ authority: sourceWallet,
42
+ destination: destinationAta,
43
+ source: sourceAta,
44
+ });
45
+ // Build transaction message with Kora as fee payer
46
+ // We create a mock signer for the fee payer address since we only need the address
47
+ const feePayerSigner = {
48
+ address: signer_address,
49
+ };
50
+ const transactionMessage = pipe(createTransactionMessage({ version: 0 }), tx => setTransactionMessageFeePayerSigner(feePayerSigner, tx), tx => setTransactionMessageLifetimeUsingBlockhash({ blockhash: blockhash, lastValidBlockHeight: BigInt(Number.MAX_SAFE_INTEGER) }, tx), tx => appendTransactionMessageInstruction(transferIx, tx));
51
+ // Compile to transaction
52
+ const transaction = compileTransaction(transactionMessage);
53
+ const base64Transaction = getBase64EncodedWireTransaction(transaction);
54
+ return { blockhash: blockhash, transaction: base64Transaction };
55
+ }
17
56
  const AUTH_ENABLED = process.env.ENABLE_AUTH === 'true';
18
57
  const KORA_SIGNER_TYPE = process.env.KORA_SIGNER_TYPE || 'memory';
19
58
  describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without auth'} | signer type: ${KORA_SIGNER_TYPE})`, () => {
@@ -90,7 +129,6 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
90
129
  expect(config.enabled_methods.get_supported_tokens).toBeDefined();
91
130
  expect(config.enabled_methods.sign_transaction).toBeDefined();
92
131
  expect(config.enabled_methods.sign_and_send_transaction).toBeDefined();
93
- expect(config.enabled_methods.transfer_transaction).toBeDefined();
94
132
  expect(config.enabled_methods.get_blockhash).toBeDefined();
95
133
  expect(config.enabled_methods.get_config).toBeDefined();
96
134
  expect(config.enabled_methods.get_version).toBeDefined();
@@ -123,30 +161,15 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
123
161
  });
124
162
  });
125
163
  describe('Transaction Operations', () => {
126
- it('should create transfer transaction (DEPRECATED endpoint)', async () => {
127
- const request = {
128
- amount: 1000000, // 1 USDC
129
- token: usdcMint,
130
- source: testWalletAddress,
131
- destination: koraAddress, // user specifies destination
132
- };
133
- const response = await client.transferTransaction(request);
134
- expect(response).toBeDefined();
135
- expect(response.transaction).toBeDefined();
136
- expect(response.blockhash).toBeDefined();
137
- expect(response.message).toBeDefined();
138
- expect(response.signer_pubkey).toBeDefined();
139
- });
140
164
  it('should estimate transaction fee', async () => {
141
- // First create a transaction
142
- const transferRequest = {
143
- amount: 1000000,
144
- token: usdcMint,
145
- source: testWalletAddress,
146
- destination: koraAddress,
147
- };
148
- const { transaction } = await client.transferTransaction(transferRequest);
149
- const fee = await client.estimateTransactionFee({ transaction, fee_token: usdcMint });
165
+ const { transaction } = await buildTokenTransferTransaction({
166
+ amount: 1000000n,
167
+ client,
168
+ destinationWallet: koraAddress,
169
+ mint: usdcMint,
170
+ sourceWallet: testWallet,
171
+ });
172
+ const fee = await client.estimateTransactionFee({ fee_token: usdcMint, transaction });
150
173
  expect(fee).toBeDefined();
151
174
  expect(typeof fee.fee_in_lamports).toBe('number');
152
175
  expect(fee.fee_in_lamports).toBeGreaterThan(0);
@@ -154,13 +177,13 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
154
177
  expect(fee.fee_in_token).toBeGreaterThan(0);
155
178
  });
156
179
  it('should sign transaction', async () => {
157
- const transferRequest = {
158
- amount: 1000000,
159
- token: usdcMint,
160
- source: testWalletAddress,
161
- destination: koraAddress,
162
- };
163
- const { transaction } = await client.transferTransaction(transferRequest);
180
+ const { transaction } = await buildTokenTransferTransaction({
181
+ amount: 1000000n,
182
+ client,
183
+ destinationWallet: koraAddress,
184
+ mint: usdcMint,
185
+ sourceWallet: testWallet,
186
+ });
164
187
  const signResult = await client.signTransaction({
165
188
  transaction,
166
189
  });
@@ -168,13 +191,13 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
168
191
  expect(signResult.signed_transaction).toBeDefined();
169
192
  });
170
193
  it('should sign and send transaction', async () => {
171
- const transferRequest = {
172
- amount: 1000000,
173
- token: usdcMint,
174
- source: testWalletAddress,
175
- destination: koraAddress,
176
- };
177
- const { transaction: transactionString } = await client.transferTransaction(transferRequest);
194
+ const { transaction: transactionString } = await buildTokenTransferTransaction({
195
+ amount: 1000000n,
196
+ client,
197
+ destinationWallet: koraAddress,
198
+ mint: usdcMint,
199
+ sourceWallet: testWallet,
200
+ });
178
201
  const transaction = transactionFromBase64(transactionString);
179
202
  // Partially sign transaction with test wallet before sending
180
203
  // Kora will add fee payer signature via signAndSendTransaction
@@ -185,29 +208,30 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
185
208
  });
186
209
  expect(signResult).toBeDefined();
187
210
  expect(signResult.signed_transaction).toBeDefined();
211
+ expect(signResult.signature).toBeDefined();
188
212
  });
189
213
  it('should get payment instruction', async () => {
190
- const transferRequest = {
191
- amount: 1000000,
192
- token: usdcMint,
193
- source: testWalletAddress,
194
- destination: koraAddress,
195
- };
214
+ const { transaction } = await buildTokenTransferTransaction({
215
+ amount: 1000000n,
216
+ client,
217
+ destinationWallet: koraAddress,
218
+ mint: usdcMint,
219
+ sourceWallet: testWallet,
220
+ });
196
221
  const [expectedSenderAta] = await findAssociatedTokenPda({
222
+ mint: usdcMint,
197
223
  owner: testWalletAddress,
198
224
  tokenProgram: TOKEN_PROGRAM_ADDRESS,
199
- mint: usdcMint,
200
225
  });
201
226
  const [koraAta] = await findAssociatedTokenPda({
227
+ mint: usdcMint,
202
228
  owner: koraAddress,
203
229
  tokenProgram: TOKEN_PROGRAM_ADDRESS,
204
- mint: usdcMint,
205
230
  });
206
- const { transaction } = await client.transferTransaction(transferRequest);
207
- const { payment_instruction, payment_amount, payment_token, payment_address, signer_address, original_transaction, } = await client.getPaymentInstruction({
208
- transaction,
231
+ const { payment_instruction, payment_amount: _payment_amount, payment_token, payment_address, signer_address, original_transaction, } = await client.getPaymentInstruction({
209
232
  fee_token: usdcMint,
210
233
  source_wallet: testWalletAddress,
234
+ transaction,
211
235
  });
212
236
  expect(payment_instruction).toBeDefined();
213
237
  expect(payment_instruction.programAddress).toBe(TOKEN_PROGRAM_ADDRESS);
@@ -225,20 +249,20 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
225
249
  describe('Bundle Operations', () => {
226
250
  it('should sign bundle of transactions', async () => {
227
251
  // Create two transfer transactions for the bundle
228
- const transferRequest1 = {
229
- amount: 1000000,
230
- token: usdcMint,
231
- source: testWalletAddress,
232
- destination: koraAddress,
233
- };
234
- const transferRequest2 = {
235
- amount: 500000,
236
- token: usdcMint,
237
- source: testWalletAddress,
238
- destination: koraAddress,
239
- };
240
- const { transaction: tx1String } = await client.transferTransaction(transferRequest1);
241
- const { transaction: tx2String } = await client.transferTransaction(transferRequest2);
252
+ const { transaction: tx1String } = await buildTokenTransferTransaction({
253
+ amount: 1000000n,
254
+ client,
255
+ destinationWallet: koraAddress,
256
+ mint: usdcMint,
257
+ sourceWallet: testWallet,
258
+ });
259
+ const { transaction: tx2String } = await buildTokenTransferTransaction({
260
+ amount: 500000n,
261
+ client,
262
+ destinationWallet: koraAddress,
263
+ mint: usdcMint,
264
+ sourceWallet: testWallet,
265
+ });
242
266
  // Partially sign both transactions with test wallet
243
267
  const tx1 = transactionFromBase64(tx1String);
244
268
  const tx2 = transactionFromBase64(tx2String);
@@ -257,20 +281,20 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
257
281
  });
258
282
  it('should sign and send bundle of transactions', async () => {
259
283
  // Create two transfer transactions for the bundle
260
- const transferRequest1 = {
261
- amount: 1000000,
262
- token: usdcMint,
263
- source: testWalletAddress,
264
- destination: koraAddress,
265
- };
266
- const transferRequest2 = {
267
- amount: 500000,
268
- token: usdcMint,
269
- source: testWalletAddress,
270
- destination: koraAddress,
271
- };
272
- const { transaction: tx1String } = await client.transferTransaction(transferRequest1);
273
- const { transaction: tx2String } = await client.transferTransaction(transferRequest2);
284
+ const { transaction: tx1String } = await buildTokenTransferTransaction({
285
+ amount: 1000000n,
286
+ client,
287
+ destinationWallet: koraAddress,
288
+ mint: usdcMint,
289
+ sourceWallet: testWallet,
290
+ });
291
+ const { transaction: tx2String } = await buildTokenTransferTransaction({
292
+ amount: 500000n,
293
+ client,
294
+ destinationWallet: koraAddress,
295
+ mint: usdcMint,
296
+ sourceWallet: testWallet,
297
+ });
274
298
  // Partially sign both transactions with test wallet
275
299
  const tx1 = transactionFromBase64(tx1String);
276
300
  const tx2 = transactionFromBase64(tx2String);
@@ -291,69 +315,26 @@ describe(`KoraClient Integration Tests (${AUTH_ENABLED ? 'with auth' : 'without
291
315
  });
292
316
  });
293
317
  describe('Error Handling', () => {
294
- it('should handle invalid token address', async () => {
295
- const request = {
296
- amount: 1000000,
297
- token: 'InvalidTokenAddress',
298
- source: testWalletAddress,
299
- destination: koraAddress,
300
- };
301
- await expect(client.transferTransaction(request)).rejects.toThrow();
302
- });
303
- it('should handle invalid amount', async () => {
304
- const request = {
305
- amount: -1, // Invalid amount
306
- token: usdcMint,
307
- source: testWalletAddress,
308
- destination: koraAddress,
309
- };
310
- await expect(client.transferTransaction(request)).rejects.toThrow();
311
- });
312
318
  it('should handle invalid transaction for signing', async () => {
313
319
  await expect(client.signTransaction({
314
320
  transaction: 'invalid_transaction',
315
321
  })).rejects.toThrow();
316
322
  });
317
323
  it('should handle invalid transaction for fee estimation', async () => {
318
- await expect(client.estimateTransactionFee({ transaction: 'invalid_transaction', fee_token: usdcMint })).rejects.toThrow();
319
- });
320
- it('should handle non-allowed token for fee payment', async () => {
321
- const transferRequest = {
322
- amount: 1000000,
323
- token: usdcMint,
324
- source: testWalletAddress,
325
- destination: koraAddress,
326
- };
327
- // TODO: API has an error. this endpoint should verify the provided fee token is supported
328
- const { transaction } = await client.transferTransaction(transferRequest);
329
- const fee = await client.estimateTransactionFee({ transaction, fee_token: usdcMint });
330
- expect(fee).toBeDefined();
331
- expect(typeof fee.fee_in_lamports).toBe('number');
332
- expect(fee.fee_in_lamports).toBeGreaterThan(0);
324
+ await expect(client.estimateTransactionFee({ fee_token: usdcMint, transaction: 'invalid_transaction' })).rejects.toThrow();
333
325
  });
334
326
  });
335
327
  describe('End-to-End Flows', () => {
336
328
  it('should handle transfer and sign flow', async () => {
337
- const request = {
338
- amount: 1000000,
339
- token: usdcMint,
340
- source: testWalletAddress,
341
- destination: koraAddress,
342
- };
343
- // Create and sign the transaction
344
- const { transaction } = await client.transferTransaction(request);
329
+ const { transaction } = await buildTokenTransferTransaction({
330
+ amount: 1000000n,
331
+ client,
332
+ destinationWallet: koraAddress,
333
+ mint: usdcMint,
334
+ sourceWallet: testWallet,
335
+ });
345
336
  const signResult = await client.signTransaction({ transaction });
346
337
  expect(signResult.signed_transaction).toBeDefined();
347
338
  });
348
- it('should reject transaction with non-allowed token', async () => {
349
- const invalidTokenMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; // Mainnet USDC mint
350
- const request = {
351
- amount: 1000000,
352
- token: invalidTokenMint,
353
- source: testWalletAddress,
354
- destination: koraAddress,
355
- };
356
- await expect(client.transferTransaction(request)).rejects.toThrow();
357
- });
358
339
  });
359
340
  });
@@ -0,0 +1 @@
1
+ export {};