Devnet and mainnet
On devnet every venue is a simulated reserve inside the Aqueduct program that earns the real venue's live mainnet APY. This page explains what is real, what is simulated and what changes on mainnet.
On this page
What is real and what is simulated#
Kamino, Jupiter Lend, Perena and the other venues are not deployed on Solana devnet, and Aave, Morpho and Maple live on other chains. So on devnet each strategy points at a simulated reserve: an account inside the Aqueduct program that holds real devnet tokens and accrues interest at a stored APY. Everything else is the real system.
| Part | On devnet |
|---|---|
| Vault program, shares, fees, caps, pause | Real: the same program that would run on mainnet, with the same checks. |
| Keeper, optimizer, sentinel, decision log | Real: the production keeper, signing real devnet transactions every 10 minutes. |
| Venue rates, TVL and utilisation | Real: read from the venues' mainnet APIs and DefiLlama every cycle. |
| Venue positions | Simulated reserves inside the program, earning the venue's live APY. |
| USDC | A test mint whose authority is the program. Anyone can mint it from the faucet. It has no value. |
| Cross-chain transfers | Simulated: a 1 bp fee is burned on each cross-chain move, and the funds stay on Solana. |
Simulated reserves#
A reserve tracks an exchange rate of USDC per reserve share, starting at 1.0. A strategy's position is a number of reserve shares, so its value is shares × exchange rate. Interest accrues continuously at the reserve's APY:
Each accrual mints the interest as new test USDC into the reserve's token account, using the program's mint authority. So a reserve always holds exactly the tokens its depositors are owed; it never pays out of thin air. Accrual happens inside every deposit, withdrawal, allocation and fee sweep, and anyone can trigger it for one reserve with the permissionless accrue_reserve instruction.
Mirroring live mainnet rates#
At the end of every cycle, the keeper writes each venue's current mainnet APY into its reserve with set_reserve_apy. The program first accrues the reserve at the old rate, then switches, so no interest is lost or invented at the boundary. The rate is capped at 100% on-chain. As a result, devnet vaults earn what the real venues pay, minute by minute.
| Venue | Rate source |
|---|---|
| Kamino Lend (main market, OnRe market) | Kamino reserve metrics API, then DefiLlama |
| Jupiter Lend | Jupiter Earn API (supply rate plus rewards), then DefiLlama |
| Kamino Earn Steakhouse | Kamino vault metrics API |
| Aave v3 on Base, Morpho, Maple | DefiLlama pool, found again by search if its id changes |
| Solstice eUSX | Exponent's SY oracle for the underlying yield |
| Exponent PT | Deepest stablecoin PT market with at least 14 days to maturity |
| Perena USD* | No live feed: the registry's fallback APY |
When every source for a venue fails, the keeper uses the registry's fallback APY and labels the quote fallback. You can see every venue's current rate and source at the keeper's /rates endpoint.
The next keeper adds a live Perena feed (7-day NAV-derived APY, TVL and collateralisation), validates every upstream response against a schema, and serves the last good quote for up to 6 hours during an outage before it falls back.
Cross-chain strategies via CCTP#
Three venues live on other chains: Aave v3 on Base and Morpho on Ethereum in Bedrock, and Maple on Ethereum in Rapids. On mainnet, USDC reaches them through Circle's Cross-Chain Transfer Protocol (CCTP), which burns USDC on Solana and mints it natively on the destination chain, with no wrapped token and no liquidity pool in between.
On devnet the program simulates this:
- The funds stay in a simulated reserve on Solana.
- Each keeper
allocateordeallocateon a cross-chain strategy burns 1 bp of the amount from the vault, standing in for CCTP's fast-transfer fee. - The keeper prices each cross-chain leg at 5 USDC plus 2 bp before it decides to move, so it rarely does.
The keeper also caps exposure to each non-Solana chain at 50% of the vault, and all cross-chain venues together at 50%. See Caps.
What changes on mainnet#
A mainnet deployment does not exist. This is the intended design, and it would need its own audit first.
- The simulated strategy kind is replaced by adapters that call each venue's program directly (for example Kamino's and Jupiter Lend's), so positions are real venue deposits.
- Cross-chain strategies move real USDC with CCTP v2, and their positions take time to enter and exit.
- The asset is real USDC, so there is no faucet and the program has no mint authority.
- Venue risks become real: exploits, bad debt, depegs and illiquidity can reduce what the vault holds. See Risks.
Devnet addresses#
| Account | Devnet address |
|---|---|
| Program | BweEZpNS6bhcwjxgEgScgZSWRSkKC9tqmC2PpfZdFbhm |
| Test USDC mint | 3VKJmjmaqvBphZz3zAjUp3kpTQ2dvhqywpSWdt4K1BeL |
| Bedrock vault | AvFAsSxZC55qX65t4Ubzv8yUQmE69YMvzUN9de3mcggt |
| Rapids vault | 9Wu98SsAKvo2JJS8k581zfB31j4NgzZMxFBxSNvhCbXC |
| Config | DyhVHoYVc5W295ukYsyS5bN9iT8E5cDDkj1qfvVTkqmJ |
| Admin | DDVtiUetxxks7LfFUxngLj1jD5huPxmJ9urNCVUKQpvq |
| Keeper | AdnZLxWGSWRVQvfABaF4dBQjzZy25euMYEsDFXYKzaZy |
| Treasury | Hs4RLeoYBs8YtUirjjE8BYaqG596koUWyYMxuJ2GgfEd |
To mint test USDC from a script rather than the app, use the SDK's faucet builder:
import { AqueductClient, DEPLOYMENTS } from "@aqueduct/sdk";
import { Connection, Keypair, sendAndConfirmTransaction } from "@solana/web3.js";
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const client = new AqueductClient(connection, DEPLOYMENTS.devnet!);
const payer = Keypair.generate(); // fund it with devnet SOL first
// up to 10,000 test USDC per call
const tx = await client.buildFaucetTx(payer.publicKey, 1_000);
await sendAndConfirmTransaction(connection, tx, [payer]);docs/ARCHITECTURE.md, programs/aqueduct/src/state.rs, programs/aqueduct/src/instructions/keeper.rs and apps/keeper/src/rates/sources.ts. If the code and this page disagree, the code wins.