11//! Denied issuer index storage. O(1) operations via swap-and-pop.
22
3- use crate :: constants:: { PERSISTENT_TTL_EXTEND_TO , PERSISTENT_TTL_THRESHOLD } ;
3+ use crate :: constants:: { MAX_DENIED_ISSUERS , PERSISTENT_TTL_EXTEND_TO , PERSISTENT_TTL_THRESHOLD } ;
4+ use crate :: error:: ContractError ;
45use super :: VcVaultDataKey ;
5- use soroban_sdk:: { Address , Env } ;
6+ use soroban_sdk:: { panic_with_error , Address , Env } ;
67
78// --- Denied issuer index ---
89
@@ -84,6 +85,9 @@ pub fn append_denied_issuer_to_index(e: &Env, issuer: &Address) {
8485 return ;
8586 }
8687 let count = read_denied_issuer_count ( e) ;
88+ if count >= MAX_DENIED_ISSUERS {
89+ panic_with_error ! ( e, ContractError :: IssuerListTooLong ) ;
90+ }
8791 write_denied_issuer_at ( e, count, issuer) ;
8892 write_denied_issuer_position ( e, issuer, count) ;
8993 write_denied_issuer_count ( e, count + 1 ) ;
@@ -101,9 +105,17 @@ pub fn remove_denied_issuer_from_index(e: &Env, issuer: &Address) {
101105 }
102106 let last = count - 1 ;
103107 if position != last {
104- let last_addr = read_denied_issuer_at ( e, last) . unwrap ( ) ;
105- write_denied_issuer_at ( e, position, & last_addr) ;
106- write_denied_issuer_position ( e, & last_addr, position) ;
108+ // Normal swap-and-pop moves the last entry into the freed slot.
109+ // Defensive: if the last slot is somehow missing (corrupt state), clear
110+ // the freed slot instead of leaving the removed issuer there — otherwise
111+ // it becomes a ghost index entry. Never panic on the missing slot.
112+ match read_denied_issuer_at ( e, last) {
113+ Some ( last_addr) => {
114+ write_denied_issuer_at ( e, position, & last_addr) ;
115+ write_denied_issuer_position ( e, & last_addr, position) ;
116+ }
117+ None => remove_denied_issuer_at ( e, position) ,
118+ }
107119 }
108120 remove_denied_issuer_at ( e, last) ;
109121 remove_denied_issuer_position ( e, issuer) ;
0 commit comments