This document describes the admin-role safety behavior implemented in quicklendx-contracts/src/admin.rs.
- Enforce single-admin ownership.
- Prevent unauthorized admin replacement.
- Support safe rotation via optional two-step flow.
- Prevent stuck/overlapping transfers using a transfer lock.
- Emit auditable events for every admin-state transition.
ADMIN_KEY("admin"): active admin address.ADMIN_INITIALIZED_KEY("adm_init"): one-time initialization flag.ADMIN_TRANSFER_LOCK_KEY("adm_lock"): transfer-in-progress lock.ADMIN_PENDING_KEY("adm_pnd"): pending admin in two-step mode.ADMIN_TWO_STEP_KEY("adm_2st"): optional two-step mode toggle.
AdminStorage::initialize(env, admin):
- Requires
admin.require_auth(). - Fails if already initialized (
OperationNotAllowed). - Writes admin + initialized flag atomically.
- Emits
adm_init.
AdminStorage::transfer_admin(env, current_admin, new_admin):
- Requires current admin auth and role check.
- Rejects self-transfer.
- Rejects transfer if lock/pending state exists.
- Performs atomic swap
current -> new. - Emits
adm_trf.
Enable: AdminStorage::set_two_step_enabled(env, admin, true).
Flow:
- Current admin initiates transfer via
transfer_admin(orinitiate_admin_transfer). - Contract stores
ADMIN_PENDING_KEY, sets transfer lock, emitsadm_req. - Pending admin must call
accept_admin_transfer. - On accept, active admin is updated, pending+lock are cleared, emits
adm_trf.
Cancel path:
- Current admin may call
cancel_admin_transferbefore acceptance. - Pending state + lock are cleared.
- Emits
adm_cnl.
Disable behavior:
set_two_step_enabled(..., false)clears pending+lock to avoid stuck transfer state.- Emits
adm_2st.
adm_init: admin initialized.adm_trf: admin transfer completed.adm_req: two-step transfer initiated.adm_cnl: pending transfer cancelled.adm_2st: two-step mode updated.
- Admin initialization is one-time.
- Unauthorized callers cannot replace admin.
- Transfer lock blocks overlapping/reentrant transfer attempts.
- Pending transfer can be accepted only by the nominated address.
- Pending/lock state can be safely cancelled or cleared (no stuck transfer).
- Admin transition events are emitted on each state change.
To keep admin transfer safety regressions visible while legacy modules are still being migrated, CI enforces a dedicated coverage threshold for src/admin.rs:
- Report generation:
cargo llvm-cov --lib --lcov --output-path coverage/lcov.info - Admin gate:
scripts/check-admin-coverage.sh coverage/lcov.info - Minimum required:
95%line coverage (ADMIN_COVERAGE_MIN, default95)
AdminStorage::verify_admin_handover(env, proposed) is a pure, read-only validation helper.
Callers (operators, downstream contracts, frontend integrations) that need to present pre-flight feedback before committing a transfer previously had to either:
- Attempt the full
transfer_adminand inspect the opaqueOperationNotAllowederror, or - Re-implement the same-address check themselves.
verify_admin_handover provides a single, well-named entry point that surfaces the identity violation as an explicit, typed OperationNotAllowed without side effects.
pub fn verify_admin_handover(env: &Env, proposed: &Address) -> Result<(), QuickLendXError>| Condition | Return value |
|---|---|
| Admin subsystem not initialized | Err(OperationNotAllowed) |
proposed == current admin |
Err(OperationNotAllowed) |
proposed != current admin |
Ok(()) |
- Read-only: no storage writes, no auth calls, no events emitted.
- Idempotent: safe to call multiple times; each call reflects the current on-chain state.
- Composable: use as a pre-flight guard before
transfer_adminorinitiate_admin_transfer.
// Pre-flight check from an outer call or test.
AdminStorage::verify_admin_handover(&env, &proposed_admin)?;
// Only reaches here if proposed != current admin.
AdminStorage::transfer_admin(&env, ¤t_admin, &proposed_admin)?;verify_admin_handover does not replace the existing guard inside transfer_admin or
initiate_admin_transfer — both functions still independently reject self-transfers.
The helper is additive and backwards-compatible.