Anchor 1.2.1, Solana CLI 4.3. Source: packages/pot-index/programs/pot_index/src. Tests: packages/pot-index/tests (LiteSVM, run with ./test.sh).
| Account | Seeds | Holds |
|---|---|---|
Config |
["config"] |
admin, pending admin, treasury, USDC mint, max_price_age_secs, protocol pause flag |
Asset |
["asset", mint] |
mint, decimals, Pyth feed id, enabled flag |
Pot |
["pot", index_mint] |
creator, name, symbol, legs (mint, weight_bps, vault, feed_id, decimals), cash vault, deposit cap, paused, finalized, rebalance state, created_at |
| Vaults | ATAs owned by the Pot PDA | one per leg + one USDC cash vault |
| Index mint | keypair supplied by the creator (vanity Pot...) |
mint authority = Pot PDA, 6 decimals |
| Instruction | Signer | Effect |
|---|---|---|
init_config, set_config, accept_admin |
admin | protocol settings (init_config by the upgrade authority only); admin change is two-step: set_config(new_admin) proposes, the new key accepts within ~1 hour (PENDING_ADMIN_TTL_SLOTS) |
migrate_config |
admin | one-off: grows a v1.1 Config to the current layout (see operations.md) |
register_asset, update_asset |
admin | allowlist of (mint, Pyth feed id); update_asset enables/disables an asset or repoints its feed for legs added from then on |
create_pot(name, symbol, deposit_cap_usd, slippage_bps, max_trade_bps) |
creator | creates Pot + index mint |
add_leg(weight_bps) x 2..5 |
creator | adds an asset with its vault |
finalize_pot |
creator | checks weights sum to 10,000 bps, locks the basket |
set_index_metadata(uri) |
creator | Metaplex metadata (name, symbol, uri) via CPI |
set_pot_params(paused, deposit_cap_usd) |
creator | the only two things a creator can change |
deposit(amount, min_shares_out) + remaining accounts: Pyth price updates per leg |
anyone | fee split, mint at NAV |
exit(shares, min_usdc_out) |
holder | burn, pro-rata transfer of cash + every leg |
exit_usdc_open(shares, min_usdc_out) + remaining [vault_i, pyth_update_i, user_ata_i, mint_i] |
holder | burn, cash share to the holder, legs to the holder's accounts, records the minimum USDC the sale must produce; requires exit_usdc_close later in the same transaction for the same Pot and USDC account |
exit_usdc_close |
holder | checks the holder's USDC grew by at least the minimum, transfers the 0.10% conversion fee to the treasury |
rebalance_open(leg_out, leg_in, amount_out) |
keeper | moves amount_out from the out-vault to the keeper, records expected min_in, requires a matching rebalance_close later in the same transaction (Instructions sysvar introspection) |
rebalance_close |
keeper | verifies the in-vault received at least min_in, closes the window |
- Entry fee 30 bps: 40% referrer, 40% creator, 20% protocol. No referrer: referrer share goes to the creator.
- Exit fee 50 bps, stays in the Pot; the last holder (burning the whole supply) pays none, since nobody is left for it to accrue to.
- 2 to 5 legs. Index decimals 6. Minimum deposit 1 USDC.
- Virtual shares/assets 1 token / $1 against first-depositor inflation.
- Oracle: max confidence 2%, max age
max_price_age_secscapped at 60 s (MAX_PRICE_AGE_SECS_LIMIT), update account posted within 150 slots. Pot values what it holds or gives atprice + conf, what it receives atprice - conf. - Rebalance: deadband 0.5% of NAV, cooldown 150 slots (asset→asset only; cash deployment has none), max slippage 1%, max trade 25% of NAV per transaction; only from an overweight leg (or cash) toward an underweight leg, never past target on either side.
- Exit to USDC: minimum = Σ legs at (price − conf) × (1 − slippage_bps) − 0.10% conversion fee; the open/close window reuses
Pot.rebalancewithleg_out == EXIT_LEG (254), so no account layout change.
Unauthorized, ProtocolPaused, PotPaused, PotNotFinalized, PotAlreadyFinalized, TooManyLegs, TooFewLegs, WeightsDoNotSum, InvalidWeight, DuplicateLeg, AssetDisabled, NameTooLong, InvalidParams, RemainingAccountsMismatch, VaultMismatch, BadOracleOwner, StalePrice, OracleConfidence, NonPositivePrice, MathOverflow, DepositTooSmall, DepositCapExceeded, SlippageShares, SlippageAssets, ZeroShares, SelfReferral, TokenOwnerMismatch, TokenMintMismatch, RebalanceOpen, RebalanceNotOpen, RebalanceWrongSlot, CpiNotAllowed, MissingClose, InvalidLeg, NotOverweight, OvershootOut, OvershootIn, TradeTooLarge, RebalanceSlippage, ZeroAmount.
Common ones in the UI: StalePrice (stock feeds stop on weekends: deposits wait, exits work), DepositTooSmall (< 1 USDC), SlippageShares (price moved between quote and execution; retry).
Deposited, Exited, ExitUsdcOpened, ExitUsdcClosed, RebalanceOpened, RebalanceClosed (camelCase in the Anchor event parser). The activity feed on each POTFolio page reads them.
Full review log: security.md.
No withdraw instruction. Exit cannot be paused and does not read the oracle. Keeper trades are atomic: an open without a close in the same transaction fails; under-delivery reverts both. Admin is two-step and will be a Squads multisig on mainnet. Known, accepted: a depositor can self-refer from a second wallet (mitigated later with registered referrers); keeper slippage up to 1% is the implicit rebalance cost.