Skip to content
Merged
40 changes: 7 additions & 33 deletions contracts/predictify-hybrid/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,12 +465,7 @@ impl AdminAccessControl {
admin: &Address,
permission: &AdminPermission,
) -> Result<(), Error> {
// Check original admin for backward compatibility first
if AdminManager::is_original_admin(env, admin) {
return Ok(());
}

// Try new multi-admin system if migrated
// Try new multi-admin system first if migrated
if AdminSystemIntegration::is_migrated(env) {
return AdminManager::validate_admin_permission(env, admin, *permission);
}
Expand Down Expand Up @@ -1258,16 +1253,6 @@ impl AdminManager {
.persistent()
.set(&count_key, &(current_count + 1));

// Maintain a list of admin addresses for iteration
let list_key = Symbol::new(env, "AdminList");
let mut admin_list: Vec<Address> = env
.storage()
.persistent()
.get(&list_key)
.unwrap_or_else(|| Vec::new(env));
admin_list.push_back(new_admin.clone());
env.storage().persistent().set(&list_key, &admin_list);

// Emit event using existing system
Self::emit_admin_change_event(env, new_admin, AdminActionType::Added);

Expand Down Expand Up @@ -1310,18 +1295,6 @@ impl AdminManager {
.set(&count_key, &(current_count - 1));
}

// Remove from admin list
let list_key = Symbol::new(env, "AdminList");
if let Some(admin_list) = env.storage().persistent().get::<_, Vec<Address>>(&list_key) {
let mut new_list: Vec<Address> = Vec::new(env);
for addr in admin_list.iter() {
if &addr != admin_to_remove {
new_list.push_back(addr.clone());
}
}
env.storage().persistent().set(&list_key, &new_list);
}

Self::emit_admin_change_event(env, admin_to_remove, AdminActionType::Removed);
Ok(())
}
Expand Down Expand Up @@ -1441,6 +1414,7 @@ impl AdminManager {
None
}


/// Emits admin change events using existing AdminActionType
pub fn emit_admin_change_event(env: &Env, admin: &Address, action: AdminActionType) {
let action_str = match action {
Expand All @@ -1462,14 +1436,14 @@ impl AdminManager {
// ===== Helper Methods =====

/// Generate a proper admin storage key using the correct environment
fn get_admin_key(env: &Env, admin: &Address) -> (Symbol, Address) {
// Use a tuple key for per-admin storage
// This avoids Symbol character limitations by using Address directly
(Symbol::new(env, "MultiAdmin"), admin.clone())
fn get_admin_key(env: &Env, admin: &Address) -> Symbol {
// Create a unique key based on admin address
let key_str = format!("MultiAdmin_{:?}", admin.to_string());
Symbol::new(env, &key_str)
}

/// Check if an address is the original admin from single-admin system
fn is_original_admin(env: &Env, admin: &Address) -> bool {
pub fn is_original_admin(env: &Env, admin: &Address) -> bool {
if let Some(original_admin) = Self::get_original_admin(env) {
return admin == &original_admin;
}
Expand Down
159 changes: 159 additions & 0 deletions contracts/predictify-hybrid/src/balance_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#![cfg(test)]

use crate::test::PredictifyTest;
use crate::types::ReflectorAsset;
use crate::errors::Error;
use soroban_sdk::{testutils::Address as _, Address, Env, Symbol};

#[test]
fn test_deposit_and_withdrawal_flow() {
let test = PredictifyTest::setup();
let env = &test.env;
let user = &test.user;
let contract_address = &test.contract_id;

// 1. Initial State: User has 1000 XLM (minted in setup), Contract has 0
let token_client = soroban_sdk::token::Client::new(env, &test.token_test.token_id);
// Verify initial token balances
// Note: token_test.token_client is associated with the token contract
// We need to use the client correctly.
// In PredictifyTest::setup, we minted 1000_0000000 to user.

assert_eq!(token_client.balance(user), 1000_0000000);
assert_eq!(token_client.balance(contract_address), 0);

// 2. Deposit Funds
let deposit_amount = 500_0000000; // 500 XLM
let client = crate::PredictifyHybridClient::new(env, contract_address);

// We need to mock auth for the user
env.mock_all_auths();

let balance = client.deposit(user, &ReflectorAsset::Stellar, &deposit_amount);

// 3. Verify Deposit
assert_eq!(balance.amount, deposit_amount);
assert_eq!(balance.user, *user);

// Verify stored balance matches
let stored_balance = client.get_balance(user, &ReflectorAsset::Stellar);
assert_eq!(stored_balance.amount, deposit_amount);

// Verify token transfer happened
assert_eq!(token_client.balance(user), 500_0000000);
assert_eq!(token_client.balance(contract_address), 500_0000000);

// 4. Withdraw Funds
let withdraw_amount = 200_0000000; // 200 XLM
let balance_after_withdraw = client.withdraw(user, &ReflectorAsset::Stellar, &withdraw_amount);

// 5. Verify Withdrawal
assert_eq!(balance_after_withdraw.amount, 300_0000000); // 500 - 200 = 300

// Verify stored balance updated
let stored_balance_2 = client.get_balance(user, &ReflectorAsset::Stellar);
assert_eq!(stored_balance_2.amount, 300_0000000);

// Verify token transfer happened (Contract -> User)
assert_eq!(token_client.balance(user), 700_0000000); // 500 + 200 = 700
assert_eq!(token_client.balance(contract_address), 300_0000000); // 500 - 200 = 300
}

#[test]
fn test_insufficient_balance_withdrawal() {
let test = PredictifyTest::setup();
let env = &test.env;
let user = &test.user;
let contract_address = &test.contract_id;
let client = crate::PredictifyHybridClient::new(env, contract_address);

env.mock_all_auths();

// Deposit 100
let deposit_amount = 100_0000000;
client.deposit(user, &ReflectorAsset::Stellar, &deposit_amount);

// Try to withdraw 150
let withdraw_amount = 150_0000000;
let result = client.try_withdraw(user, &ReflectorAsset::Stellar, &withdraw_amount);

assert_eq!(result, Err(Ok(Error::InsufficientBalance)));
}

#[test]
fn test_invalid_deposit_amount() {
let test = PredictifyTest::setup();
let env = &test.env;
let user = &test.user;
let contract_address = &test.contract_id;
let client = crate::PredictifyHybridClient::new(env, contract_address);

env.mock_all_auths();

// Try to deposit 0
let result = client.try_deposit(user, &ReflectorAsset::Stellar, &0);
assert_eq!(result, Err(Ok(Error::InvalidInput)));

// Try to deposit negative
let result_neg = client.try_deposit(user, &ReflectorAsset::Stellar, &-100);
assert_eq!(result_neg, Err(Ok(Error::InvalidInput)));
}

#[test]
fn test_invalid_withdraw_amount() {
let test = PredictifyTest::setup();
let env = &test.env;
let user = &test.user;
let contract_address = &test.contract_id;
let client = crate::PredictifyHybridClient::new(env, contract_address);

env.mock_all_auths();

client.deposit(user, &ReflectorAsset::Stellar, &1000);

// Try to withdraw 0
let result = client.try_withdraw(user, &ReflectorAsset::Stellar, &0);
assert_eq!(result, Err(Ok(Error::InvalidInput)));

// Try to withdraw negative
let result_neg = client.try_withdraw(user, &ReflectorAsset::Stellar, &-100);
assert_eq!(result_neg, Err(Ok(Error::InvalidInput)));
}

#[test]
fn test_multiple_deposits() {
let test = PredictifyTest::setup();
let env = &test.env;
let user = &test.user;
let contract_address = &test.contract_id;
let client = crate::PredictifyHybridClient::new(env, contract_address);

env.mock_all_auths();

// Deposit 1
client.deposit(user, &ReflectorAsset::Stellar, &100);
let b1 = client.get_balance(user, &ReflectorAsset::Stellar);
assert_eq!(b1.amount, 100);

// Deposit 2
client.deposit(user, &ReflectorAsset::Stellar, &200);
let b2 = client.get_balance(user, &ReflectorAsset::Stellar);
assert_eq!(b2.amount, 300);
}

#[test]
fn test_deposit_invalid_asset() {
let test = PredictifyTest::setup();
let env = &test.env;
let user = &test.user;
let contract_address = &test.contract_id;
let client = crate::PredictifyHybridClient::new(env, contract_address);

env.mock_all_auths();

// Try to deposit Bitcoin (not configured/supported yet in balances.rs match statement)
// The current implementation in balances.rs returns Error::InvalidInput for non-Stellar assets
// because it can't resolve the token client for them.
let result = client.try_deposit(user, &ReflectorAsset::BTC, &100);
assert_eq!(result, Err(Ok(Error::InvalidInput)));
}
Loading
Loading