Overview
If a critical vulnerability is discovered in the farming-pool contract, the current pause mechanism (pause) prevents new operations but leaves all staked/locked tokens permanently trapped — there is no admin path to return tokens to users.
An emergency withdrawal function allows the admin to return a specific user's tokens during an emergency, after the pool is paused.
Design
/// Admin: return all tokens for `user` in an emergency.
///
/// May only be called while the pool is paused. Does NOT slash credits — the
/// user's credit record is preserved for any future claim mechanism.
/// Emits an `emrg_exit` event with `(admin, user, amount)`.
pub fn emergency_withdraw(env: Env, user: Address) -> Result<i128, PoolError> {
get_admin(&env).require_auth();
if !pool_is_paused(&env) {
return Err(PoolError::NotPaused);
}
bump_instance(&env);
let mut total_returned: i128 = 0;
let token = token::TokenClient::new(&env, &get_stake_token(&env));
// Return lock position
if let Some(pos) = get_position(&env, &user) {
token.transfer(&env.current_contract_address(), &user, &pos.amount);
total_returned += pos.amount;
remove_position(&env, &user);
}
// Return stake balance
if let Some(stake) = get_user_stake(&env, &user) {
token.transfer(&env.current_contract_address(), &user, &stake.amount);
total_returned += stake.amount;
remove_user_stake(&env, &user);
}
if total_returned == 0 {
return Err(PoolError::NoActiveStake);
}
env.events().publish(
(symbol_short!("pool"), symbol_short!("emrg_exit")),
(get_admin(&env), user, total_returned),
);
Ok(total_returned)
}
Add NotPaused = 13 to PoolError.
Safeguards
- Only callable while paused (prevents routine admin extraction of user funds)
- Credits are preserved, not zeroed (users lose time but not accrued credits)
- Event emitted for every withdrawal so the action is fully auditable
Acceptance Criteria
Overview
If a critical vulnerability is discovered in the farming-pool contract, the current pause mechanism (
pause) prevents new operations but leaves all staked/locked tokens permanently trapped — there is no admin path to return tokens to users.An emergency withdrawal function allows the admin to return a specific user's tokens during an emergency, after the pool is paused.
Design
Add
NotPaused = 13toPoolError.Safeguards
Acceptance Criteria
emergency_withdrawimplemented, admin-only, paused-onlyemrg_exitevent emittedNotPausederror returned if pool is not pausedErr(PoolError::NotPaused)