Sign In

@solana-compass/sdk

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solana-compass/sdk

Solana SDK for building apps with Jupiter, Kamino, MarginFi, Drift, Loopscale, and more

latest
npmnpm
Version
0.3.5
Version published
Maintainers
1
Created
Source

@solana-compass/sdk

Solana SDK for building apps with Jupiter, Kamino, MarginFi, Drift, Loopscale, and more. Extracted from the Sol CLI so websites, bots, and other consumers can reuse the same integrations without dragging in Commander, SQLite, or filesystem dependencies.

Install

npm install @solana-compass/sdk @solana/kit

Optional peer dependencies for DeFi protocol support:

# Kamino lending & earn vaults
npm install @kamino-finance/klend-sdk @coral-xyz/anchor

# MarginFi lending
npm install @mrgnlabs/marginfi-client-v2

# Drift lending
npm install @drift-labs/sdk

If these aren't installed, those providers are silently skipped.

Quick Start

import { createSolSdk, registerDefaultProviders, InMemoryConfig } from '@solana-compass/sdk';
import { createSolanaRpc } from '@solana/kit';

const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');

const sdk = createSolSdk({
  rpc,
  rpcUrl: 'https://api.mainnet-beta.solana.com', // needed for v1 compat providers
  signer: {
    getSigner: async (id) => walletAdapter, // your TransactionSigner
    getAddress: async (id) => walletAdapter.address,
  },
});

// Register DeFi providers (Kamino, MarginFi, Drift, Jupiter Lend, Loopscale)
await registerDefaultProviders(sdk);

// Read-only — works immediately
const prices = await sdk.price.getPrices(['So11111111111111111111111111111111']);
const rates = await sdk.lend.getRates(['SOL', 'USDC']);
const quote = await sdk.swap.getQuote('SOL', 'USDC', 1.0);

// Write — wallet signs the transaction
const result = await sdk.swap.executeSwap('SOL', 'USDC', 1.0, 'main');

// Portfolio
const report = await sdk.portfolio.get([
  { name: 'main', address: 'YourWa11etAddress...' },
]);

Architecture

The SDK uses context-based dependency injection. Every service closes over a SolContext that carries pluggable implementations for logging, caching, config, signing, and transaction logging.

Your App                              Sol CLI
┌─────────────────────┐     ┌──────────────────────────┐
│ WalletAdapter signer │     │ FileSigner (key files)    │
│ InMemoryCache        │     │ SqliteCache (repos)       │
│ NoopLogger           │     │ CliLogger (verbose/warn)  │
│ NoopTransactionLogger│     │ SqliteTxLogger (tx_log)   │
└──────────┬──────────┘     │ TomlConfig (config.toml)  │
           │                └──────────┬───────────────┘
           ▼                           ▼
    ┌─────────────────────────────────────┐
    │  @solana-compass/sdk                │
    │  createSolSdk(opts) → SolSdk        │
    │  price, swap, stake, lend, earn,    │
    │  orders, predict, portfolio, x402   │
    └─────────────────────────────────────┘

SolSdk Interface

interface SolSdk {
  price:     PriceService;
  token:     TokenService;
  registry:  TokenRegistryService;
  lists:     TokenListsService;
  swap:      SwapService;
  stake:     StakeService;
  lend:      LendService;
  earn:      EarnService;
  order:     OrderService;
  predict:   PredictService;
  portfolio: PortfolioService;
  x402:      X402Service;
  onramp:    OnrampService;
  tx:        TransactionService;
  ctx:       SolContext;
}

DI Interfaces

SolSdkOptions

interface SolSdkOptions {
  rpc: Rpc<SolanaRpcApi>;
  rpcUrl?: string;                           // needed by v1 compat (MarginFi, Drift)
  logger?: Logger;                           // defaults to NoopLogger
  config?: ConfigProvider;                   // defaults to InMemoryConfig({})
  cache?: CacheProvider;                     // defaults to InMemoryCache
  txLogger?: TransactionLogger;              // defaults to NoopTransactionLogger
  signer: SignerProvider;                    // required
  analyticsInstruction?: () => Instruction | null;
}

Logger

interface Logger {
  verbose(msg: string): void;
  warn(msg: string): void;
}

ConfigProvider

interface ConfigProvider {
  get(key: string): unknown;  // supports dot-notation: 'api.jupiterApiKey'
}

Config keys used by the SDK:

KeyTypeDescription
api.jupiterApiKeystringJupiter API key (optional, falls back to lite API)
api.jupiterBaseUrlstringProxy URL for Jupiter API calls
api.dflowApiKeystringDFlow router API key
api.dflowBaseUrlstringProxy URL for DFlow API calls
api.loopscaleBaseUrlstringProxy URL for Loopscale API calls
api.coingeckoApiKeystringCoinGecko API key (price fallback)
defaults.routerstringDefault swap router (best, jupiter, dflow)
defaults.validatorstringDefault staking validator vote account
defaults.slippagenumberDefault swap slippage in basis points
lend.defaultProtocolstringDefault lending protocol
earn.defaultProtocolstringDefault earn protocol
predict.defaultProviderstringDefault prediction market provider

CacheProvider

interface CacheProvider {
  // Price cache
  getLatestPrice(mint: string): PriceCacheEntry | undefined;
  insertPrice(mint: string, priceUsd: number, source: string): void;

  // Token cache
  getTokenByMint(mint: string): TokenCacheEntry | undefined;
  getTokenBySymbol(symbol: string): TokenCacheEntry[];
  isTokenCacheStale(mint: string, ttlHours?: number): boolean;
  upsertTokenBatch(tokens: TokenUpsert[]): void;

  // Optional — token lists
  getTokenList?(category: string, interval: string | null): TokenListCacheRow[];
  replaceTokenList?(category: string, interval: string | null, entries: TokenListCacheRow[]): void;
  isTokenListStale?(category: string, interval: string | null, ttlMinutes: number): boolean;

  // Optional — snapshots
  createSnapshot?(label?: string): number;
  insertSnapshotEntry?(entry: SnapshotEntryRow): void;
  getSnapshotEntries?(snapshotId: number): SnapshotEntryRow[];
  getSnapshot?(id: number): SnapshotRow | undefined;
  getLatestSnapshot?(): SnapshotRow | undefined;
  listSnapshots?(limit: number): SnapshotRow[];
}

SignerProvider

interface SignerProvider {
  getSigner(identifier: string): Promise<TransactionSigner>;
  getAddress(identifier: string): Promise<string>;
  getRawBytes?(identifier: string): Uint8Array;  // v1 compat (MarginFi oracle crank)
}

The identifier is whatever your app uses to distinguish wallets. The CLI uses wallet names (e.g. "main"), but you can use addresses, indexes, or anything.

TransactionLogger

interface TransactionLogger {
  log(entry: TxLogEntry): void;
  updateStatus(signature: string, status: string, error?: string): void;
}

interface TxLogEntry {
  signature: string;
  type: string;
  walletName?: string;
  fromMint?: string;
  toMint?: string;
  fromAmount?: string;
  toAmount?: string;
  fromPriceUsd?: number;
  toPriceUsd?: number;
  status: string;
  error?: string;
}

Default Implementations

The SDK ships with zero-dependency defaults so you can get started without implementing every interface:

import {
  NoopLogger,              // verbose/warn are no-ops
  NoopTransactionLogger,   // log/updateStatus are no-ops
  InMemoryConfig,          // wraps a plain object, dot-notation get
  InMemoryCache,           // Map-based price + token cache
} from '@solana-compass/sdk';

// Pass a config object with dot-separated keys
const config = new InMemoryConfig({
  'api.jupiterBaseUrl': 'https://mysite.com/api/jupiter',
  'defaults.slippage': 100,
});
config.get('api.jupiterBaseUrl'); // 'https://mysite.com/api/jupiter'
config.get('defaults.slippage');  // 100

Service Reference

Price

sdk.price.getPrices(mints: string[]): Promise<Map<string, PriceResult>>
sdk.price.getPrice(mint: string): Promise<PriceResult | undefined>
sdk.price.getCachedPrice(mint: string): PriceResult | undefined

Fetches USD prices via Jupiter Price API with CoinGecko fallback. Results are cached.

import { SOL_MINT } from '@solana-compass/sdk';

const prices = await sdk.price.getPrices([SOL_MINT]);
const solPrice = prices.get(SOL_MINT);
console.log(solPrice?.priceUsd); // 150.42

Token

sdk.token.getSolBalance(addr: string): Promise<number>
sdk.token.getTokenBalances(walletAddress: string): Promise<TokenBalance[]>
sdk.token.getAllTokenAccounts(walletAddress: string): Promise<TokenAccountInfo[]>
interface TokenBalance {
  mint: string;
  symbol: string;
  name: string;
  decimals: number;
  balance: string;     // raw lamport/token amount
  uiBalance: number;   // human-readable
}

Token Registry

sdk.registry.resolveToken(symbolOrMint: string): Promise<TokenMetadata | undefined>
sdk.registry.resolveTokens(queries: string[]): Promise<Map<string, TokenMetadata>>
sdk.registry.syncTokenCache(): Promise<number>

Resolves token symbols (e.g. "SOL", "USDC") or mint addresses to full metadata. Uses a well-known token list first, then falls back to Jupiter's token API.

const sol = await sdk.registry.resolveToken('SOL');
console.log(sol?.mint);     // 'So11111111111111111111111111111111'
console.log(sol?.decimals); // 9

Token Lists

sdk.lists.getCategories(): CategoryInfo[]
sdk.lists.browseTokens(category: string, opts?: {
  interval?: string;
  limit?: number;
}): Promise<TokenListEntry[]>

Browse curated token lists (trending, new, top volume, etc.) from the Jupiter Token API.

Swap

sdk.swap.getQuote(
  inputSymbol: string,
  outputSymbol: string,
  amount: number,
  opts?: { slippageBps?: number; router?: string },
): Promise<SwapQuote>

sdk.swap.executeSwap(
  inputSymbol: string,
  outputSymbol: string,
  amount: number,
  walletName: string,
  opts?: { slippageBps?: number; skipPreflight?: boolean; router?: string },
): Promise<SwapResult>

Uses a pluggable router system. The default best router queries Jupiter and DFlow in parallel and picks the best quote. Force a specific router with opts.router.

// Quote only
const quote = await sdk.swap.getQuote('SOL', 'USDC', 1.0);
console.log(`${quote.inputUiAmount} ${quote.inputSymbol}${quote.outputUiAmount} ${quote.outputSymbol}`);

// Execute
const result = await sdk.swap.executeSwap('SOL', 'USDC', 1.0, 'main');
console.log(result.explorerUrl);

Stake

sdk.stake.getStakeAccounts(walletAddress: string): Promise<StakeAccountInfo[]>

sdk.stake.createAndDelegateStake(
  walletName: string,
  amountSol: number,
  validatorVote?: string,        // defaults to Solana Compass
): Promise<CreateStakeResult>

sdk.stake.withdrawStake(
  walletName: string,
  stakeAccountAddress: string,
  amountSol?: number,            // partial withdraw
  force?: boolean,               // deactivate if still active
): Promise<WithdrawStakeResult>

sdk.stake.claimMev(
  walletName: string,
  walletAddress: string,
  stakeAccountAddress?: string,  // specific account, or all
  withdrawOnly?: boolean,        // withdraw instead of compound
): Promise<ClaimMevResult[]>

Native Solana staking. Creates stake account, funds it, and delegates — all in a single transaction.

Lend

sdk.lend.getRates(tokens?: string[], protocol?: string): Promise<RatesResult>
sdk.lend.getPositions(walletAddress: string, protocol?: string): Promise<LendingPosition[]>
sdk.lend.deposit(walletName: string, token: string, amount: number, protocol?: string): Promise<LendWriteResult>
sdk.lend.withdraw(walletName: string, token: string, amount: number, protocol?: string): Promise<LendWriteResult>
sdk.lend.borrow(walletName: string, token: string, amount: number, collateral: string, protocol?: string): Promise<LendWriteResult>
sdk.lend.repay(walletName: string, token: string, amount: number, protocol?: string): Promise<LendWriteResult>
sdk.lend.registerProvider(provider: LendProvider): void

Multi-protocol lending across Kamino, MarginFi, Drift, Jupiter Lend, and Loopscale. Reads query all providers in parallel. Writes target a specific protocol (or the config default).

interface LendingRate {
  protocol: string;
  token: string;
  mint: string;
  depositApy: number;
  borrowApy: number;
  totalDeposited: number;
  totalBorrowed: number;
  utilizationPct: number;
}

interface LendingPosition {
  protocol: string;
  token: string;
  mint: string;
  type: 'deposit' | 'borrow';
  amount: number;
  valueUsd: number;
  apy: number;
  healthFactor?: number;
}

Earn

sdk.earn.getVaults(tokens?: string[], protocol?: string, sort?: 'apy' | 'tvl'): Promise<VaultsResult>
sdk.earn.getPositions(walletAddress: string, protocol?: string): Promise<EarnPosition[]>
sdk.earn.deposit(walletName: string, token: string, amount: number, protocol?: string, vaultId?: string): Promise<EarnWriteResult>
sdk.earn.withdraw(walletName: string, token: string, amount: number, protocol?: string): Promise<EarnWriteResult>
sdk.earn.registerProvider(provider: EarnProvider): void

Yield vaults across Kamino and Loopscale. Same multi-provider pattern as lend.

Orders (DCA & Limit)

sdk.order.createDca(
  totalAmount: number, inputSymbol: string, outputSymbol: string,
  walletName: string, opts: { interval: string; count: number },
): Promise<DcaCreateResult>
sdk.order.listDca(walletAddress: string, opts?: { status?: string }): Promise<DcaOrder[]>
sdk.order.cancelDca(orderKey: string, walletName: string): Promise<string>

sdk.order.createLimit(
  inputAmount: number, inputSymbol: string, outputSymbol: string,
  walletName: string, opts: { targetPrice: number; slippageBps?: number; expiredAt?: number },
): Promise<LimitCreateResult>
sdk.order.listLimit(walletAddress: string, opts?: { status?: string }): Promise<LimitOrder[]>
sdk.order.cancelLimit(orderKey: string, walletName: string): Promise<string>

sdk.order.getOpenOrders(walletAddress: string): Promise<OpenOrderPosition[]>

DCA and limit orders via Jupiter's Recurring and Trigger APIs.

Predict (Prediction Markets)

sdk.predict.listEvents(opts?): Promise<PredictionEvent[]>
sdk.predict.searchEvents(query: string, limit?: number): Promise<PredictionEvent[]>
sdk.predict.getEvent(eventId: string): Promise<PredictionEvent>
sdk.predict.getMarket(marketId: string): Promise<PredictionMarket>
sdk.predict.getOrderbook(marketId: string): Promise<PredictionOrderbook | null>
sdk.predict.buy(walletName: string, marketId: string, isYes: boolean, amountUsd: number, maxPrice?: number): Promise<PredictionOrderResult>
sdk.predict.sell(walletName: string, positionPubkey: string, minPrice?: number): Promise<PredictionCloseResult>
sdk.predict.claim(walletName: string, positionPubkey: string): Promise<PredictionClaimResult>
sdk.predict.getPositions(walletAddress: string): Promise<PredictionPosition[]>
sdk.predict.getHistory(walletAddress: string, limit?: number): Promise<PredictionHistoryEntry[]>
sdk.predict.registerProvider(provider: PredictProvider): void

Portfolio

sdk.portfolio.get(wallets: { name: string; address: string }[]): Promise<PortfolioReport>
sdk.portfolio.takeSnapshot(wallets: { name: string; address: string }[], label?: string): Promise<SnapshotResult>
sdk.portfolio.autoSnapshot(report: PortfolioReport): Promise<boolean>
sdk.portfolio.compareToSnapshot(wallets: { name: string; address: string }[], snapshotId?: number): Promise<CompareResult>
sdk.portfolio.getPnl(wallets: { name: string; address: string }[], sinceId?: number): Promise<CompareResult>

Aggregates positions across all services (tokens, staking, lending, earn, orders, prediction markets) into a unified view. Snapshots and P&L require the optional CacheProvider snapshot methods.

interface PortfolioReport {
  wallets: { name: string; address: string }[];
  positions: PortfolioPosition[];
  allocation: AllocationEntry[];
  totalValueUsd: number;
  claimableMev: number;
  lastSnapshot?: { id: number; label?: string; ago: string };
}

X402 (Pay-Per-Request)

sdk.x402.fetch(url: string, opts?: X402FetchOptions): Promise<X402FetchResult>

HTTP client for the x402 payment protocol. Automatically detects 402 Payment Required responses, pays via USDC transfer, and retries with proof of payment.

Onramp

sdk.onramp.getUrl(params: {
  walletAddress: string;
  amount?: number;
  currency?: string;
  provider?: string;
}): string
sdk.onramp.listProviders(): string[]

Generates fiat onramp URLs for purchasing SOL.

Transaction

sdk.tx.buildAndSendTransaction(
  instructions: Instruction[],
  payer: TransactionSigner,
  opts?: BuildAndSendOpts,
): Promise<SendResult>

sdk.tx.sendEncodedTransaction(
  encodedTx: string,
  opts?: SendEncodedOpts,
): Promise<SendResult>

Handles blockhash fetching, signing, encoding, sending, confirmation polling, and retry with error classification. All write services use this internally.

interface SendResult {
  signature: string;
  status: string;
  attempts: number;
  elapsed_ms: number;
  explorerUrl: string;
}

Also exported as standalone utilities:

import { classifyError, ErrorClass, injectSigners } from '@solana-compass/sdk';

classifyError(err); // ErrorClass.RETRYABLE_TRANSIENT | TERMINAL_PROGRAM | etc.
injectSigners(instructions, [signer]); // attach signers to instruction accounts

Utilities

Pure functions re-exported from the SDK:

import {
  // Solana helpers
  isValidAddress, lamportsToSol, solToLamports, uiToTokenAmount,
  tokenAmountToUi, shortenAddress, explorerUrl, SOL_MINT, SOL_DECIMALS,

  // Token list
  WELL_KNOWN_TOKENS, getWellKnownBySymbol, getWellKnownByMint,

  // Formatting
  fmtPrice, timed,

  // Retry / rate limiting
  withRetry, RateLimiter, isRetryableHttpError,

  // Constants
  SOLANA_COMPASS_VOTE,
  PROTOCOL_NAMES,         // lend protocol names
  EARN_PROTOCOL_NAMES,    // earn protocol names
  PREDICT_CATEGORIES,     // prediction market categories
  parseInterval,          // 'day' → seconds

  // Compat layers (for v1 SDK bridging)
  getKaminoRpc, kAddress, kSigner, getCurrentSlot,
  getMarginFiV1Connection, MarginFiDummyWallet,
  getDriftV1Connection, DriftDummyWallet,
} from '@solana-compass/sdk';

API Proxy Support

For browser apps, you'll want to keep API keys server-side. The SDK supports URL overrides for all external APIs:

const sdk = createSolSdk({
  rpc,
  signer: { ... },
  config: new InMemoryConfig({
    'api.jupiterBaseUrl': 'https://mysite.com/api/jupiter',
    'api.dflowBaseUrl': 'https://mysite.com/api/dflow',
    'api.loopscaleBaseUrl': 'https://mysite.com/api/loopscale',
  }),
});

When a base URL is set, all API calls for that service go through your proxy. Your proxy adds the API key server-side so it never appears in client code.

Custom Providers

Register your own lend, earn, or predict providers:

import type { LendProvider, EarnProvider, PredictProvider } from '@solana-compass/sdk';

class MyLendProvider implements LendProvider {
  name = 'my-protocol';
  capabilities = { deposit: true, withdraw: true, borrow: false, repay: false };
  async getRates(tokens?) { ... }
  async getPositions(walletAddress) { ... }
  async deposit(walletName, token, amount) { ... }
  async withdraw(walletName, token, amount) { ... }
}

sdk.lend.registerProvider(new MyLendProvider());

License

MIT

FAQs

Package last updated on 09 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts