@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.
- package/dist/src/client.d.ts +212 -7
- package/dist/src/client.js +234 -17
- package/dist/src/error.d.ts +41 -0
- package/dist/src/error.js +54 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/kit/executor.d.ts +2 -2
- package/dist/src/kit/executor.js +20 -7
- package/dist/src/kit/index.d.ts +85 -25
- package/dist/src/kit/index.js +79 -49
- package/dist/src/kit/payment.js +3 -2
- package/dist/src/kit/planner.d.ts +2 -2
- package/dist/src/plugin.d.ts +85 -0
- package/dist/src/plugin.js +179 -0
- package/dist/src/types/index.d.ts +200 -80
- package/dist/test/auth-setup.js +4 -4
- package/dist/test/integration.test.js +223 -298
- package/dist/test/kit-client.test.js +66 -3
- package/dist/test/plugin.test.js +169 -79
- package/dist/test/setup.d.ts +8 -15
- package/dist/test/setup.js +76 -153
- package/dist/test/unit.test.js +365 -161
- package/package.json +37 -34
- package/dist/src/kit/plugin.d.ts +0 -31
- package/dist/src/kit/plugin.js +0 -107
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable numeric error codes for RPC responses following JSON-RPC 2.0 spec (-32000 to -32099 for server errors).
|
|
3
|
+
*/
|
|
4
|
+
export var KoraErrorCode;
|
|
5
|
+
(function (KoraErrorCode) {
|
|
6
|
+
// Validation errors (-32000 to -32019)
|
|
7
|
+
KoraErrorCode[KoraErrorCode["InvalidTransaction"] = -32000] = "InvalidTransaction";
|
|
8
|
+
KoraErrorCode[KoraErrorCode["ValidationError"] = -32001] = "ValidationError";
|
|
9
|
+
KoraErrorCode[KoraErrorCode["UnsupportedFeeToken"] = -32002] = "UnsupportedFeeToken";
|
|
10
|
+
KoraErrorCode[KoraErrorCode["InsufficientFunds"] = -32003] = "InsufficientFunds";
|
|
11
|
+
KoraErrorCode[KoraErrorCode["InvalidRequest"] = -32004] = "InvalidRequest";
|
|
12
|
+
KoraErrorCode[KoraErrorCode["FeeEstimationFailed"] = -32005] = "FeeEstimationFailed";
|
|
13
|
+
KoraErrorCode[KoraErrorCode["TransactionExecutionFailed"] = -32006] = "TransactionExecutionFailed";
|
|
14
|
+
// Signing errors (-32020 to -32029)
|
|
15
|
+
KoraErrorCode[KoraErrorCode["SigningError"] = -32020] = "SigningError";
|
|
16
|
+
// Auth / Rate limiting (-32030 to -32039)
|
|
17
|
+
KoraErrorCode[KoraErrorCode["RateLimitExceeded"] = -32030] = "RateLimitExceeded";
|
|
18
|
+
KoraErrorCode[KoraErrorCode["UsageLimitExceeded"] = -32031] = "UsageLimitExceeded";
|
|
19
|
+
KoraErrorCode[KoraErrorCode["Unauthorized"] = -32032] = "Unauthorized";
|
|
20
|
+
// Token / Swap (-32040 to -32049)
|
|
21
|
+
KoraErrorCode[KoraErrorCode["SwapError"] = -32040] = "SwapError";
|
|
22
|
+
KoraErrorCode[KoraErrorCode["TokenOperationError"] = -32041] = "TokenOperationError";
|
|
23
|
+
// Account errors (-32050 to -32059)
|
|
24
|
+
KoraErrorCode[KoraErrorCode["AccountNotFound"] = -32050] = "AccountNotFound";
|
|
25
|
+
// External services (-32060 to -32069)
|
|
26
|
+
KoraErrorCode[KoraErrorCode["JitoError"] = -32060] = "JitoError";
|
|
27
|
+
KoraErrorCode[KoraErrorCode["RecaptchaError"] = -32061] = "RecaptchaError";
|
|
28
|
+
// Internal errors (-32090 to -32099)
|
|
29
|
+
KoraErrorCode[KoraErrorCode["InternalServerError"] = -32090] = "InternalServerError";
|
|
30
|
+
KoraErrorCode[KoraErrorCode["ConfigError"] = -32091] = "ConfigError";
|
|
31
|
+
KoraErrorCode[KoraErrorCode["SerializationError"] = -32092] = "SerializationError";
|
|
32
|
+
KoraErrorCode[KoraErrorCode["RpcError"] = -32093] = "RpcError";
|
|
33
|
+
})(KoraErrorCode || (KoraErrorCode = {}));
|
|
34
|
+
/**
|
|
35
|
+
* Represents a structured error returned by the Kora paymaster service.
|
|
36
|
+
*/
|
|
37
|
+
export class KoraError extends Error {
|
|
38
|
+
/** The stable numeric error code */
|
|
39
|
+
code;
|
|
40
|
+
/** Optional structured data about the error */
|
|
41
|
+
data;
|
|
42
|
+
constructor(code, message, data) {
|
|
43
|
+
const finalCode = code ?? -32603;
|
|
44
|
+
const finalMessage = message ?? 'Unknown error';
|
|
45
|
+
super(`Kora Error ${finalCode}: ${finalMessage}`);
|
|
46
|
+
this.name = 'KoraError';
|
|
47
|
+
this.code = finalCode;
|
|
48
|
+
this.data = data;
|
|
49
|
+
// Ensure proper stack trace in environments that support Error.captureStackTrace
|
|
50
|
+
if (Error.captureStackTrace) {
|
|
51
|
+
Error.captureStackTrace(this, KoraError);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
+
export * from './error.js';
|
|
1
2
|
export * from './types/index.js';
|
|
2
3
|
export { KoraClient } from './client.js';
|
|
3
|
-
export { koraPlugin, type
|
|
4
|
-
export { createKitKoraClient, type KoraKitClient } from './kit/index.js';
|
|
4
|
+
export { koraPlugin, type KoraApi } from './plugin.js';
|
package/dist/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Address, type TransactionMessage, type TransactionMessageWithFeePayer, type TransactionSigner } from '@solana/kit';
|
|
2
2
|
import { KoraClient } from '../client.js';
|
|
3
|
-
import type {
|
|
4
|
-
export declare function createKoraTransactionPlanExecutor(koraClient: KoraClient, config:
|
|
3
|
+
import type { KoraBundleConfig } from '../types/index.js';
|
|
4
|
+
export declare function createKoraTransactionPlanExecutor(koraClient: KoraClient, config: KoraBundleConfig, userSigner: TransactionSigner, payerSigner: TransactionSigner, payment: {
|
|
5
5
|
destinationTokenAccount: Address;
|
|
6
6
|
sourceTokenAccount: Address;
|
|
7
7
|
} | undefined, resolveProvisoryComputeUnitLimit: (<T extends TransactionMessage & TransactionMessageWithFeePayer>(transactionMessage: T) => Promise<T>) | undefined): import("@solana/kit").TransactionPlanExecutor<import("@solana/kit").TransactionPlanResultContext>;
|
package/dist/src/kit/executor.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { appendTransactionMessageInstructions, blockhash, createTransactionMessage, createTransactionPlanExecutor, getBase64EncodedWireTransaction, getBase64Encoder, getSignatureFromTransaction, getTransactionDecoder, partiallySignTransactionMessageWithSigners, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signature, } from '@solana/kit';
|
|
2
2
|
import { removePaymentInstruction, updatePaymentInstructionAmount } from './payment.js';
|
|
3
|
-
|
|
3
|
+
// TODO: Create a bundle-aware executor (e.g. `createKoraBundlePlanExecutor`) that collects
|
|
4
|
+
// multiple planned transaction messages into a single `signAndSendBundle` call instead of
|
|
5
|
+
// submitting each one individually via `signAndSendTransaction`. This would let users
|
|
6
|
+
// compose Jito bundles through the Kit plan/execute pipeline rather than manually encoding
|
|
7
|
+
// transactions and calling `client.kora.signAndSendBundle()`.
|
|
8
|
+
export function createKoraTransactionPlanExecutor(koraClient, config, userSigner, payerSigner, payment, resolveProvisoryComputeUnitLimit) {
|
|
4
9
|
return createTransactionPlanExecutor({
|
|
5
10
|
async executeTransactionMessage(_context, transactionMessage) {
|
|
6
11
|
// Kora manages blockhash validity; set max height to avoid premature client-side expiry checks
|
|
@@ -20,8 +25,13 @@ export function createKoraTransactionPlanExecutor(koraClient, config, payerSigne
|
|
|
20
25
|
fee_token: config.feeToken,
|
|
21
26
|
transaction: prePaymentTx,
|
|
22
27
|
});
|
|
23
|
-
if (fee_in_token
|
|
24
|
-
|
|
28
|
+
if (fee_in_token == null) {
|
|
29
|
+
console.warn('[kora] fee_in_token is undefined — defaulting to 0. ' +
|
|
30
|
+
'If paid pricing is expected, check that the fee token is correctly configured on the server.');
|
|
31
|
+
}
|
|
32
|
+
const feeInToken = fee_in_token ?? 0;
|
|
33
|
+
if (feeInToken < 0) {
|
|
34
|
+
throw new Error(`Kora fee estimation returned a negative fee (${feeInToken}). This indicates a server-side error.`);
|
|
25
35
|
}
|
|
26
36
|
const currentIxs = 'instructions' in msgForEstimation
|
|
27
37
|
? msgForEstimation.instructions
|
|
@@ -31,9 +41,9 @@ export function createKoraTransactionPlanExecutor(koraClient, config, payerSigne
|
|
|
31
41
|
'The message structure may be incompatible with this version of the Kora SDK.');
|
|
32
42
|
}
|
|
33
43
|
// Replace placeholder with real fee amount, or strip it if fee is 0
|
|
34
|
-
const finalIxs =
|
|
35
|
-
? updatePaymentInstructionAmount(currentIxs,
|
|
36
|
-
: removePaymentInstruction(currentIxs, sourceTokenAccount, destinationTokenAccount,
|
|
44
|
+
const finalIxs = feeInToken > 0
|
|
45
|
+
? updatePaymentInstructionAmount(currentIxs, userSigner, sourceTokenAccount, destinationTokenAccount, feeInToken, config.tokenProgramId)
|
|
46
|
+
: removePaymentInstruction(currentIxs, sourceTokenAccount, destinationTokenAccount, userSigner, config.tokenProgramId);
|
|
37
47
|
const resolvedMsg = pipe(createTransactionMessage({ version: 0 }), m => setTransactionMessageFeePayerSigner(payerSigner, m), m => setTransactionMessageLifetimeUsingBlockhash({
|
|
38
48
|
blockhash: blockhash(bh),
|
|
39
49
|
lastValidBlockHeight: BigInt(Number.MAX_SAFE_INTEGER),
|
|
@@ -43,7 +53,10 @@ export function createKoraTransactionPlanExecutor(koraClient, config, payerSigne
|
|
|
43
53
|
else {
|
|
44
54
|
finalTx = prePaymentTx;
|
|
45
55
|
}
|
|
46
|
-
const result = await koraClient.signAndSendTransaction({
|
|
56
|
+
const result = await koraClient.signAndSendTransaction({
|
|
57
|
+
transaction: finalTx,
|
|
58
|
+
user_id: config.userId,
|
|
59
|
+
});
|
|
47
60
|
if (result.signature) {
|
|
48
61
|
return signature(result.signature);
|
|
49
62
|
}
|
package/dist/src/kit/index.d.ts
CHANGED
|
@@ -1,50 +1,110 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
export type KoraKitClient = Awaited<ReturnType<typeof createKitKoraClient>>;
|
|
1
|
+
import { type ClientWithIdentity } from '@solana/kit';
|
|
2
|
+
import type { KoraBundleConfig, KoraKitClientConfig } from '../types/index.js';
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
4
|
+
* Kora bundle plugin.
|
|
5
|
+
*
|
|
6
|
+
* Composes RPC + payer + Kora RPC methods + transaction planning/execution into a single
|
|
7
|
+
* plugin that mirrors the shape of `solanaRpc` from `@solana/kit-plugin-rpc`.
|
|
6
8
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
+
* The caller's signer is read from `client.identity` — install `identity()` (or `signer()`)
|
|
10
|
+
* from `@solana/kit-plugin-signer` before this plugin. The fee payer (Kora's address) is
|
|
11
|
+
* fetched from the Kora node and installed internally as a noop signer, overriding any
|
|
12
|
+
* `payer` already on the client.
|
|
9
13
|
*
|
|
10
14
|
* @beta This API is experimental and may change in future releases.
|
|
11
15
|
*
|
|
12
16
|
* @example
|
|
13
|
-
* ```
|
|
14
|
-
* import {
|
|
15
|
-
* import {
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { createClient } from '@solana/kit';
|
|
19
|
+
* import { identity } from '@solana/kit-plugin-signer';
|
|
20
|
+
* import { kora } from '@solana/kora';
|
|
21
|
+
*
|
|
22
|
+
* const client = await createClient()
|
|
23
|
+
* .use(identity(userSigner))
|
|
24
|
+
* .use(kora({
|
|
25
|
+
* endpoint: 'https://kora.example.com',
|
|
26
|
+
* rpcUrl: 'https://api.mainnet-beta.solana.com',
|
|
27
|
+
* feeToken: address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
|
|
28
|
+
* }));
|
|
29
|
+
*
|
|
30
|
+
* const signature = await client.sendTransaction([myInstruction]);
|
|
25
31
|
* ```
|
|
26
32
|
*/
|
|
27
|
-
export declare function
|
|
33
|
+
export declare function kora(config: KoraBundleConfig): <T extends ClientWithIdentity>(client: T) => Promise<Omit<Omit<Omit<Omit<Omit<T, "payer"> & {
|
|
34
|
+
payer: import("@solana/kit").NoopSigner<string>;
|
|
35
|
+
}, "rpc"> & {
|
|
28
36
|
rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
|
|
29
|
-
rpcSubscriptions: import("@solana/kit").RpcSubscriptions<import("@solana/kit").SolanaRpcSubscriptionsApi>;
|
|
30
37
|
} & {
|
|
31
38
|
kora: {
|
|
39
|
+
estimateBundleFee(request: import("../types/index.js").EstimateBundleFeeRequest): Promise<import("../types/index.js").KitEstimateBundleFeeResponse>;
|
|
32
40
|
estimateTransactionFee(request: import("../types/index.js").EstimateTransactionFeeRequest): Promise<import("../types/index.js").KitEstimateFeeResponse>;
|
|
33
41
|
getBlockhash(): Promise<import("../types/index.js").KitBlockhashResponse>;
|
|
34
42
|
getConfig(): Promise<import("../types/index.js").KitConfigResponse>;
|
|
35
43
|
getPayerSigner(): Promise<import("../types/index.js").KitPayerSignerResponse>;
|
|
36
44
|
getPaymentInstruction(request: import("../types/index.js").GetPaymentInstructionRequest): Promise<import("../types/index.js").KitPaymentInstructionResponse>;
|
|
37
45
|
getSupportedTokens(): Promise<import("../types/index.js").KitSupportedTokensResponse>;
|
|
46
|
+
getVersion(): Promise<import("../types/index.js").GetVersionResponse>;
|
|
47
|
+
signAndSendBundle(request: import("../types/index.js").SignAndSendBundleRequest): Promise<import("../types/index.js").KitSignAndSendBundleResponse>;
|
|
38
48
|
signAndSendTransaction(request: import("../types/index.js").SignAndSendTransactionRequest): Promise<import("../types/index.js").KitSignAndSendTransactionResponse>;
|
|
49
|
+
signBundle(request: import("../types/index.js").SignBundleRequest): Promise<import("../types/index.js").KitSignBundleResponse>;
|
|
39
50
|
signTransaction(request: import("../types/index.js").SignTransactionRequest): Promise<import("../types/index.js").KitSignTransactionResponse>;
|
|
40
|
-
transferTransaction(request: import("../types/index.js").TransferTransactionRequest): Promise<import("../types/index.js").KitTransferTransactionResponse>;
|
|
41
51
|
};
|
|
42
|
-
} & {
|
|
43
|
-
payer: import("@solana/kit").TransactionSigner;
|
|
44
52
|
} & {
|
|
45
53
|
paymentAddress: import("@solana/kit").Address | undefined;
|
|
46
|
-
} & {
|
|
54
|
+
}, "transactionPlanner"> & {
|
|
47
55
|
transactionPlanner: import("@solana/kit").TransactionPlanner;
|
|
56
|
+
}, "transactionPlanExecutor"> & {
|
|
57
|
+
transactionPlanExecutor: import("@solana/kit").TransactionPlanExecutor;
|
|
58
|
+
}, keyof import("@solana/kit").ClientWithTransactionPlanning | keyof import("@solana/kit").ClientWithTransactionSending> & import("@solana/kit").ClientWithTransactionPlanning & import("@solana/kit").ClientWithTransactionSending>;
|
|
59
|
+
/**
|
|
60
|
+
* The type returned by {@link createKitKoraClient}.
|
|
61
|
+
*
|
|
62
|
+
* @deprecated Derive the client type from your own `createClient().use(...).use(kora(...))`
|
|
63
|
+
* composition instead. Will be removed in 0.4.0.
|
|
64
|
+
*/
|
|
65
|
+
export type KoraKitClient = Awaited<ReturnType<typeof createKitKoraClient>>;
|
|
66
|
+
/**
|
|
67
|
+
* Creates a Kora Kit client composed from Kit plugins.
|
|
68
|
+
*
|
|
69
|
+
* @beta This API is experimental and may change in future releases.
|
|
70
|
+
*
|
|
71
|
+
* @deprecated Compose with the {@link kora} plugin instead:
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { createClient } from '@solana/kit';
|
|
74
|
+
* import { identity } from '@solana/kit-plugin-signer';
|
|
75
|
+
* import { kora } from '@solana/kora';
|
|
76
|
+
*
|
|
77
|
+
* const client = await createClient()
|
|
78
|
+
* .use(identity(userSigner))
|
|
79
|
+
* .use(kora({ endpoint, rpcUrl, feeToken }));
|
|
80
|
+
* ```
|
|
81
|
+
* Will be removed in 0.4.0.
|
|
82
|
+
*/
|
|
83
|
+
export declare function createKitKoraClient(config: KoraKitClientConfig): Promise<import("@solana/kit").Client<Omit<Omit<Omit<Omit<Omit<Omit<object, "identity"> & {
|
|
84
|
+
identity: import("@solana/kit").TransactionSigner;
|
|
85
|
+
}, "payer"> & {
|
|
86
|
+
payer: import("@solana/kit").NoopSigner<string>;
|
|
87
|
+
}, "rpc"> & {
|
|
88
|
+
rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
|
|
48
89
|
} & {
|
|
90
|
+
kora: {
|
|
91
|
+
estimateBundleFee(request: import("../types/index.js").EstimateBundleFeeRequest): Promise<import("../types/index.js").KitEstimateBundleFeeResponse>;
|
|
92
|
+
estimateTransactionFee(request: import("../types/index.js").EstimateTransactionFeeRequest): Promise<import("../types/index.js").KitEstimateFeeResponse>;
|
|
93
|
+
getBlockhash(): Promise<import("../types/index.js").KitBlockhashResponse>;
|
|
94
|
+
getConfig(): Promise<import("../types/index.js").KitConfigResponse>;
|
|
95
|
+
getPayerSigner(): Promise<import("../types/index.js").KitPayerSignerResponse>;
|
|
96
|
+
getPaymentInstruction(request: import("../types/index.js").GetPaymentInstructionRequest): Promise<import("../types/index.js").KitPaymentInstructionResponse>;
|
|
97
|
+
getSupportedTokens(): Promise<import("../types/index.js").KitSupportedTokensResponse>;
|
|
98
|
+
getVersion(): Promise<import("../types/index.js").GetVersionResponse>;
|
|
99
|
+
signAndSendBundle(request: import("../types/index.js").SignAndSendBundleRequest): Promise<import("../types/index.js").KitSignAndSendBundleResponse>;
|
|
100
|
+
signAndSendTransaction(request: import("../types/index.js").SignAndSendTransactionRequest): Promise<import("../types/index.js").KitSignAndSendTransactionResponse>;
|
|
101
|
+
signBundle(request: import("../types/index.js").SignBundleRequest): Promise<import("../types/index.js").KitSignBundleResponse>;
|
|
102
|
+
signTransaction(request: import("../types/index.js").SignTransactionRequest): Promise<import("../types/index.js").KitSignTransactionResponse>;
|
|
103
|
+
};
|
|
104
|
+
} & {
|
|
105
|
+
paymentAddress: import("@solana/kit").Address | undefined;
|
|
106
|
+
}, "transactionPlanner"> & {
|
|
107
|
+
transactionPlanner: import("@solana/kit").TransactionPlanner;
|
|
108
|
+
}, "transactionPlanExecutor"> & {
|
|
49
109
|
transactionPlanExecutor: import("@solana/kit").TransactionPlanExecutor;
|
|
50
|
-
}>>;
|
|
110
|
+
}, keyof import("@solana/kit").ClientWithTransactionPlanning | keyof import("@solana/kit").ClientWithTransactionSending> & import("@solana/kit").ClientWithTransactionPlanning & import("@solana/kit").ClientWithTransactionSending>>;
|
package/dist/src/kit/index.js
CHANGED
|
@@ -1,67 +1,97 @@
|
|
|
1
|
-
import { address,
|
|
1
|
+
import { address, createClient, createNoopSigner, createSolanaRpc, pipe } from '@solana/kit';
|
|
2
2
|
import { planAndSendTransactions, transactionPlanExecutor as transactionPlanExecutorPlugin, transactionPlanner as transactionPlannerPlugin, } from '@solana/kit-plugin-instruction-plan';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { solanaRpcConnection } from '@solana/kit-plugin-rpc';
|
|
4
|
+
import { identity, payer } from '@solana/kit-plugin-signer';
|
|
5
5
|
import { estimateAndUpdateProvisoryComputeUnitLimitFactory, estimateComputeUnitLimitFactory, } from '@solana-program/compute-budget';
|
|
6
6
|
import { KoraClient } from '../client.js';
|
|
7
|
+
import { koraPlugin } from '../plugin.js';
|
|
7
8
|
import { createKoraTransactionPlanExecutor } from './executor.js';
|
|
8
9
|
import { buildPlaceholderPaymentInstruction, koraPaymentAddress } from './payment.js';
|
|
9
10
|
import { buildComputeBudgetInstructions, createKoraTransactionPlanner } from './planner.js';
|
|
10
|
-
import { koraPlugin } from './plugin.js';
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Kora bundle plugin.
|
|
13
|
+
*
|
|
14
|
+
* Composes RPC + payer + Kora RPC methods + transaction planning/execution into a single
|
|
15
|
+
* plugin that mirrors the shape of `solanaRpc` from `@solana/kit-plugin-rpc`.
|
|
13
16
|
*
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
17
|
+
* The caller's signer is read from `client.identity` — install `identity()` (or `signer()`)
|
|
18
|
+
* from `@solana/kit-plugin-signer` before this plugin. The fee payer (Kora's address) is
|
|
19
|
+
* fetched from the Kora node and installed internally as a noop signer, overriding any
|
|
20
|
+
* `payer` already on the client.
|
|
16
21
|
*
|
|
17
22
|
* @beta This API is experimental and may change in future releases.
|
|
18
23
|
*
|
|
19
24
|
* @example
|
|
20
|
-
* ```
|
|
21
|
-
* import {
|
|
22
|
-
* import {
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { createClient } from '@solana/kit';
|
|
27
|
+
* import { identity } from '@solana/kit-plugin-signer';
|
|
28
|
+
* import { kora } from '@solana/kora';
|
|
29
|
+
*
|
|
30
|
+
* const client = await createClient()
|
|
31
|
+
* .use(identity(userSigner))
|
|
32
|
+
* .use(kora({
|
|
33
|
+
* endpoint: 'https://kora.example.com',
|
|
34
|
+
* rpcUrl: 'https://api.mainnet-beta.solana.com',
|
|
35
|
+
* feeToken: address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
|
|
36
|
+
* }));
|
|
37
|
+
*
|
|
38
|
+
* const signature = await client.sendTransaction([myInstruction]);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function kora(config) {
|
|
42
|
+
return async (client) => {
|
|
43
|
+
const koraClient = new KoraClient({
|
|
44
|
+
apiKey: config.apiKey,
|
|
45
|
+
getRecaptchaToken: config.getRecaptchaToken,
|
|
46
|
+
hmacSecret: config.hmacSecret,
|
|
47
|
+
rpcUrl: config.endpoint,
|
|
48
|
+
});
|
|
49
|
+
const { signer_address, payment_address } = await koraClient.getPayerSigner();
|
|
50
|
+
const paymentAddr = payment_address ? address(payment_address) : undefined;
|
|
51
|
+
const payerSigner = createNoopSigner(address(signer_address));
|
|
52
|
+
const computeBudgetIxs = buildComputeBudgetInstructions(config);
|
|
53
|
+
const solanaRpc = createSolanaRpc(config.rpcUrl);
|
|
54
|
+
const hasCuEstimation = config.computeUnitLimit === undefined;
|
|
55
|
+
const resolveProvisoryComputeUnitLimit = hasCuEstimation
|
|
56
|
+
? estimateAndUpdateProvisoryComputeUnitLimitFactory(estimateComputeUnitLimitFactory({ rpc: solanaRpc }))
|
|
57
|
+
: undefined;
|
|
58
|
+
const userSigner = client.identity;
|
|
59
|
+
const payment = paymentAddr
|
|
60
|
+
? await buildPlaceholderPaymentInstruction(userSigner, paymentAddr, config.feeToken, config.tokenProgramId)
|
|
61
|
+
: undefined;
|
|
62
|
+
const koraTransactionPlanner = createKoraTransactionPlanner(payerSigner, computeBudgetIxs, payment?.instruction, hasCuEstimation);
|
|
63
|
+
const koraTransactionPlanExecutor = createKoraTransactionPlanExecutor(koraClient, config, userSigner, payerSigner, payment
|
|
64
|
+
? {
|
|
65
|
+
destinationTokenAccount: payment.destinationTokenAccount,
|
|
66
|
+
sourceTokenAccount: payment.sourceTokenAccount,
|
|
67
|
+
}
|
|
68
|
+
: undefined, resolveProvisoryComputeUnitLimit);
|
|
69
|
+
return pipe(client, payer(payerSigner), solanaRpcConnection(config.rpcUrl), koraPlugin({
|
|
70
|
+
endpoint: config.endpoint,
|
|
71
|
+
koraClient,
|
|
72
|
+
}), koraPaymentAddress(paymentAddr), transactionPlannerPlugin(koraTransactionPlanner), transactionPlanExecutorPlugin(koraTransactionPlanExecutor), planAndSendTransactions());
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Creates a Kora Kit client composed from Kit plugins.
|
|
77
|
+
*
|
|
78
|
+
* @beta This API is experimental and may change in future releases.
|
|
23
79
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* });
|
|
80
|
+
* @deprecated Compose with the {@link kora} plugin instead:
|
|
81
|
+
* ```ts
|
|
82
|
+
* import { createClient } from '@solana/kit';
|
|
83
|
+
* import { identity } from '@solana/kit-plugin-signer';
|
|
84
|
+
* import { kora } from '@solana/kora';
|
|
30
85
|
*
|
|
31
|
-
* const
|
|
86
|
+
* const client = await createClient()
|
|
87
|
+
* .use(identity(userSigner))
|
|
88
|
+
* .use(kora({ endpoint, rpcUrl, feeToken }));
|
|
32
89
|
* ```
|
|
90
|
+
* Will be removed in 0.4.0.
|
|
33
91
|
*/
|
|
92
|
+
// TODO: Bundle support — the plan/execute pipeline currently handles single transactions only.
|
|
93
|
+
// For Jito bundles, users must manually encode transactions and call `client.kora.signAndSendBundle()`.
|
|
34
94
|
export async function createKitKoraClient(config) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
hmacSecret: config.hmacSecret,
|
|
38
|
-
rpcUrl: config.endpoint,
|
|
39
|
-
});
|
|
40
|
-
const { signer_address, payment_address } = await koraClient.getPayerSigner();
|
|
41
|
-
const paymentAddr = payment_address ? address(payment_address) : undefined;
|
|
42
|
-
const payerSigner = createNoopSigner(address(signer_address));
|
|
43
|
-
const computeBudgetIxs = buildComputeBudgetInstructions(config);
|
|
44
|
-
const solanaRpc = createSolanaRpc(config.rpcUrl);
|
|
45
|
-
const hasCuEstimation = config.computeUnitLimit === undefined;
|
|
46
|
-
const resolveProvisoryComputeUnitLimit = hasCuEstimation
|
|
47
|
-
? estimateAndUpdateProvisoryComputeUnitLimitFactory(estimateComputeUnitLimitFactory({ rpc: solanaRpc }))
|
|
48
|
-
: undefined;
|
|
49
|
-
const payment = paymentAddr
|
|
50
|
-
? await buildPlaceholderPaymentInstruction(config.feePayerWallet, paymentAddr, config.feeToken, config.tokenProgramId)
|
|
51
|
-
: undefined;
|
|
52
|
-
const koraTransactionPlanner = createKoraTransactionPlanner(payerSigner, computeBudgetIxs, payment?.instruction, hasCuEstimation);
|
|
53
|
-
const koraTransactionPlanExecutor = createKoraTransactionPlanExecutor(koraClient, config, payerSigner, payment
|
|
54
|
-
? {
|
|
55
|
-
destinationTokenAccount: payment.destinationTokenAccount,
|
|
56
|
-
sourceTokenAccount: payment.sourceTokenAccount,
|
|
57
|
-
}
|
|
58
|
-
: undefined, resolveProvisoryComputeUnitLimit);
|
|
59
|
-
return createEmptyClient()
|
|
60
|
-
.use(rpc(config.rpcUrl))
|
|
61
|
-
.use(koraPlugin({ apiKey: config.apiKey, endpoint: config.endpoint, hmacSecret: config.hmacSecret }))
|
|
62
|
-
.use(payer(payerSigner))
|
|
63
|
-
.use(koraPaymentAddress(paymentAddr))
|
|
64
|
-
.use(transactionPlannerPlugin(koraTransactionPlanner))
|
|
65
|
-
.use(transactionPlanExecutorPlugin(koraTransactionPlanExecutor))
|
|
66
|
-
.use(planAndSendTransactions());
|
|
95
|
+
const { feePayerWallet, ...bundleConfig } = config;
|
|
96
|
+
return await createClient().use(identity(feePayerWallet)).use(kora(bundleConfig));
|
|
67
97
|
}
|
package/dist/src/kit/payment.js
CHANGED
|
@@ -27,7 +27,7 @@ export async function buildPlaceholderPaymentInstruction(feePayerWallet, payment
|
|
|
27
27
|
authority: feePayerWallet,
|
|
28
28
|
destination: destinationTokenAccount,
|
|
29
29
|
source: sourceTokenAccount,
|
|
30
|
-
});
|
|
30
|
+
}, { programAddress: tokenProgram });
|
|
31
31
|
return { destinationTokenAccount, instruction, sourceTokenAccount };
|
|
32
32
|
}
|
|
33
33
|
function isPlaceholderPaymentInstruction(ix, sourceTokenAccount, destinationTokenAccount, feePayerWallet, tokenProgramId) {
|
|
@@ -50,12 +50,13 @@ export function updatePaymentInstructionAmount(instructions, feePayerWallet, sou
|
|
|
50
50
|
return ix;
|
|
51
51
|
}
|
|
52
52
|
replaced = true;
|
|
53
|
+
const tokenProgram = tokenProgramId ?? TOKEN_PROGRAM_ADDRESS;
|
|
53
54
|
return getTransferInstruction({
|
|
54
55
|
amount,
|
|
55
56
|
authority: feePayerWallet,
|
|
56
57
|
destination: destinationTokenAccount,
|
|
57
58
|
source: sourceTokenAccount,
|
|
58
|
-
});
|
|
59
|
+
}, { programAddress: tokenProgram });
|
|
59
60
|
});
|
|
60
61
|
if (!replaced) {
|
|
61
62
|
throw new Error('Failed to update payment instruction: no matching placeholder transfer instruction found. ' +
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type Instruction, type TransactionSigner } from '@solana/kit';
|
|
2
|
-
import type {
|
|
3
|
-
export declare function buildComputeBudgetInstructions(config:
|
|
2
|
+
import type { KoraBundleConfig } from '../types/index.js';
|
|
3
|
+
export declare function buildComputeBudgetInstructions(config: KoraBundleConfig): Instruction[];
|
|
4
4
|
export declare function createKoraTransactionPlanner(payerSigner: TransactionSigner, computeBudgetIxs: Instruction[], paymentInstruction: Instruction | undefined, hasCuEstimation: boolean): import("@solana/kit").TransactionPlanner;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { EstimateBundleFeeRequest, EstimateTransactionFeeRequest, GetPaymentInstructionRequest, GetVersionResponse, KitBlockhashResponse, KitConfigResponse, KitEstimateBundleFeeResponse, KitEstimateFeeResponse, KitPayerSignerResponse, KitPaymentInstructionResponse, KitSignAndSendBundleResponse, KitSignAndSendTransactionResponse, KitSignBundleResponse, KitSignTransactionResponse, KitSupportedTokensResponse, KoraPluginConfig, SignAndSendBundleRequest, SignAndSendTransactionRequest, SignBundleRequest, SignTransactionRequest } from './types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a Kora Kit plugin that adds Kora paymaster functionality to a Kit client.
|
|
4
|
+
*
|
|
5
|
+
* The plugin exposes all Kora RPC methods with Kit-typed responses (Address, Blockhash).
|
|
6
|
+
*
|
|
7
|
+
* **Note:** The plugin pattern with `createClient().use()` requires `@solana/kit` v6.8.0+.
|
|
8
|
+
* For older kit versions, use `KoraClient` directly instead.
|
|
9
|
+
*
|
|
10
|
+
* @param config - Plugin configuration
|
|
11
|
+
* @param config.endpoint - Kora RPC endpoint URL
|
|
12
|
+
* @param config.apiKey - Optional API key for authentication
|
|
13
|
+
* @param config.hmacSecret - Optional HMAC secret for signature-based authentication
|
|
14
|
+
* @returns A Kit plugin function that adds `.kora` to the client
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { createClient } from '@solana/kit';
|
|
19
|
+
* import { koraPlugin } from '@solana/kora';
|
|
20
|
+
*
|
|
21
|
+
* const client = createClient()
|
|
22
|
+
* .use(koraPlugin({ endpoint: 'https://kora.example.com' }));
|
|
23
|
+
*
|
|
24
|
+
* // All responses have Kit-typed fields
|
|
25
|
+
* const config = await client.kora.getConfig();
|
|
26
|
+
* // config.fee_payers is Address[] not string[]
|
|
27
|
+
*
|
|
28
|
+
* const { signer_pubkey } = await client.kora.signTransaction({ transaction: tx });
|
|
29
|
+
* // signer_pubkey is Address not string
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function koraPlugin(config: KoraPluginConfig): <T extends object>(c: T) => T & {
|
|
33
|
+
kora: {
|
|
34
|
+
/**
|
|
35
|
+
* Estimates the bundle fee with Kit-typed addresses.
|
|
36
|
+
*/
|
|
37
|
+
estimateBundleFee(request: EstimateBundleFeeRequest): Promise<KitEstimateBundleFeeResponse>;
|
|
38
|
+
/**
|
|
39
|
+
* Estimates the transaction fee with Kit-typed addresses.
|
|
40
|
+
*/
|
|
41
|
+
estimateTransactionFee(request: EstimateTransactionFeeRequest): Promise<KitEstimateFeeResponse>;
|
|
42
|
+
/**
|
|
43
|
+
* Gets the latest blockhash with Kit Blockhash type.
|
|
44
|
+
*/
|
|
45
|
+
getBlockhash(): Promise<KitBlockhashResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves the current Kora server configuration with Kit-typed addresses.
|
|
48
|
+
*/
|
|
49
|
+
getConfig(): Promise<KitConfigResponse>;
|
|
50
|
+
/**
|
|
51
|
+
* Retrieves the payer signer and payment destination with Kit-typed addresses.
|
|
52
|
+
*/
|
|
53
|
+
getPayerSigner(): Promise<KitPayerSignerResponse>;
|
|
54
|
+
/**
|
|
55
|
+
* Creates a payment instruction with Kit-typed response.
|
|
56
|
+
*/
|
|
57
|
+
getPaymentInstruction(request: GetPaymentInstructionRequest): Promise<KitPaymentInstructionResponse>;
|
|
58
|
+
/**
|
|
59
|
+
* Retrieves the list of tokens supported for fee payment with Kit-typed addresses.
|
|
60
|
+
*/
|
|
61
|
+
getSupportedTokens(): Promise<KitSupportedTokensResponse>;
|
|
62
|
+
/**
|
|
63
|
+
* Gets the version of the Kora server.
|
|
64
|
+
*/
|
|
65
|
+
getVersion(): Promise<GetVersionResponse>;
|
|
66
|
+
/**
|
|
67
|
+
* Signs and sends a bundle of transactions via Jito with Kit-typed response.
|
|
68
|
+
*/
|
|
69
|
+
signAndSendBundle(request: SignAndSendBundleRequest): Promise<KitSignAndSendBundleResponse>;
|
|
70
|
+
/**
|
|
71
|
+
* Signs and sends a transaction with Kit-typed response.
|
|
72
|
+
*/
|
|
73
|
+
signAndSendTransaction(request: SignAndSendTransactionRequest): Promise<KitSignAndSendTransactionResponse>;
|
|
74
|
+
/**
|
|
75
|
+
* Signs a bundle of transactions with Kit-typed response.
|
|
76
|
+
*/
|
|
77
|
+
signBundle(request: SignBundleRequest): Promise<KitSignBundleResponse>;
|
|
78
|
+
/**
|
|
79
|
+
* Signs a transaction with Kit-typed response.
|
|
80
|
+
*/
|
|
81
|
+
signTransaction(request: SignTransactionRequest): Promise<KitSignTransactionResponse>;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/** Type representing the Kora API exposed by the plugin */
|
|
85
|
+
export type KoraApi = ReturnType<ReturnType<typeof koraPlugin>>['kora'];
|