🔴 Critical · contracts/token-factory/src/lib.rs:851-881
Description
Two functions perform the identical state transition (state.admin = new_admin, guarded by the same auth/self-transfer checks):
pub fn transfer_admin(env: Env, admin: Address, new_admin: Address) -> Result<(), Error> { /* no event */ }
pub fn update_admin(env: Env, current_admin: Address, new_admin: Address) -> Result<(), Error> { /* emits adm_upd event */ }
Having two public entrypoints for the same privileged operation, one of which is silent on-chain and one of which isn't, is a real operational hazard: any off-chain monitoring/alerting built against the adm_upd event (a reasonable thing to build, given admin-key rotation is a critical security event worth alerting on) will miss an admin transfer performed via transfer_admin. An attacker who has compromised the admin key and wants to quietly rotate to a key they control before performing further damage has a documented, working way to do so without tripping event-based monitoring.
Tasks
Acceptance Criteria
🔴 Critical ·
contracts/token-factory/src/lib.rs:851-881Description
Two functions perform the identical state transition (
state.admin = new_admin, guarded by the same auth/self-transfer checks):Having two public entrypoints for the same privileged operation, one of which is silent on-chain and one of which isn't, is a real operational hazard: any off-chain monitoring/alerting built against the
adm_updevent (a reasonable thing to build, given admin-key rotation is a critical security event worth alerting on) will miss an admin transfer performed viatransfer_admin. An attacker who has compromised the admin key and wants to quietly rotate to a key they control before performing further damage has a documented, working way to do so without tripping event-based monitoring.Tasks
transfer_admincallupdate_admininternally (or vice versa) so there is exactly one code path and one event-emission point, then mark the other#[deprecated](if Soroban tooling supports contract-level deprecation) or remove it in a coordinated upgrade.transfer_adminemit the sameadm_updevent asupdate_adminso no admin-rotation path is silent.AdminPanel.tsx) and any indexer/monitoring code for which of the two functions it actually calls, and whether it currently listens foradm_upd.Acceptance Criteria
FactoryState.adminemits an event, verified by test.