Admin Operations

Instructions below use the Anchor program.methods pattern. There are two authority roles:

  • Alpha, Neutral, and Defense: the fund authority signer on pause_fund, transfer_authority, and update_fund_params.

  • Keystone Core: the portfolio authority signer on pause_portfolio, toggle_emergency, and update_params.

Fee collection: On Alpha, Neutral, and Defense, collect_fees is permissionless (any signer); fees mint to the fund admin’s configured recipient ATA.

User-facing (not admin): migrate_portfolio on Core is called by the user moving their own ksCORE between portfolio IDs; it is not a portfolio-authority admin op.


Keystone Core

Program: keystone_core. Replace portfolioId, account PDAs, and coreProgram with your deployment values.

Pause / unpause portfolio

await coreProgram.methods
  .pausePortfolio(portfolioId, true) // true = pause, false = unpause
  .accounts({
    portfolioState,
    authority: portfolioAuthority.publicKey,
  })
  .signers([portfolioAuthority])
  .rpc();

Emergency de-risk (target override)

Shifts allocation targets toward defense-heavy weights. Not the same as pause.

Update portfolio parameters

Pass null for each optional field you do not want to change. Order and types must match programs/keystone-core/src/lib.rsupdate_params.


Alpha, Neutral, and Defense

The following examples use program as the Anchor client for keystone_alpha, keystone_neutral, or keystone_defense as appropriate.

Close Fund

Closes the fund_state PDA and reclaims rent to authority. Admin-only. Uses raw byte validation at data[16..48] to read the stored authority without Anchor deserialization — safe to call even when the struct layout is stale after a program upgrade.

Note: close_fund only closes fund_state. The share_mint PDA is permanent and survives. After closing, initialize_fund can re-create the fund state — share_mint will be reused via init_if_needed.

To close all funds in one pass, use scripts/devnet/close-funds.ts:

IDL account key: the main state PDA is passed as fundState in .accounts({ ... }) (Rust field fund_state). Fetch/decoding still uses the generated namespaces fundState, basisFundState, and defenseFundState from the account struct types FundState, BasisFundState, and DefenseFundState.

Pause / Unpause

Transfer Authority

Irreversible. Recommended: single keypair → 3-of-5 multisig at $100k TVL → 5-of-7 at $1M+.

Collect fees (permissionless on funds)

Anyone may submit the transaction; fee shares mint to the fund’s fee-recipient ATA. Pass the correct fundState (state PDA), shareMint, fundAuthority, authorityShares (admin ATA for the share mint), solOracle (Neutral), and caller as required by each program’s IDL.


Keystone Alpha Fund: Lending

Enable Lending

Deploy / Withdraw from Lending

Update Alpha Fund Params

Pass null for unchanged parameters. Match order to programs/keystone-alpha/src/lib.rs / keystone_alpha.json.


Keystone Neutral Fund

Jupiter Perps addresses on BasisFundState

BasisFundState stores jupiter_pool, jupiter_sol_custody, and jupiter_collateral_custody. Every deploy/close/settle instruction checks that the accounts you pass match these on-chain pubkeys.

Important: update_fund_params in the current program does not accept Jupiter pool or custody pubkeys — only the optional fields listed in programs/keystone-neutral/src/lib.rs (through swap_to_jitosol_when_parking). If your deployment needs to set or rotate Jupiter routing keys, use whatever process your deployment uses to write correct values into BasisFundState (e.g. program upgrade, init path, or a future admin instruction). Do not follow outdated examples that pass Jupiter pubkeys as extra arguments to updateFundParams; they will not match the IDL.

Update Neutral Fund Params

Pass null for unchanged parameters. Argument order must match keystone_neutral.json / update_fund_params in programs/keystone-neutral/src/lib.rs (26 optionals ending in swap_to_jitosol_when_parking).

Enable Self-Healing + Positive-APY Guards

After this, the keeper can drive the automated cycle (settle_funding, emergency_close_position, deploy_reverse, close_reverse, deploy_capital, parking instructions) according to on-chain gates.

Idle USDC in Marginfi: There is no separate unpark_capital instruction. When usdc_in_lending > 0, deploy_capital withdraws from Marginfi in the same transaction before redeploying (when Marginfi accounts are supplied).

Manual Position Management


Emergency Response

  1. PausepauseFund / pausePortfolio with paused: true

  2. Assess — on-chain state, oracles, integrated protocols

  3. Fix — parameter updates or program upgrade

  4. Unpause — when safe


Troubleshooting

Fund won’t rebalance (Alpha): Check paused, cooldown, circuit breaker, oracle, gas.

Position won’t close (Neutral): Confirm auto_close_on_negative_funding and funding history. Use close_position for an immediate admin close when appropriate.

NAV drawdown guard not triggering: nav_drawdown_guard_bps > 0; guard is evaluated in settle_funding.

deploy_reverse / close_reverse failing: Check position flags, Kamino setup (enable_reverse_basis), balances, and remaining accounts per IDL.

Jupiter account mismatch: On-chain jupiter_pool / custodies in BasisFundState must match the accounts you pass to deploy/close/settle instructions.

Last updated