Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/hunty-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ doctest = false
soroban-sdk = { workspace = true }
reward-interface = { path = "../reward-interface" }
hunty-migration = { path = "../migration", package = "hunty-migration" }
common = { path = "../common", package = "hunty-common" }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
Expand Down
55 changes: 36 additions & 19 deletions contracts/hunty-core/src/admin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use soroban_sdk::{Address, Env, Symbol, Vec};
use crate::storage::{is_admin, is_paused, set_paused, set_admin, is_blacklisted, set_blacklisted};
use soroban_sdk::{Address, Env, Symbol, Vec, symbol_short, String};
use crate::storage::Storage;
use crate::types::HuntStatus;
use common::audit::*;
use common::audit_emitter::emit_audit_event;

Expand All @@ -8,31 +9,31 @@ pub const CONTRACT_NAME: Symbol = symbol_short!("HUNTY");
/// Toggle contract pause state
pub fn toggle_pause(env: &Env, admin: &Address) {
admin.require_auth();
assert!(is_admin(env, admin), "Unauthorized: caller is not admin");
assert!(Storage::is_admin(env, admin), "Unauthorized: caller is not admin");

let current = is_paused(env);
let current = Storage::is_paused(env);
let new_state = !current;
set_paused(env, new_state);
Storage::set_paused(env, new_state);

let action = if new_state { ACTION_PAUSE } else { ACTION_UNPAUSE };

let mut details = Vec::new(env);
details.push_back((symbol_short!("prev_state"), String::from_str(env, if current { "unpaused" } else { "paused" })));
details.push_back((symbol_short!("new_state"), String::from_str(env, if new_state { "paused" } else { "unpaused" })));
details.push_back((symbol_short!("prev"), String::from_str(env, if current { "unpaused" } else { "paused" })));
details.push_back((symbol_short!("new"), String::from_str(env, if new_state { "paused" } else { "unpaused" })));

emit_audit_event(env, admin, action, CONTRACT_NAME, details);
}

/// Transfer admin role to new address
pub fn transfer_admin(env: &Env, current_admin: &Address, new_admin: &Address) {
current_admin.require_auth();
assert!(is_admin(env, current_admin), "Unauthorized");
assert!(Storage::is_admin(env, current_admin), "Unauthorized");

let old_admin = get_admin(env);
set_admin(env, new_admin);
let old_admin = Storage::get_admin(env);
Storage::set_admin(env, new_admin);

let mut details = Vec::new(env);
details.push_back((symbol_short!("old_admin"), old_admin.to_string()));
details.push_back((symbol_short!("old_admin"), old_admin.unwrap().to_string()));
details.push_back((symbol_short!("new_admin"), new_admin.to_string()));

emit_audit_event(env, current_admin, ACTION_ADMIN_TRANSFERRED, CONTRACT_NAME, details);
Expand All @@ -41,9 +42,9 @@ pub fn transfer_admin(env: &Env, current_admin: &Address, new_admin: &Address) {
/// Add address to blacklist
pub fn blacklist_add(env: &Env, admin: &Address, target: &Address) {
admin.require_auth();
assert!(is_admin(env, admin), "Unauthorized");
assert!(Storage::is_admin(env, admin), "Unauthorized");

set_blacklisted(env, target, true);
Storage::set_blacklisted(env, target, true);

let mut details = Vec::new(env);
details.push_back((symbol_short!("target"), target.to_string()));
Expand All @@ -55,9 +56,9 @@ pub fn blacklist_add(env: &Env, admin: &Address, target: &Address) {
/// Remove address from blacklist
pub fn blacklist_remove(env: &Env, admin: &Address, target: &Address) {
admin.require_auth();
assert!(is_admin(env, admin), "Unauthorized");
assert!(Storage::is_admin(env, admin), "Unauthorized");

set_blacklisted(env, target, false);
Storage::set_blacklisted(env, target, false);

let mut details = Vec::new(env);
details.push_back((symbol_short!("target"), target.to_string()));
Expand All @@ -69,17 +70,33 @@ pub fn blacklist_remove(env: &Env, admin: &Address, target: &Address) {
/// Emergency stop all active hunts
pub fn emergency_stop_all(env: &Env, admin: &Address, reason: &str) {
admin.require_auth();
assert!(is_admin(env, admin), "Unauthorized");
assert!(Storage::is_admin(env, admin), "Unauthorized");

// Stop all active hunts
let active_hunts = get_active_hunt_ids(env);
let active_hunts = Storage::get_active_hunt_ids(env);
for hunt_id in active_hunts.iter() {
set_hunt_status(env, hunt_id, HuntStatus::EmergencyStopped);
Storage::set_hunt_status(env, hunt_id, HuntStatus::EmergencyStopped);
}

let mut details = Vec::new(env);
details.push_back((symbol_short!("reason"), String::from_str(env, reason)));
details.push_back((symbol_short!("hunts_affected"), active_hunts.len().to_string()));

let mut buf = [0u8; 11];
let mut val = active_hunts.len();
let mut idx = 11;
if val == 0 {
idx -= 1;
buf[idx] = b'0';
} else {
while val > 0 {
idx -= 1;
buf[idx] = b'0' + (val % 10) as u8;
val /= 10;
}
}
let len_str = core::str::from_utf8(&buf[idx..]).unwrap();
let count_str = String::from_str(env, len_str);
details.push_back((symbol_short!("affected"), count_str));

emit_audit_event(env, admin, ACTION_EMERGENCY, CONTRACT_NAME, details);
}
15 changes: 2 additions & 13 deletions contracts/hunty-core/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ pub enum HuntErrorCode {
AddressBlacklisted = 36,
ContractPaused = 37,
InvalidMaxAttempts = 38,
MaxAttemptsExceeded = 39,
}

#[derive(Debug)]
Expand Down Expand Up @@ -84,8 +83,6 @@ pub enum HuntError {
InvalidTimeBonusConfig,
AddressBlacklisted,
ContractPaused,
InvalidMaxAttempts,
MaxAttemptsExceeded { clue_id: u32, limit: u32 },
}

impl fmt::Display for HuntError {
Expand Down Expand Up @@ -202,12 +199,12 @@ impl fmt::Display for HuntError {
HuntError::PendingAdminMismatch { expected, actual } => {
write!(
f,
"Pending admin mismatch: expected {}, got {}",
"Pending admin mismatch: expected {:?}, got {:?}",
expected, actual
)
}
HuntError::AdminAlreadyProposed { pending } => {
write!(f, "Admin rotation already proposed for {}", pending)
write!(f, "Admin rotation already proposed for {:?}", pending)
}
HuntError::AddressBlacklisted => {
write!(f, "Address is blacklisted from creating hunts")
Expand All @@ -221,12 +218,6 @@ impl fmt::Display for HuntError {
HuntError::ContractPaused => {
write!(f, "Contract is currently paused")
}
HuntError::InvalidMaxAttempts => {
write!(f, "max_attempts_per_clue must be greater than zero")
}
HuntError::MaxAttemptsExceeded { clue_id, limit } => {
write!(f, "Max attempts ({}) exceeded for clue {}", limit, clue_id)
}
}
}
}
Expand Down Expand Up @@ -270,8 +261,6 @@ impl From<HuntError> for HuntErrorCode {
HuntError::InvalidTimeBonusConfig => HuntErrorCode::InvalidTimeBonusConfig,
HuntError::AddressBlacklisted => HuntErrorCode::AddressBlacklisted,
HuntError::ContractPaused => HuntErrorCode::ContractPaused,
HuntError::InvalidMaxAttempts => HuntErrorCode::InvalidMaxAttempts,
HuntError::MaxAttemptsExceeded { .. } => HuntErrorCode::MaxAttemptsExceeded,
}
}
}
Loading
Loading