fn admin(env: soroban_sdk::Env) -> soroban_sdk::Address
fn keeper(env: soroban_sdk::Env) -> Option
fn router(env: soroban_sdk::Env) -> Option
Replace the Registry Wasm. Auth: admin (checked BEFORE the swap).
The gen-1 Registry shipped WITHOUT this ("holds no funds, keep it dumb") and that immutability backfired: the 2026-08-01 audit fixes touched Registry code, and with Factory + every Strategy holding this address permanently, the only patch path was discarding the entire fleet. The admin already controls everything the Registry decides (whitelist, router, keeper, fees), so an admin-gated upgrade adds repairability without adding power.
fn upgrade(env: soroban_sdk::Env, new_wasm_hash: soroban_sdk::BytesN<32>)
Some = upsert, None = remove. Enforces the two fee caps loudly:
fee_bps ≤ MAX_FEE_BPS, and no non-zero fee_bps while the treasury is
unset (set the treasury first). Auth: admin.
fn set_vault(
env: soroban_sdk::Env,
vault: soroban_sdk::Address,
info: Option,
) -> Result<(), soroban_sdk::Error>
(treasury, rebalance_delta_bps) — the protocol-wide fee config read by
every Strategy on the withdraw/rebalance path.
fn fee_config(env: soroban_sdk::Env) -> (Option, u32)
Set/rotate the fleet-wide keeper address (key rotation in one tx). Auth: admin.
fn set_keeper(env: soroban_sdk::Env, keeper: soroban_sdk::Address)
Set the Soroswap router (used from T2). Auth: admin.
fn set_router(env: soroban_sdk::Env, router: soroban_sdk::Address)
Read a vault's record (None if not whitelisted). Bumps the entry's TTL on
read so an ACTIVELY-USED listing can't silently archive and revert new
deposits until re-listed (the Strategy reads this inside every invest, so a
vault in active use stays alive; a real tx persists the bump, a bare simulate
does not).
fn vault_info(env: soroban_sdk::Env, vault: soroban_sdk::Address) -> Option
Accept a proposed admin rotation, step 2 of 2. Auth: the NOMINEE.
fn accept_admin(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Deploy-time init. Stores admin; every other key follows the read-default
rule (Router/Treasury/Keeper ⇒ None, RebalanceDeltaBps ⇒ 0,
KeeperEnabled ⇒ false) and is written only when first set.
fn __constructor(env: soroban_sdk::Env, admin: soroban_sdk::Address)
The keeper-swap oracle floor config, or None while unset (⇒ keeper cannot swap).
fn oracle_config(env: soroban_sdk::Env) -> Option
fn keeper_enabled(env: soroban_sdk::Env) -> bool
Set the protocol-wide treasury + rebalance surcharge. Validates
delta_bps ≤ BPS − MAX_FEE_BPS so fee_bps + delta_bps ≤ 10_000 for every
vault by construction. Once set, the treasury stays Some. Auth: admin.
fn set_fee_config(
env: soroban_sdk::Env,
treasury: soroban_sdk::Address,
delta_bps: u32,
) -> Result<(), soroban_sdk::Error>
Opt asset in/out of the set a KEEPER may name as rebalance_swap's
asset_out. Auth: admin.
Why this exists: rebalance_swap takes a caller-supplied path and
amount_out_min with no on-chain price oracle, and the same keeper can
call rebalance_withdraw first in the SAME transaction — so without a
bound the reachable amount is an entire invested position, not the idle
float. Every hop of the path is checked against this list, not just the
output (an endpoint-only check is trivially routed around via
[allowed, EVIL, allowed]).
What this does NOT do. It narrows the total-loss case; it does not
eliminate it. Allowlisting ASSETS says nothing about pool DEPTH: a
compromised keeper can seed the canonical pair between two allowlisted
assets with dust and still dump a whole position into it at
amount_out_min = 1. The real residual bound is the depth of the
shallowest canonical pair among allowlisted assets — which an attacker can
influence. Only the T3 oracle floor closes this; do not read this l
fn set_swap_asset(env: soroban_sdk::Env, asset: soroban_sdk::Address, allowed: bool)
Rotate the admin (the 2-of-3 multisig address), step 1 of 2. Auth: current
admin. Nothing changes until the nominee calls accept_admin.
It used to be a single set_admin, justified in the security model as
"recoverable by the multisig itself". That was wrong. require_admin
gates set_admin AND upgrade on whatever RegAdmin currently holds, so
a rotation to an address nobody controls leaves no path back: the Registry
can never be re-administered and never upgraded. User funds stay safe —
owners withdraw from their own Strategy, which needs no Registry admin —
but vault listings, fees, router, keeper and oracle freeze permanently and
recovery means deploying a whole new fleet.
The nominee signing step 2 proves the key exists and can sign BEFORE it takes effect, which is the only thing that makes a typo survivable.
fn transfer_admin(env: soroban_sdk::Env, new_admin: soroban_sdk::Address)
Configure the price oracle that bounds a KEEPER's rebalance_swap. Auth: admin.
This closes the accepted T2 HIGH risk: with no oracle, amount_out_min and the
path are caller-supplied, so a compromised keeper key could dump a whole position
into a dust pool at amount_out_min = 1 and the measured-output check would call
it a success. With a floor, that swap simply reverts.
decimals is pinned here rather than read per swap: the Strategy would otherwise
spend a second cross-contract call on every rebalance to discover a constant. An
admin who points this at an oracle with different decimals and does not update the
field mis-scales the floor, so the two MUST be set together — which is why they
live in one struct and one setter.
Use an INDEPENDENT oracle, never Soroswap: Soroswap is the venue being bounded, so pricing the bound from it makes the whole check circular.
fn set_oracle_config(
env: soroban_sdk::Env,
oracle: soroban_sdk::Address,
base: soroban_sdk::Address,
decimals: u32,
max_slippage_bps: u32,
max_price_age: u64,
) -> Result<(), soroban_sdk::Error>
Global kill-switch — fleet-wide halt of keeper-driven rebalances. Auth: admin.
fn set_keeper_enabled(env: soroban_sdk::Env, enabled: bool)
Is asset allowlisted as a keeper-path swap output? Read-default false.
fn swap_asset_allowed(env: soroban_sdk::Env, asset: soroban_sdk::Address) -> bool
Remove the oracle config. Auth: admin.
A TARGETED kill-switch: because the floor is fail-closed, clearing it stops
every keeper swap fleet-wide while leaving rebalance_withdraw /
rebalance_deposit — which are leak-proof by construction — running. The
alternative, set_keeper_enabled(false), is a much bigger hammer: it also
halts every rebalance. If the ORACLE is what misbehaved, this is the
proportionate response.
fn clear_oracle_config(env: soroban_sdk::Env)