Summary
contracts/risk-pool/src/lib.rs provides two admin functions — send_premium_to_treasury() (line 338) and send_premium_to_backstop() (line 352) — that transfer arbitrary amounts of USDC from the pool to the treasury or backstop address with no accounting of how much premium has already been distributed.
When receive_premium() is called, it splits the amount (80% LP, 10% treasury, 10% backstop) and immediately transfers the treasury and backstop shares. However, the two admin functions allow the admin to additionally drain pool liquidity to those same addresses — bypassing LP capital protection.
Code
pub fn send_premium_to_treasury(env: Env, caller: Address, amount: i128) {
Self::require_admin(&env, &caller);
// ← no check against AccumulatedPremium or any premium ledger
token::Client::new(&env, &usdc).transfer(&env.current_contract_address(), &treasury, &amount);
}
Impact
- Admin can call
receive_premium (which already sends 10% to treasury) and then immediately call send_premium_to_treasury for the same amount, effectively double-paying the treasury from LP capital
- There is no on-chain accounting of how much has been distributed — the admin can drain pool liquidity to trusted addresses without LP consent
- This partially defeats the purpose of the 7-day timelock on
execute_admin_withdrawal, which was designed to limit admin power over pool funds
Fix
Either remove these functions entirely (since receive_premium already handles distribution), or gate them with the same 7-day timelock mechanism and log the amounts against a premium-distribution ledger:
// Track cumulative distributions
let distributed: i128 = env.storage().instance().get(&StorageKey::TotalTreasuryDistributed).unwrap_or(0);
let available_for_distribution = accumulated_premium_10pct - distributed;
if amount > available_for_distribution { panic_with_error!(&env, Error::InsufficientFunds); }
Severity: High
Summary
contracts/risk-pool/src/lib.rsprovides two admin functions —send_premium_to_treasury()(line 338) andsend_premium_to_backstop()(line 352) — that transfer arbitrary amounts of USDC from the pool to the treasury or backstop address with no accounting of how much premium has already been distributed.When
receive_premium()is called, it splits the amount (80% LP, 10% treasury, 10% backstop) and immediately transfers the treasury and backstop shares. However, the two admin functions allow the admin to additionally drain pool liquidity to those same addresses — bypassing LP capital protection.Code
Impact
receive_premium(which already sends 10% to treasury) and then immediately callsend_premium_to_treasuryfor the same amount, effectively double-paying the treasury from LP capitalexecute_admin_withdrawal, which was designed to limit admin power over pool fundsFix
Either remove these functions entirely (since
receive_premiumalready handles distribution), or gate them with the same 7-day timelock mechanism and log the amounts against a premium-distribution ledger:Severity: High