Venues
A venue is anywhere idle USDC can earn yield: a mock lending program on localnet, or Kamino Lend / Marginfi on mainnet. Every venue is hidden behind one interface so the engine — planner, risk gate, executor, custody — never depends on a specific protocol. This doc covers the adapter contract and the two real integrations.
The position model: receipt shares
Every venue, regardless of protocol, is modeled as receipt shares: a quantity the adapter can value (shares → USDC) and invert (USDC → shares). Two physical shapes implement this:
- Token-receipt venues (Kamino cTokens, the mock venue) — depositing mints SPL receipt tokens to the owner's account; the position is an SPL balance at the strategy's Swig wallet PDA, bounded by the custody role's per-mint allowance.
- Account-based venues (Marginfi) — the position is shares inside a protocol account whose authority is the Swig wallet PDA. There's no receipt mint, but custody still holds: only the Swig can move the shares, and withdrawals can only land back in the Swig.
Either way, the engine sees the same thing: a venue it can deposit into, value, and unwind, under custody it doesn't have to special-case.
The adapter contract
interface VenueAdapter {
id: string;
programIds(): PublicKey[]; // for custody role scoping
list(): Promise<VenueSnapshot[]>; // venues + live APY/util/TVL
receiptBalance(venue, owner): Promise<bigint>; // the position (owner may be a PDA)
positionValue(venue, receipts): bigint; // shares → USDC
receiptsForAmount(venue, amount): bigint; // USDC → shares (round up)
refreshIxs(venue): Promise<Ix[]>; // pre-deposit/withdraw refresh
depositIxs(venue, user, amount): Promise<Ix[]>; // USDC → position
withdrawIxs(venue, user, receiptAmount): Promise<Ix[]>; // position → USDC
healthCheck?(venue, owner): Promise<string | null>; // null = healthy
}A VenueSnapshot carries the live apyBps, costBps, the receipt→deposit
index, and the risk inputs utilizationBps and tvlUsd (consumed by the
scorer). programIds() feeds the custody role scope —
the union across all adapters is exactly what the engine's Swig role is permitted.
The registry
A VenueRegistry merges N adapters into one view: it concatenates their venue
lists, routes per-venue calls to the owning adapter, exposes the union of program
IDs for custody, and runs all health checks. The engine talks only to the
registry. Adapters are enabled per deployment by env (OLBOS_KAMINO=1,
OLBOS_MARGINFI=1; OLBOS_MOCK=0 disables the localnet mock on mainnet).
Kamino Lend (kamino-main)
The first real venue. Solana's largest lending market.
- Flow: obligation-free cToken —
depositReserveLiquidity(USDC → kTokens) andredeemReserveCollateral(kTokens → USDC). This maps 1:1 onto the receipt model, so custody is unchanged. - SDK seam:
@kamino-finance/klend-sdkv8 is built on@solana/kit(web3.js v2) while the engine speaks web3.js v1. The adapter owns the boundary: a kit RPC,createNoopSignerfor the Swig PDA owner (the PDA "signs" via the wrapped Swig instruction, never in the SDK), and a small kit→web3 instruction conversion. - APY:
reserve.totalSupplyAPY(slot); utilization fromcalculateUtilizationRatio(); TVL from supply (USD-stable mint). - Pinning:
@kamino-finance/farms-sdkis pinned to3.2.24(a later release dropped a module klend imports). - Verified: live-mainnet deposit simulation passes (~82k CU).
Constants: program KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD, main market
7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF.
Marginfi (marginfi)
The second real venue. Adds genuine venue diversity so rebalancing is real, not theatrical — the live spread between the two (e.g. 6.3% vs 3.2%) is what the engine exists to capture.
- Flow: account-based. The position lives in a marginfi account whose
authority is the Swig wallet PDA, derived deterministically via
marginfiAccountInitializePda(seeds:"marginfi_account"+ group + authority + u16 index + u16 third-party-id). Deterministic derivation means discovery needs no stored state; the account-init instruction rides inside the wrapped Swig segment on the first deposit. - SDK:
@mrgnlabs/marginfi-client-v2(web3.js v1 native — no kit seam). The engine wallet is passed as a read-only dummy; the Swig PDA is the authority everywhere. Marginfi's SDK explicitly allows off-curve authority ATAs (they built it for their own smart-wallet product), which is exactly the PDA-owner case. - APY:
bank.computeInterestRates().lendingRateis an APR fraction → converted to APY by daily compounding. Utilization fromcomputeUtilizationRate(). - Pinning: the published SDK (6.4.2) can't decode the full production group
(mainnet has newer bank types), so the adapter pins
preloadedBankAddresses: [USDC bank]— which it wanted anyway. - Health check: the lend-only invariant — the marginfi account must never carry a liability. A borrow appearing trips the fail-safe kill. This guards the residual borrow surface noted in Security.
- Verified: live-mainnet init+deposit simulation passes (~105k CU).
Constants: program MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA, production group
4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8.
The mock venues (delphi, olympia)
Localnet-only. A custom Anchor lending program (programs/mock-venue) with an
accrue/set_apy lever for deterministic demos and tests. Deliberately named
after Greek treasuries, not real protocols — a mock must never be mistakable for
the thing it imitates. They never appear in production or user-facing material.
Adding a venue
A new venue is a new VenueAdapter. The checklist:
- Implement the interface — the protocol's deposit/withdraw, valued in the receipt-share model.
- Return real
utilizationBps/tvlUsdso the scorer can risk-adjust it. - Add its program ID to
programIds()— custody scoping picks it up automatically. - If it's account-based, ensure the position account's authority is the Swig PDA.
- Add a
healthCheckfor any invariant the engine should never violate. - Verify against live state with a sigVerify-off simulation before any real capital (borrow a whale's balance, simulate the deposit).
Custody, the planner, the gates, and the dashboard all work unchanged — that's the point of the seam.
Next: Strategies · Operations.
