stablekit

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 10, 2026 License: MIT Imports: 21 Imported by: 0

README

stablekit

A focused Go SDK for stablecoin operations on Solana — balance queries, SPL transfers (regular and gasless via Kora), and Jupiter quotes.

Wraps solana-go and bundles internal Kora and Jupiter clients so callers only need a single import.

Scope

Intentionally narrow — stablecoin send + balance + quote. Not a general-purpose Solana SDK; drop down to client.RPC() for anything outside this surface.

Installation

go get github.com/rillehq/stablekit

Quick start

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/gagliardetto/solana-go"
	"github.com/rillehq/stablekit"
)

func main() {
	c := stablekit.NewClient(stablekit.Config{
		RPCEndpoint:  "https://api.mainnet-beta.solana.com",
		KoraEndpoint: "https://kora.example.com",
		KoraAPIKey:   "...",
	})
	ctx := context.Background()

	owner := solana.MustPublicKeyFromBase58("...")

	// Balance
	bal, err := c.Balance(ctx, owner, stablekit.USDC)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("USDC balance: %d\n", bal)

	// Gasless transfer (Kora pays SOL fee)
	sig, err := c.GaslessTransfer(ctx, stablekit.GaslessTransferOpts{
		SenderSigner: solana.MustPrivateKeyFromBase58("..."),
		Recipient:    solana.MustPublicKeyFromBase58("..."),
		Mint:         stablekit.USDC,
		Amount:       1_000_000, // 1 USDC
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Sent:", sig)

	// Jupiter quote (USDT → USDC)
	quote, err := c.Quote(ctx, stablekit.USDT, stablekit.USDC, 1_000_000, 10)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Out:", quote.OutAmount, "Impact:", quote.PriceImpactPct)

	// Execute the swap (user pays SOL fee)
	res, err := c.Swap(ctx, stablekit.SwapOpts{
		UserSigner:  solana.MustPrivateKeyFromBase58("..."),
		InputMint:   stablekit.USDT,
		OutputMint:  stablekit.USDC,
		Amount:      1_000_000,
		SlippageBps: 10,
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Swap signature:", res.Signature)
}

API surface

Method Purpose
Balance SPL token balance for an owner+mint (returns 0 if ATA missing)
ResolveATA Derive the owner's ATA address; report whether it exists on-chain
CreateATA Create the owner's ATA on-chain (idempotent; ~0.002 SOL rent)
SendStable SPL transfer where the sender pays the SOL fee
GaslessTransfer SPL transfer where Kora pays the SOL fee (user pays in token)
GaslessTransferTx Kora-built simple transfer (returns base64 transaction)
Quote Jupiter v6 quote (USDT↔USDC etc.)
Swap Quote + execute swap in one call (user pays SOL fee)
RPC() Underlying solana-go RPC client (escape hatch)
KoraEnabled() Whether Kora was configured

Configuration

Field Description Default
RPCEndpoint Solana JSON-RPC URL (required)
KoraEndpoint Kora fee-abstraction URL. Empty = gasless calls return ErrKoraDisabled
KoraAPIKey x-api-key header for Kora
KoraHMACSecret HMAC secret for x-hmac-signature per Kora request
JupiterEndpoint Override Jupiter v6 URL https://quote-api.jup.ag/v6
JupiterAPIKey x-api-key for paid Jupiter tiers
HTTPClient Shared *http.Client 15s timeout
MaxRetries Retries on 429 / 5xx / transport errors 3

Errors

import "errors"

_, err := c.GaslessTransfer(ctx, opts)
if errors.Is(err, stablekit.ErrKoraDisabled) {
	// Kora not configured
}

var ke *stablekit.KoraError
if errors.As(err, &ke) {
	fmt.Printf("kora rpc error %d: %s\n", ke.Code, ke.Message)
}

var je *stablekit.JupiterError
if errors.As(err, &je) {
	fmt.Printf("jupiter http %d: %s\n", je.StatusCode, je.Body)
}

License

MIT

Documentation

Overview

Package stablekit is a focused Go SDK for stablecoin operations on Solana: balance queries, SPL transfers (regular and gasless via Kora), and Jupiter quotes. It wraps solana-go and bundles internal Kora JSON-RPC and Jupiter HTTP clients.

Scope is intentionally narrow: stablecoin balance + send + quote. It is not a general-purpose Solana SDK — drop down to the underlying solana-go RPC when you need more.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrSourceATAMissing    = errors.New("stablekit: source associated token account does not exist")
	ErrDestATAMissing      = errors.New("stablekit: destination associated token account does not exist")
	ErrInsufficientBalance = errors.New("stablekit: insufficient token balance")
	ErrKoraDisabled        = errors.New("stablekit: Kora is not configured (set Config.KoraEndpoint to enable gasless calls)")
)

Sentinel errors.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a thread-safe stablecoin SDK over solana-go, with Kora and Jupiter clients bundled.

func NewClient

func NewClient(cfg Config) *Client

NewClient creates a stablekit Client. Panics if RPCEndpoint is empty.

func (*Client) Balance

func (c *Client) Balance(ctx context.Context, owner solana.PublicKey, mint Mint) (uint64, error)

Balance returns the raw token balance held by owner's Associated Token Account for mint. Returns 0 (no error) if the ATA does not yet exist.

func (*Client) CreateATA added in v0.1.2

func (c *Client) CreateATA(ctx context.Context, opts CreateATAOpts) (CreateATAResult, error)

CreateATA creates the Associated Token Account for opts.Owner+opts.Mint if it does not already exist. PayerSigner pays the ~0.002 SOL rent and signs the transaction.

Idempotent: if the ATA already exists, no transaction is sent and AlreadyExists is true.

func (*Client) GaslessTransfer

func (c *Client) GaslessTransfer(ctx context.Context, opts GaslessTransferOpts) (string, error)

GaslessTransfer transfers SPL stablecoin via Kora. Kora pays the SOL fee and broadcasts; the user only pays the fee in opts.FeeToken (defaults to opts.Mint). Returns the on-chain transaction signature.

func (*Client) GaslessTransferTx

func (c *Client) GaslessTransferTx(ctx context.Context, opts GaslessTransferTxOpts) (string, error)

GaslessTransferTx asks Kora to build a complete pre-signed transfer transaction (Kora's TransferTransaction RPC) and returns the base64 transaction. Useful when you want Kora to handle the entire flow.

func (*Client) KoraEnabled

func (c *Client) KoraEnabled() bool

KoraEnabled reports whether the Kora client was configured.

func (*Client) Quote

func (c *Client) Quote(ctx context.Context, in, out Mint, amount uint64, slippageBps int) (QuoteResponse, error)

Quote returns a Jupiter swap quote between two stablecoin mints.

func (*Client) RPC

func (c *Client) RPC() *rpc.Client

RPC returns the underlying solana-go RPC client. Use this when stablekit does not expose an operation directly. The returned client is shared — do not Close it.

func (*Client) ResolveATA

func (c *Client) ResolveATA(ctx context.Context, owner solana.PublicKey, mint Mint) (address solana.PublicKey, exists bool, err error)

ResolveATA derives the Associated Token Account address for owner+mint and reports whether it exists on-chain.

func (*Client) SendStable

func (c *Client) SendStable(ctx context.Context, opts SendOpts) (SendResult, error)

SendStable transfers SPL stablecoin from opts.From to opts.To. The sender pays the SOL fee. Auto-creates the destination ATA if opts.CreateDestATA is true.

func (*Client) Swap added in v0.1.1

func (c *Client) Swap(ctx context.Context, opts SwapOpts) (SwapResult, error)

Swap quotes and executes a Jupiter swap in one call. The user pays the SOL fee. Returns the on-chain signature and the realized quote.

type Config

type Config struct {
	// RPCEndpoint is a Solana JSON-RPC endpoint URL (required).
	// Example: https://api.mainnet-beta.solana.com
	RPCEndpoint string

	// KoraEndpoint is the Kora fee-abstraction JSON-RPC URL.
	// Optional. When unset, GaslessTransfer/GaslessTransferTx return ErrKoraDisabled.
	KoraEndpoint string
	// KoraAPIKey is sent as the x-api-key header when calling Kora.
	KoraAPIKey string
	// KoraHMACSecret signs each Kora request as x-hmac-signature.
	KoraHMACSecret string

	// JupiterEndpoint overrides the default Jupiter v6 endpoint
	// (https://quote-api.jup.ag/v6).
	JupiterEndpoint string
	// JupiterAPIKey is an optional Jupiter API key for paid tiers.
	JupiterAPIKey string

	// HTTPClient is shared by Kora and Jupiter clients. Defaults to a 15s
	// timeout client.
	HTTPClient *http.Client
	// MaxRetries on transient errors (5xx, 429, transport). Defaults to 3.
	MaxRetries int
}

Config holds the configuration for a stablekit Client.

type CreateATAOpts added in v0.1.2

type CreateATAOpts struct {
	// Owner is the wallet that will own the ATA. Can be any pubkey —
	// callers commonly create ATAs for their users from a service-owned
	// payer wallet.
	Owner solana.PublicKey
	// Mint identifies the stablecoin.
	Mint Mint
	// PayerSigner pays the ~0.002 SOL rent and signs the transaction.
	// Typically a Rille-controlled wallet when warming up a new user
	// deposit address.
	PayerSigner solana.PrivateKey
}

CreateATAOpts is the parameter for Client.CreateATA.

type CreateATAResult added in v0.1.2

type CreateATAResult struct {
	// Address is the derived ATA address.
	Address solana.PublicKey
	// Signature is the on-chain transaction signature. Zero if AlreadyExists.
	Signature solana.Signature
	// AlreadyExists is true if the ATA already existed and no tx was sent.
	AlreadyExists bool
}

CreateATAResult is what Client.CreateATA returns.

type GaslessTransferOpts

type GaslessTransferOpts struct {
	// SenderSigner is the user's private key (also derives the source wallet).
	SenderSigner solana.PrivateKey
	// Recipient is the recipient owner pubkey (not an ATA).
	Recipient solana.PublicKey
	// Mint identifies the stablecoin.
	Mint Mint
	// Amount is in the token's smallest unit.
	Amount uint64
	// FeeToken is the mint that Kora will deduct the fee from. Defaults to
	// Mint when empty.
	FeeToken Mint
}

GaslessTransferOpts is the parameter for Client.GaslessTransfer.

The user signs only their portion of the transaction; Kora signs as fee payer and broadcasts. The user pays the fee in the transfer token (or any token Kora supports), not in SOL.

type GaslessTransferTxOpts

type GaslessTransferTxOpts struct {
	// Source is the sender wallet address.
	Source string
	// Destination is the recipient wallet address.
	Destination string
	// Mint identifies the stablecoin.
	Mint Mint
	// Amount is in the token's smallest unit.
	Amount uint64
}

GaslessTransferTxOpts is the parameter for Client.GaslessTransferTx — the Kora-built "simple transfer" path.

type JupiterError

type JupiterError struct {
	StatusCode int
	Body       string
}

JupiterError represents a non-2xx response from Jupiter.

func (*JupiterError) Error

func (e *JupiterError) Error() string

Error implements the error interface.

type KoraError

type KoraError struct {
	Code    int
	Message string
}

KoraError represents an error returned by the Kora JSON-RPC server.

func (*KoraError) Error

func (e *KoraError) Error() string

Error implements the error interface.

type Mint

type Mint string

Mint is the on-chain mint address of a stablecoin.

const (
	USDC Mint = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
	USDT Mint = "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
	EURC Mint = "HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr"
)

Common Solana stablecoin mints. Adding a constant here is a convenience — any base58 mint address works at the API surface.

func (Mint) PublicKey

func (m Mint) PublicKey() (solana.PublicKey, error)

PublicKey parses the mint as a solana.PublicKey.

func (Mint) String

func (m Mint) String() string

String returns the mint address as a string.

type QuoteResponse

type QuoteResponse struct {
	InputMint            string         `json:"inputMint"`
	InAmount             string         `json:"inAmount"`
	OutputMint           string         `json:"outputMint"`
	OutAmount            string         `json:"outAmount"`
	OtherAmountThreshold string         `json:"otherAmountThreshold"`
	SwapMode             string         `json:"swapMode"`
	SlippageBps          int            `json:"slippageBps"`
	PriceImpactPct       string         `json:"priceImpactPct"`
	RoutePlan            []routePlanHop `json:"routePlan"`
}

QuoteResponse is the body returned by Jupiter's GET /quote.

type SendOpts

type SendOpts struct {
	// From is the sender wallet (also the SOL fee payer).
	From solana.PublicKey
	// FromSigner signs the transaction. Must control the wallet at From.
	FromSigner solana.PrivateKey
	// To is the recipient owner pubkey (not an ATA).
	To solana.PublicKey
	// Mint identifies the stablecoin.
	Mint Mint
	// Amount is in the token's smallest unit (e.g. 1 USDC = 1_000_000).
	Amount uint64
	// CreateDestATA controls whether to add an ATA-creation instruction
	// when the recipient's ATA does not yet exist. When false, SendStable
	// returns ErrDestATAMissing.
	CreateDestATA bool
}

SendOpts is the parameter for Client.SendStable.

type SendResult

type SendResult struct {
	// Signature is the on-chain transaction signature.
	Signature solana.Signature
	// CreatedDestATA is true if the recipient's ATA was created in this tx.
	CreatedDestATA bool
}

SendResult is what Client.SendStable returns.

type SwapOpts added in v0.1.1

type SwapOpts struct {
	// UserSigner signs the swap tx and provides the user pubkey. Must hold
	// a balance of InputMint at the derived ATA.
	UserSigner solana.PrivateKey
	// InputMint is the source stablecoin mint.
	InputMint Mint
	// OutputMint is the destination stablecoin mint.
	OutputMint Mint
	// Amount is the input amount in the InputMint's smallest unit.
	Amount uint64
	// SlippageBps caps acceptable slippage in basis points (e.g. 10 = 0.10%).
	// Defaults to 50 if zero.
	SlippageBps int
	// WrapAndUnwrapSol asks Jupiter to auto-wrap SOL → wSOL and unwrap on
	// the way back. Only relevant if InputMint or OutputMint is wrapped SOL.
	// Defaults to true.
	WrapAndUnwrapSol *bool
}

SwapOpts is the parameter for Client.Swap.

Swap is a one-call helper that fetches a Jupiter quote, asks Jupiter to build a swap transaction, signs it with UserSigner, and submits it. The user pays the SOL fee. For "user has zero SOL" scenarios use the (yet to be added) GaslessSwap.

type SwapResult added in v0.1.1

type SwapResult struct {
	// Signature is the on-chain transaction signature.
	Signature solana.Signature
	// Quote is the Jupiter quote that was actually executed (handy for
	// reporting realized price / impact back to callers).
	Quote QuoteResponse
}

SwapResult is what Client.Swap returns.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL