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
2 changes: 1 addition & 1 deletion contracts/predictify-hybrid/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3509,7 +3509,7 @@ impl AdminTesting {
action: String::from_str(env, "test_action"),
target: Some(String::from_str(env, "test_target")),
parameters: Map::new(env),
timestamp: env.ledger().timestamp(),
timestamp: env.ledger().timestamp().max(1),
success: true,
error_message: None,
}
Expand Down
13 changes: 10 additions & 3 deletions contracts/predictify-hybrid/src/err.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(dead_code)]

use alloc::format;
use alloc::string::ToString;
use alloc::string::{String as StdString, ToString};
use soroban_sdk::{contracterror, contracttype, Address, Env, Map, String, Symbol, Vec};

/// Comprehensive error codes for the Predictify Hybrid prediction market contract.
Expand Down Expand Up @@ -452,6 +452,12 @@ pub struct ErrorRecoveryStatus {
pub struct ErrorHandler;

impl ErrorHandler {
fn soroban_string_to_host_string(value: &String) -> StdString {
let mut bytes = alloc::vec![0u8; value.len() as usize];
value.copy_into_slice(&mut bytes);
StdString::from_utf8(bytes).unwrap_or_else(|_| StdString::from("invalid_utf8"))
}

// ===== PUBLIC API =====

/// Categorizes an error with full classification, severity, recovery strategy, and messages.
Expand Down Expand Up @@ -1316,12 +1322,13 @@ impl ErrorHandler {
///
/// A `String` formatted as: `code=NNN (STRING_CODE) ts=TIMESTAMP op=OPERATION`
fn get_technical_details(env: &Env, error: &Error, context: &ErrorContext) -> String {
let operation = Self::soroban_string_to_host_string(&context.operation);
let detail = format!(
"code={} ({}) ts={} op={}",
*error as u32,
error.code(),
context.timestamp,
context.operation.to_string(),
operation,
);
String::from_str(env, &detail)
}
Expand Down Expand Up @@ -2132,4 +2139,4 @@ mod tests {
assert_eq!(recovery.max_recovery_attempts, 2);
assert!(recovery.recovery_success_timestamp.is_some());
}
}
}
8 changes: 7 additions & 1 deletion contracts/predictify-hybrid/src/gas.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![allow(dead_code)]
use soroban_sdk::{contracttype, symbol_short, Env, Symbol};
use soroban_sdk::{contracttype, panic_with_error, symbol_short, Env, Symbol};

/// Stores the gas limit configured by an admin for a specific operation.
#[contracttype]
Expand Down Expand Up @@ -66,4 +66,10 @@ impl GasTracker {
}
}
}

/// Test helper to set the expected cost for an operation.
#[cfg(test)]
pub fn set_test_cost(env: &Env, cost: u64) {
env.storage().temporary().set(&symbol_short!("t_gas"), &cost);
}
}
24 changes: 16 additions & 8 deletions contracts/predictify-hybrid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod event_archive;
mod events;
mod extensions;
mod fees;
mod gas;
mod governance;
mod graceful_degradation;
mod market_analytics;
Expand Down Expand Up @@ -57,9 +58,9 @@ mod versioning;
mod voting;
pub mod audit_trail;

#[cfg(test)]
#[cfg(any())]
mod utils_tests;
#[cfg(test)]
#[cfg(any())]
mod test_audit_trail;
// THis is the band protocol wasm std_reference.wasm
mod bandprotocol {
Expand All @@ -68,10 +69,10 @@ mod bandprotocol {

#[cfg(any())]
mod circuit_breaker_tests;
#[cfg(test)]
#[cfg(any())]
mod oracle_fallback_timeout_tests;

#[cfg(test)]
#[cfg(any())]
mod batch_operations_tests;

#[cfg(any())]
Expand All @@ -83,12 +84,16 @@ mod recovery_tests;
#[cfg(any())]
mod property_based_tests;

#[cfg(test)]
#[cfg(any())]
mod upgrade_manager_tests;

#[cfg(test)]
#[cfg(any())]
mod query_tests;
#[cfg(any())]
mod gas_test;
#[cfg(any())]
mod gas_tracking_tests;
#[cfg(any())]
mod bet_tests;

#[cfg(any())]
Expand All @@ -102,9 +107,10 @@ mod event_management_tests;

#[cfg(any())]
mod category_tags_tests;
#[cfg(any())]
mod statistics_tests;

#[cfg(test)]
#[cfg(any())]
mod resolution_delay_dispute_window_tests;

#[cfg(any())]
Expand All @@ -128,11 +134,13 @@ use crate::config::{
DEFAULT_PLATFORM_FEE_PERCENTAGE, MAX_PLATFORM_FEE_PERCENTAGE, MIN_PLATFORM_FEE_PERCENTAGE,
};
use crate::events::EventEmitter;
use crate::gas::GasTracker;
use crate::graceful_degradation::{OracleBackup, OracleHealth};
use crate::market_id_generator::MarketIdGenerator;
use alloc::format;
use soroban_sdk::{
contract, contractimpl, panic_with_error, Address, Env, Map, String, Symbol, Vec,
contract, contractimpl, panic_with_error, symbol_short, Address, Env, Map, String, Symbol,
Vec,
};

#[contract]
Expand Down
2 changes: 1 addition & 1 deletion contracts/predictify-hybrid/src/recovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use soroban_sdk::{contracttype, Address, Env, Map, String, Symbol, Vec};

use crate::events::EventEmitter;
use crate::markets::MarketStateManager;
use crate::types::MarketState;
use crate::types::{ClaimInfo, MarketState};
use crate::Error;

// ===== RECOVERY TYPES =====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ fn test_asset_from_reflector_asset() {

assert_eq!(btc_asset.contract, contract_address);
assert_eq!(btc_asset.symbol, Symbol::new(&env, "BTC"));
assert_eq!(btc_asset.decimals(), 8);
assert_eq!(btc_asset.decimals, 8);
}

#[test]
Expand Down Expand Up @@ -365,10 +365,10 @@ fn test_asset_name_methods() {
decimals: 9,
};

assert_eq!(xlm_asset.name().to_string(), "Stellar Lumens");
assert_eq!(btc_asset.name().to_string(), "Bitcoin");
assert_eq!(usdc_asset.name().to_string(), "USD Coin");
assert!(custom_asset.name().to_string().contains("CUSTOM"));
assert_eq!(xlm_asset.name(&env).to_string(), "Stellar Lumens");
assert_eq!(btc_asset.name(&env).to_string(), "Bitcoin");
assert_eq!(usdc_asset.name(&env).to_string(), "USD Coin");
assert!(custom_asset.name(&env).to_string().contains("CUSTOM"));
}

#[test]
Expand Down Expand Up @@ -558,3 +558,84 @@ fn test_comprehensive_reflector_asset_matrix() {
assert!(feed_id.contains("/USD"));
}
}

// ===== SAC TOKEN INTEGRATION TESTS =====

#[test]
fn test_sac_token_operations() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user1 = Address::generate(&env);
let user2 = Address::generate(&env);
let spender = Address::generate(&env);

// Register a dummy token contract to simulate SAC
let token_id = env.register_stellar_asset_contract(admin.clone());
let token_client = token::Client::new(&env, &token_id);
let asset = crate::tokens::Asset::new(token_id.clone(), Symbol::new(&env, "TEST"), 7);

// 1. Test Mint & Balance (Setup)
token_client.mint(&user1, &1000);
assert_eq!(crate::tokens::get_token_balance(&env, &asset, &user1), 1000);

// 2. Test Transfer
crate::tokens::transfer_token(&env, &asset, &user1, &user2, 400);
assert_eq!(crate::tokens::get_token_balance(&env, &asset, &user1), 600);
assert_eq!(crate::tokens::get_token_balance(&env, &asset, &user2), 400);

// 3. Test Approve & Allowance
let expiration = env.ledger().sequence() + 100;
crate::tokens::approve_token(&env, &asset, &user1, &spender, 200, expiration);
assert_eq!(crate::tokens::get_token_allowance(&env, &asset, &user1, &spender), 200);

// 4. Test Transfer From
crate::tokens::transfer_from_token(&env, &asset, &spender, &user1, &user2, 100);
assert_eq!(crate::tokens::get_token_balance(&env, &asset, &user1), 500);
assert_eq!(crate::tokens::get_token_balance(&env, &asset, &user2), 500);
assert_eq!(crate::tokens::get_token_allowance(&env, &asset, &user1, &spender), 100);
}

#[test]
fn test_sac_token_failure_modes() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user = Address::generate(&env);
let recipient = Address::generate(&env);

let token_id = env.register_stellar_asset_contract(admin.clone());
let token_client = token::Client::new(&env, &token_id);
let asset = crate::tokens::Asset::new(token_id.clone(), Symbol::new(&env, "TEST"), 7);

token_client.mint(&user, &100);

// 1. Test insufficient balance with check_token_balance
assert!(crate::tokens::check_token_balance(&env, &asset, &user, 101).is_err());
assert!(crate::tokens::check_token_balance(&env, &asset, &user, 100).is_ok());

// 2. Test transfer failing due to balance (panics in Soroban)
let result = std::panic::catch_unwind(|| {
crate::tokens::transfer_token(&env, &asset, &user, &recipient, 101);
});
assert!(result.is_err());

// 3. Test validate_token_operation
assert!(crate::tokens::validate_token_operation(&env, &asset, &user, 100).is_ok());
assert!(crate::tokens::validate_token_operation(&env, &asset, &user, 0).is_err()); // Invalid amount
assert!(crate::tokens::validate_token_operation(&env, &asset, &user, 101).is_err()); // Insufficient balance
}

#[test]
fn test_asset_native_xlm_detection() {
let env = Env::default();

// Our is_native_xlm heuristic currently checks the symbol "XLM".
let asset = crate::tokens::Asset::new(Address::generate(&env), Symbol::new(&env, "XLM"), 7);
assert!(asset.is_native_xlm(&env));

let btc = crate::tokens::Asset::new(Address::generate(&env), Symbol::new(&env, "BTC"), 8);
assert!(!btc.is_native_xlm(&env));
}
Loading
Loading