we should also include a sample example nextjs folder to test out the action(to be included in /src/app/api/action/transferTx/route.ts , or we only include the route.ts file for the action and test it out in another nextjs folder and put that demo?
Also where should the route.ts file be included in this repo?
Kora is a Solana paymaster node that provides a JSON-RPC interface for handling gasless transactions and fee abstractions. It enables developers to build applications where users can pay transaction fees in tokens other than SOL.
- JSON-RPC server with middleware support
- Transaction fee estimation in any supported token
- Gasless transaction support
- Transfer transactions with any supported token
- Remote signer support
- Disallowed account, allowed spl tokens, programs config
Note: only
signAndSend
submits a transaction to an RPC, all other methods only return a signed transaction
kora-lib
: Shared library for korakora-rpc
: JSON-RPC serverkora-cli
: Command line interface for kora
git clone https://github.com/solana-foundation/kora.git
cd kora
make install
Basic usage:
kora -- [OPTIONS]
Option | Description | Default |
---|---|---|
-p, --port <PORT> |
Port number for the RPC server | 8080 |
--rpc-url <RPC_URL> |
RPC URL to connect to | http://127.0.0.1:8899 |
--logging-format <FORMAT> |
Logging format (standard or json) | standard |
--metrics-endpoint <ENDPOINT> |
Optional metrics endpoint URL | - |
--private-key <PRIVATE_KEY> |
Base58-encoded private key for signing | - |
--config <FILE> |
Path to kora.toml config file | kora.toml |
--no-load-signer |
Skip loading the signer | false |
--with-turnkey-signer |
Use Turnkey signer | false |
--turnkey-api-public-key <TURNKEY_API_PUBLIC_KEY> |
Turnkey API public key | - |
--turnkey-api-private-key <TURNKEY_API_PRIVATE_KEY> |
Turnkey API private key | - |
--turnkey-organization-id <TURNKEY_ORGANIZATION_ID> |
Turnkey organization ID | - |
--turnkey-private-key-id <TURNKEY_PRIVATE_KEY_ID> |
Turnkey private key ID | - |
--turnkey-public-key <TURNKEY_PUBLIC_KEY> |
Turnkey public key | - |
Variable | Description | Example |
---|---|---|
RUST_LOG |
Controls log level and filtering | "info,sqlx=error" |
RPC_URL |
Alternative way to specify the RPC URL | - |
KORA_PRIVATE_KEY |
Alternative way to specify the signing private key | - |
TURNKEY_API_PUBLIC_KEY |
Turnkey API public key | - |
TURNKEY_API_PRIVATE_KEY |
Turnkey API private key | - |
TURNKEY_ORGANIZATION_ID |
Turnkey organization ID | - |
TURNKEY_PRIVATE_KEY_ID |
Turnkey private key ID | - |
TURNKEY_PUBLIC_KEY |
Turnkey public key | - |
TEST_SENDER_PUBKEY |
Test sender pubkey | - |
TEST_SENDER_MNEMONIC |
Test sender mnemonic | - |
The kora.toml
file configures the paymaster node's features and supported tokens:
[validation]
max_allowed_lamports = 1000000
max_signatures = 10
allowed_programs = [
"11111111111111111111111111111111", # System Program
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", # Token Program
]
allowed_tokens = [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", # USDC
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", # USDT
"So11111111111111111111111111111111111111112", # SOL
]
allowed_spl_paid_tokens = [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", # USDC
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", # USDT
]
disallowed_accounts = []
Estimates the transaction fee in terms of a specified token.
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "estimateTransactionFee",
"params": [
{
"transaction": "<base64-encoded-transaction>",
"fee_token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
}
]
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"fee_in_lamports": "1000000"
}
}
Returns supported token addresses.
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "getSupportedTokens",
"params": []
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tokens": [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"So11111111111111111111111111111111111111112"
]
}
}
Signs a transaction with the paymaster's key.
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "signTransaction",
"params": [
{
"transaction": "<base64-encoded-transaction>"
}
]
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"signature": "<base58-encoded-signature>",
"signed_transaction": "<base64-encoded-signed-transaction>"
}
}
Signs and submits a transaction to the network.
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "signAndSend",
"params": [
{
"transaction": "<base64-encoded-transaction>"
}
]
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"signature": "<base58-encoded-signature>",
"signed_transaction": "<bas64-encoded-signed-transaction>"
}
}
Create a transfer request and sign as the paymaster (SPL and SOL)
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "transactionTransfer",
"params": [
1000000, // lamports value
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // mint address
"5KKsLVU6TcbVDK4BS6K1DGDxnh4Q9xjYJ8XaDCG5t8ht", // source
"AVmDft8deQEo78bRKcGN5ZMf3hyjeLBK4Rd4xGB46yQM" // recipient
]
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"transaction": "<base64-encoded-transaction>",
"message": "<message>",
"blockhash": "<base58-encoded-blockhash>"
}
}
Returns the latest blockhash.
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockhash",
"params": []
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockhash": "<base58-encoded-blockhash>"
}
}
Returns the paymaster configuration.
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "getConfig",
"params": []
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"fee_payer": "<payer-pubkey>",
"validation_config": {
"max_allowed_lamports": 1000000,
"max_signatures": 10,
"allowed_programs": ["11111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
"allowed_tokens": ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", "So11111111111111111111111111111111111111112"]
}
}
}
Signs a transaction if the user has paid the required amount of tokens.
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "signTransactionIfPaid",
"params": [
{
"transaction": "<base64-encoded-transaction>",
"cost_in_lamports": 1000000,
"margin": 0.0
}
]
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"signature": "<base58-encoded-signature>",
"signed_transaction": "<base64-encoded-signed-transaction>"
}
}
- Rust 1.70 or later
- Solana CLI tools (for testing)
- A Solana RPC endpoint (local or remote)
- Docker
- swagger-cli (
npm install -g swagger-cli
) for API documentation
# Build
make build
# Run all tests
make test
# Run integration tests
make test-integrations
# Basic
kora -- \
--rpc-url <RPC_URL> \
--port <PORT>
# With Turnkey (or use environment variables)
kora -- \
--rpc-url <RPC_URL> \
--port <PORT> \
--with-turnkey-signer \
--turnkey-api-public-key <TURNKEY_API_PUBLIC_KEY> \
--turnkey-api-private-key <TURNKEY_API_PRIVATE_KEY> \
--turnkey-organization-id <TURNKEY_ORGANIZATION_ID> \
--turnkey-private-key-id <TURNKEY_PRIVATE_KEY_ID> \
--turnkey-public-key <TURNKEY_PUBLIC_KEY>
# No signer
kora -- \
--rpc-url <RPC_URL> \
--port <PORT> \
--no-load-signer
# Load private key at runtime without .env
kora -- \
--rpc-url <RPC_URL> \
--port <PORT> \
--private-key <PRIVATE_KEY>
# Run clippy with warnings as errors
make lint
# Run clippy with auto-fix
make lint-fix-all
# Format code
make fmt
# Openapi
make openapi
-
Start a local Solana validator:
solana-test-validator
-
Run the server with development settings:
RUST_LOG=debug cargo run -- --port 9000 --rpc-url http://localhost:8899
This project is licensed under the MIT License - see the LICENSE file for details.