Skip to content
Open app

Program reference

The Aqueduct Anchor program on Solana: its accounts and PDAs, all 23 instructions by who may call them, events, errors and constants, generated from the IDL.

On this page

Overview#

Aqueduct is a single Anchor 1.0 program. Its program ID is BweEZpNS6bhcwjxgEgScgZSWRSkKC9tqmC2PpfZdFbhm on devnet, deployed with the upgradeable loader; the upgrade authority is the admin key. The tables on this page are generated from the IDL in packages/sdk/src/idl/aqueduct.json when the docs are built, so they match the deployed interface.

Count
Instructions23
Account types4
Events9
Custom errors19

Accounts#

All state lives in four account types. Amounts are in base units (6 decimals); rates are fixed point scaled by 1018.

Config#

  • admin
    Type
    pubkey
    Notes
    Creates vaults, reserves and strategies, sets caps and fees, rotates keeper and treasury, pauses. Changed only through `propose_admin` + `accept_admin`.
  • keeper
    Type
    pubkey
    Notes
    Can rebalance between idle and whitelisted strategies and update simulated APYs. Cannot withdraw.
  • treasury
    Type
    pubkey
    Notes
    Receives management- and performance-fee shares.
  • paused
    Type
    bool
    Notes
    Blocks deposits and allocations. Never blocks withdrawals, deallocations or fee collection.
  • bump
    Type
    u8
Global settings, one per program.

Vault#

  • config
    Type
    pubkey
  • asset_mint
    Type
    pubkey
    Notes
    The deposited asset (USDC on mainnet, the program's test mint on devnet).
  • share_mint
    Type
    pubkey
    Notes
    Vault share mint, PDA `["shares", vault]`, same decimals as the asset.
  • idle
    Type
    pubkey
    Notes
    Uninvested assets, PDA `["idle", vault]`. Deposits land here and withdrawals pay from here.
  • tier
    Type
    u8
    Notes
    0 Bedrock (conservative), 1 Rapids (higher yield). Only used as a seed and label.
  • fee_bps
    Type
    u16
    Notes
    Annual management fee on assets.
  • deposit_cap
    Type
    u64
    Notes
    Max total assets in base units, 0 = unlimited.
  • last_fee_ts
    Type
    i64
    Notes
    Last time the management fee was accrued (advanced only once a whole share is due).
  • pending_fee_shares
    Type
    u64
    Notes
    Accrued management + performance fees, in shares, not yet minted to the treasury.
  • strategy_count
    Type
    u8
  • strategies
    Type
    [pubkey; 8]
    Notes
    Strategy PDAs in index order; the first `strategy_count` are live. Strategies can be disabled but never removed.
  • lifetime_deposits
    Type
    u64
    Notes
    Running totals for dashboards only; saturating, never used in share math.
  • lifetime_withdrawals
    Type
    u64
  • bump
    Type
    u8
  • share_bump
    Type
    u8
  • idle_bump
    Type
    u8
  • perf_fee_bps
    Type
    u16
    Notes
    Share of yield above the high-water mark taken as a performance fee.
  • high_water_mark
    Type
    u128
    Notes
    Highest share price (assets per share, RATE_SCALE) on which a performance fee has been charged. 0 = not yet set; the first accrual starts it at the current price.
One per tier. Holds no tokens itself: USDC sits in the idle account and in strategies.

Strategy#

  • vault
    Type
    pubkey
  • index
    Type
    u8
  • kind
    Type
    StrategyKind
  • chain
    Type
    u8
    Notes
    0 solana, 1 ethereum, 2 base, 3 arbitrum
  • risk_score
    Type
    u8
    Notes
    1 (safest) .. 10
  • max_alloc_bps
    Type
    u16
    Notes
    Hard cap on this strategy's value as a share of vault total assets, checked on `allocate`.
  • enabled
    Type
    bool
    Notes
    Disabled strategies take no new allocations; their positions can still be withdrawn.
  • reserve
    Type
    pubkey
    Notes
    The simulated reserve holding this strategy's funds.
  • position_shares
    Type
    u64
    Notes
    Reserve shares held by this strategy.
  • name
    Type
    [u8; 32]
  • venue
    Type
    [u8; 32]
  • bump
    Type
    u8
A whitelisted venue of a vault with its hard cap and risk score.

Reserve#

  • asset_mint
    Type
    pubkey
  • token_account
    Type
    pubkey
    Notes
    Token account holding the reserve's assets, PDA `["reserve_vault", reserve]`, owned by the reserve.
  • name_seed
    Type
    [u8; 32]
    Notes
    The `id` seed, zero-padded, and its length.
  • name_len
    Type
    u8
  • apy_bps
    Type
    u32
    Notes
    Simple annual rate, set by the keeper to mirror the real venue (max 100%).
  • exchange_rate
    Type
    u128
    Notes
    Assets per reserve share, scaled by RATE_SCALE.
  • total_shares
    Type
    u64
  • last_update
    Type
    i64
  • bump
    Type
    u8
  • token_bump
    Type
    u8
Devnet simulated venue. Real test tokens, interest accrues at apy_bps.

PDAs#

Every account is a program-derived address. The SDK exports one helper per seed (configPda, vaultPda, …).

  • Config
    Seeds
    "config"
    Purpose
    One per program: admin, keeper, treasury, paused flag.
    Example (devnet)
    DyhV…kqmJ
  • Vault
    Seeds
    "vault", asset_mint, [tier]
    Purpose
    One per asset and tier (Bedrock = 0, Rapids = 1).
    Example (devnet)
    AvFA…cggt
  • Share mint
    Seeds
    "shares", vault
    Purpose
    SPL mint of the vault's shares; the vault PDA is its authority.
    Example (devnet)
    HE2n…yMzW
  • Idle account
    Seeds
    "idle", vault
    Purpose
    Token account holding the vault's undeployed USDC.
    Example (devnet)
    8q8Z…9eiL
  • Strategy
    Seeds
    "strategy", vault, [index]
    Purpose
    One per whitelisted venue, index 0 to 7.
    Example (devnet)
    Ecc4…6Wyx
  • Reserve
    Seeds
    "reserve", asset_mint, id
    Purpose
    Devnet simulated venue; `id` is the registry strategy id.
    Example (devnet)
    6voY…Ujc1
  • Reserve vault
    Seeds
    "reserve_vault", reserve
    Purpose
    Token account holding a reserve's USDC.
    Example (devnet)
    FKWY…ypKH
  • Mint authority
    Seeds
    "mint_auth"
    Purpose
    Mint authority of the test USDC mint (faucet and interest backing).
    Example (devnet)
    79h1…8eB3
Examples use the Bedrock vault and its strategy #0 (Kamino Lend). Derived at build time with the SDK's PDA helpers (packages/sdk/src/pda.ts).

Instructions#

Grouped by the signer the program requires. Amounts are base units; index is a strategy's position in its vault.

User instructions#

  • deposit
    amount: u64

    Accrues every reserve and the fees, then mints shares at the current share price. Blocked while paused or when the deposit would exceed the vault cap.

  • deposit_checked
    amount: u64min_shares_out: u64

  • faucet
    amount: u64

    Devnet only. Mints test USDC to the caller, at most 10,000 per call.

  • withdraw
    shares: u64

    Burns shares for USDC at the current share price. If idle is short, pulls the rest from strategies in the same transaction, Solana venues first. Never blocked by pause.

  • withdraw_checked
    shares: u64min_assets_out: u64

Users: 5 instructions, from the program IDL.

Keeper instructions#

  • allocate
    index: u8amount: u64

    Moves idle USDC into a strategy. Rejected if the position would exceed the strategy's cap against total assets, if the strategy is disabled, or while paused. Cross-chain strategies burn a simulated 1 bp bridge fee.

  • deallocate
    index: u8amount: u64

    Moves USDC from a strategy back to idle. `u64::MAX` exits the whole position. Allowed while paused so the keeper can always de-risk.

  • set_reserve_apy
    apy_bps: u32

    Devnet only. Accrues a simulated reserve at its old rate, then sets the new APY (at most 10,000 bps).

Keeper: 3 instructions, from the program IDL.

Permissionless instructions#

  • accept_admin

  • accrue_reserve

    Devnet only. Advances a simulated reserve's exchange rate and mints the interest into it so it stays fully backed.

  • collect_fees

    Accrues fees on fresh totals and mints the vault's pending fee shares to the treasury's share account. Anyone can call it; the keeper does so every hour.

Anyone (permissionless): 3 instructions, from the program IDL.

Neither can move funds anywhere new: fee shares only go to the treasury in the config, and accrued interest only into the reserve.

Admin instructions#

  • add_strategy
    index: u8name: stringvenue: stringchain: u8risk_score: u8max_alloc_bps: u16

    Whitelists a reserve as the vault's next strategy (at most 8). Risk score 1 to 10, cap at most 10,000 bps.

  • cancel_admin_proposal

  • create_reserve
    id: stringapy_bps: u32

    Devnet only. Creates a simulated venue and its token account.

  • create_vault
    tier: u8fee_bps: u16perf_fee_bps: u16deposit_cap: u64

    Creates a vault with its share mint and idle account. Management fee at most 200 bps a year, performance fee at most 3,000 bps.

  • initialize_config
    keeper: pubkeytreasury: pubkey

    Creates the program config. The signer becomes the admin.

  • migrate_vault

    Grows a vault created before the performance fee existed to the current layout. Idempotent.

  • propose_admin
    new_admin: pubkey

  • set_keeper
    keeper: pubkey

    Rotates the keeper key.

  • set_paused
    paused: bool

    Pauses or unpauses deposits and allocations. Withdrawals, deallocations and fee sweeps keep working.

  • set_treasury
    treasury: pubkey

    Changes the account that receives fee shares.

  • update_strategy
    max_alloc_bps: u16risk_score: u8enabled: bool

    Changes a strategy's cap and risk score, or disables it. A disabled strategy receives nothing new but can still be drained.

  • update_vault
    deposit_cap: u64fee_bps: u16perf_fee_bps: u16

    Changes the deposit cap and fees (same limits). Management fee accrued so far is settled at the old rate first.

Admin: 12 instructions, from the program IDL.

Strategy accounts#

Every instruction that values a vault (deposit, withdraw, allocate, deallocate, collect_fees) takes the vault's strategies as remaining accounts, as triplets in vault order:

Text
[strategy 0, reserve 0, reserve token 0, strategy 1, reserve 1, reserve token 1, …]   all writable

The program requires exactly 3 × strategy_count accounts and checks that each strategy is the vault's, in the right slot, owned by the program and matched to its reserve and token account. Anything else fails with StrategyAccountsMismatch. Without this, a caller could omit a strategy to make the vault look smaller and buy shares cheaply. The SDK's strategyMetas(vault) builds the list.

The fixed accounts of a deposit or withdrawal, in order:

#AccountFlags
0usersigner, writable
1configPDA
2vaultwritable
3asset_mintwritable
4share_mintwritable
5idlewritable
6user_assetwritable, PDA
7user_shareswritable, PDA
8mint_authPDA
9token_programprogram
10associated_token_programprogram
11system_programprogram

Events#

The program emits Anchor events, readable from transaction logs:

EventFields
AdminProposedadmin: pubkey, new_admin: pubkey
ApyUpdatedreserve: pubkey, apy_bps: u32
ConfigUpdatedadmin: pubkey, keeper: pubkey, treasury: pubkey, paused: bool
Depositedvault: pubkey, user: pubkey, assets: u64, shares: u64, total_assets: u64
FeesCollectedvault: pubkey, treasury_shares: pubkey, shares: u64
Rebalancedvault: pubkey, strategy_index: u8, allocate: bool, amount: u64, bridge_fee: u64, strategy_value: u64, total_assets: u64
StrategyConfiguredvault: pubkey, strategy: pubkey, index: u8, reserve: pubkey, chain: u8, risk_score: u8, max_alloc_bps: u16, enabled: bool
VaultConfiguredvault: pubkey, asset_mint: pubkey, tier: u8, fee_bps: u16, perf_fee_bps: u16, deposit_cap: u64
Withdrawnvault: pubkey, user: pubkey, assets: u64, shares: u64, total_assets: u64

Errors#

  • 6000
    Name
    Paused
    Message
    Protocol is paused
  • 6001
    Name
    Unauthorized
    Message
    Signer is not authorised for this action
  • 6002
    Name
    ZeroAmount
    Message
    Amount must be greater than zero
  • 6003
    Name
    DepositCapExceeded
    Message
    Deposit would exceed the vault cap
  • 6004
    Name
    StrategyCapExceeded
    Message
    Allocation would exceed the strategy's risk cap
  • 6005
    Name
    TooManyStrategies
    Message
    Vault already has the maximum number of strategies
  • 6006
    Name
    BadStrategyIndex
    Message
    Strategy index is out of range, or not the next free slot when adding
  • 6007
    Name
    StrategyAccountsMismatch
    Message
    Strategy accounts passed do not match the vault
  • 6008
    Name
    StrategyDisabled
    Message
    Strategy is disabled
  • 6009
    Name
    InsufficientIdle
    Message
    Not enough idle liquidity
  • 6010
    Name
    InsufficientLiquidity
    Message
    Not enough liquidity to cover withdrawal
  • 6011
    Name
    InsufficientPosition
    Message
    Strategy position is smaller than requested amount
  • 6012
    Name
    NameTooLong
    Message
    Name is too long (max 32 bytes)
  • 6013
    Name
    InvalidParameter
    Message
    Parameter out of range
  • 6014
    Name
    FaucetLimit
    Message
    Faucet amount exceeds per-call maximum
  • 6015
    Name
    MathOverflow
    Message
    Math overflow
  • 6016
    Name
    ZeroAssetsOut
    Message
    Withdrawal would return zero assets
  • 6017
    Name
    SlippageExceeded
    Message
    Output is below the caller's minimum (slippage bound)
  • 6018
    Name
    AllocationTooSmall
    Message
    Allocation is too small to buy any reserve shares after the bridge fee
Custom program errors start at 6000. Hex codes in wallet errors: 0x1770 = 6000.
Reading an error in a wallet

Wallets show program errors as hex, such as custom program error: 0x1773. Convert to decimal (0x1773 = 6003) and look it up above: 6003 is DepositCapExceeded. See SDK errors for doing this in code.

Constants#

ConstantValueMeaning
MAX_STRATEGIES8Strategies per vault
VIRTUAL_OFFSET1,000Virtual shares and assets in the share math, base units
RATE_SCALE1018Fixed-point scale of exchange rates and the high-water mark
BRIDGE_FEE_BPS1Simulated CCTP fee per cross-chain move, bps
FAUCET_MAX10,000 USDCPer faucet call
MAX_FEE_BPS200Management fee limit, bps per year
MAX_PERF_FEE_BPS3,000Performance fee limit, bps of yield
MAX_APY_BPS10,000Simulated reserve APY limit
NAME_LEN32Bytes in a strategy or venue name and a reserve id
From programs/aqueduct/src/constants.rs.

Build and test#

From a checkout of the repository, with the Solana and Anchor CLIs installed (Rust 1.89, Anchor 1.0):

Terminal
anchor build
pnpm --filter @aqueduct/sdk sync-idl     # copy the new IDL and types into the SDK

# SDK integration tests against a local validator with the program loaded
solana-test-validator --reset --bpf-program BweEZpNS6bhcwjxgEgScgZSWRSkKC9tqmC2PpfZdFbhm target/deploy/aqueduct.so
RPC_URL=http://127.0.0.1:8899 pnpm --filter @aqueduct/sdk test

Pending upgrade#

Needs a program upgrade, not deployed

A new program version is built and tested but not deployed. It adds deposit_checked and withdraw_checked, which take a minimum number of shares or USDC out and fail below it, and a two-step admin handover (propose_admin, accept_admin, cancel_admin_proposal). Admin actions and fee sweeps also emit events. The existing instructions keep their signatures. None of this is callable on devnet until the program is upgraded.

Written against programs/aqueduct/src/lib.rs, programs/aqueduct/src/state.rs, programs/aqueduct/src/constants.rs and packages/sdk/src/idl/aqueduct.json. If the code and this page disagree, the code wins.