Skip to main content

Overview

Kamino Earn vaults accept token deposits and automatically allocate them across lending markets. Depositors receive shares representing their portion of the vault, and yield accrues without any manual compounding. This guide walks through integrating Kamino vaults into a Next.js app with Dynamic embedded wallets. For the final code, see the GitHub repository.

How it works

When a user deposits tokens, the Kamino SDK builds a set of instructions that create a share account and mint vault shares proportional to the deposit. Yield accrues as the vault allocates capital to borrowers. When withdrawing, shares are burned and the user receives back the underlying tokens plus earned interest. The SDK returns instruction groups that must each be sent as a separate transaction, confirmed in order:
  • Deposit: depositIxsstakeInFarmIfNeededIxsstakeInFlcFarmIfNeededIxs
  • Withdraw: unstakeFromFarmIfNeededIxswithdrawIxspostWithdrawIxs
Combining groups into one transaction exceeds the 1232-byte Solana limit. This guide shows two approaches for signing and sending these transactions:

Setup

Project setup

Follow the React Quickstart using the Custom setup path with Solana. Scaffold a Next.js app with create-next-app.
In the Dynamic dashboard, enable Solana under Chains & Networks, enable Embedded wallets under Wallets, and add your app’s origin under Security → Allowed Origins.

Install dependencies

@kamino-finance/klend-sdk uses WASM internally. Always import it with ssr: false in Next.js to avoid server-side rendering errors.

Environment variables

.env.local
Your environment ID is in the Dynamic dashboard under Developer Settings → SDK & API Keys. The Solana RPC URL is sourced automatically from your Dynamic dashboard network configuration. You can optionally override it with NEXT_PUBLIC_SOLANA_RPC_URL if you prefer a specific endpoint.

Initialize Dynamic

Create src/lib/dynamic.ts:
src/lib/dynamic.ts

Set up wallet context

Create src/lib/providers.tsx. DynamicProvider owns reactive auth state; InnerProviders reads it via useUser() and useGetWalletAccounts() from @dynamic-labs-sdk/react-hooks:
src/lib/providers.tsx

Fetching vault data

Create src/lib/kamino.ts to load vaults and metrics from the Kamino REST API:
src/lib/kamino.ts

Deposit and withdraw

Create src/lib/useVaultOperations.ts. The shared prepareTransaction helper builds one unsigned VersionedTransaction per instruction group. The signing strategy differs between the two approaches.

Transaction preparation (shared)

Both approaches use the same prepareTransaction helper:

With SVM Gas Sponsorship (gasless)

Enable SVM Gas Sponsorship in the Dynamic dashboard under Settings → Embedded Wallets. This is available exclusively for V3 MPC embedded wallets.
Dynamic sponsors the SOL transaction fee so users don’t need a SOL balance. signAndSendSponsoredTransaction handles signing, fee-payer replacement, and broadcasting in one call. Transactions are sent one at a time since each is independently sponsored.
src/lib/useVaultOperations.ts

Without gas sponsorship

When SVM Gas Sponsorship is not enabled, users pay Solana transaction fees from their wallet SOL balance. All transactions are built in parallel and signed in a single MPC round with signAllTransactions, then sent and confirmed sequentially. This minimizes round-trips to the MPC network.
src/lib/useVaultOperations.ts

Wiring it together

The main page loads vaults with useQuery and disables SSR on the VaultsInterface component to avoid WASM errors:
src/app/page.tsx
Inside VaultsInterface, vaults are paginated (6 per page) and metrics are fetched only for the current page. After an action completes, all transactions are already confirmed on-chain. Positions are refetched immediately and vault metrics are invalidated. Multiple retries at increasing intervals handle Kamino’s API indexing lag (can be up to ~15s):
handleDeposit and handleWithdraw return Promise<boolean> so VaultCard can clear its input field only on success:

Run the app

Add http://localhost:3000 to your allowed origins in the Dynamic dashboard under Developer Settings → CORS Origins. If you’re using SVM Gas Sponsorship, also enable it in Settings → Embedded Wallets → SVM Gas Sponsorship.

Full source code

GitHub repository →

Additional resources

Last modified on June 24, 2026