Skip to content

[risk-pool] propose_new_admin stores pending admin without emitting an event or requiring new admin's confirmation before the proposal is visible on-chain #141

Description

@nonsobethel0-dev

Summary

contracts/risk-pool/src/lib.rs implements a two-step admin transfer via propose_new_admin() (line 663) and accept_admin() (line 670). However, propose_new_admin has two issues:

  1. No event emitted — the pending admin proposal is stored silently in instance storage with no on-chain notification. Off-chain monitoring cannot detect an impending admin change.
  2. No expiry — the pending admin proposal has no timeout. A compromised admin can propose a malicious address and wait indefinitely for an opportunity to force-accept it.

Code

pub fn propose_new_admin(env: Env, admin: Address, new_admin: Address) {
    Self::require_admin(&env, &admin);
    env.storage().instance().set(&StorageKey::PendingAdmin, &new_admin);
    // ← no event emitted
    // ← no expiry timestamp stored
}

accept_admin (line 670) correctly requires the proposed admin to authorize, but without an event from propose_new_admin, observers have no way to know a proposal is pending until accept_admin is called.

Impact

  • LPs who monitor contract events to detect admin changes will have no warning before the admin handover completes
  • If the admin key is compromised, the attacker can silently propose a malicious address and wait for a low-attention period to accept it
  • A stale pending admin proposal that was never cancelled stays valid forever — a forgotten proposal becomes an unintended attack surface

Fix

pub fn propose_new_admin(env: Env, admin: Address, new_admin: Address) {
    Self::require_admin(&env, &admin);
    let now = env.ledger().timestamp();
    let proposal = PendingAdminProposal {
        new_admin: new_admin.clone(),
        proposed_at: now,
        expires_at: now + 7 * 24 * 60 * 60, // 7-day window
    };
    env.storage().instance().set(&StorageKey::PendingAdmin, &proposal);
    env.events().publish(
        (Symbol::new(&env, "admin_proposed"),),
        AdminProposed { current_admin: admin, new_admin },
    );
}

Severity: Medium

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions