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
9 changes: 9 additions & 0 deletions contracts/analytics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub use errors::ContractError;
/// Type alias used by generated client code and test harnesses.
pub type Error = ContractError;

/// Maximum distance between a supplied metric timestamp and ledger time.
/// Adjust this deployment constant when the expected ingestion delay changes.
const MAX_TIMESTAMP_DRIFT_SECONDS: u64 = 86_400;

/// Persistent-storage keys used by the Analytics contract.
#[contracttype]
#[derive(Clone)]
Expand Down Expand Up @@ -256,6 +260,11 @@ impl Analytics {
// Require caller authentication
caller.require_auth();

let current_timestamp = env.ledger().timestamp();
if timestamp.abs_diff(current_timestamp) > MAX_TIMESTAMP_DRIFT_SECONDS {
return Err(ContractError::ValueOutOfRange);
}

// Check for duplicate entry (same metric_name, timestamp)
let key = DataKey::Metric(metric_name.clone(), timestamp);
if env.storage().persistent().has(&key) {
Expand Down
18 changes: 18 additions & 0 deletions contracts/analytics/tests/time_series.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ fn test_pause_and_unpause() {
assert_eq!(client.record_metric_at(&metric, &200, &2000), 200);
}

#[test]
fn test_record_metric_at_rejects_timestamp_outside_ledger_drift() {
let (env, client, _admin) = setup_env();
env.ledger().set_timestamp(100_000);
let metric = symbol_short!("future");
let timestamp = env.ledger().timestamp() + 86_401;
let stale_timestamp = env.ledger().timestamp() - 86_401;

assert_eq!(
client.try_record_metric_at(&metric, &timestamp, &100),
Err(Ok(ContractError::ValueOutOfRange))
);
assert_eq!(
client.try_record_metric_at(&metric, &stale_timestamp, &100),
Err(Ok(ContractError::ValueOutOfRange))
);
}

#[test]
fn test_time_bucketed_aggregation() {
let (env, client, _admin) = setup_env();
Expand Down
3 changes: 3 additions & 0 deletions contracts/markets/tests/issues_007_012_014_022.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ fn test_user_bet_enumeration_reflects_market_cancellation() {
fn test_default_winner_takes_all_payout() {
let te = setup_test_env();
let m1 = create_standard_market(&te, 2);
let token_client = soroban_sdk::token::Client::new(&te.env, &te.token_address);
let starting_balance = token_client.balance(&te.bettor1);

// Bettor1 stakes 200 on Outcome 0 (Yes)
// Bettor2 stakes 600 on Outcome 1 (No)
Expand All @@ -351,6 +353,7 @@ fn test_default_winner_takes_all_payout() {

let payout = te.client.claim_winnings(&te.bettor1, &m1);
assert_eq!(payout, 800);
assert_eq!(token_client.balance(&te.bettor1), starting_balance - 200 + payout);

// Losing bettor gets error
let lose_res = te.client.try_claim_winnings(&te.bettor2, &m1);
Expand Down
43 changes: 42 additions & 1 deletion contracts/oracles/tests/auth_snap.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#![cfg(test)]

use soroban_sdk::{
contract, contractimpl, testutils::Address as _, Address, Env, String,
contract, contractimpl, testutils::{Address as _, MockAuth, MockAuthInvoke}, Address, Env,
IntoVal, String,
};
use oracles::{OraclesContract, OraclesContractClient, OraclePriceData};

Expand Down Expand Up @@ -121,6 +122,46 @@ fn test_remove_oracle_fails_without_auth() {
client.remove_oracle(&admin, &oracle);
}

#[test]
#[should_panic]
fn test_add_oracle_rejects_non_admin_signer() {
let setup = setup_test_env();
let outsider = Address::generate(&setup.env);
setup.env.mock_all_auths();
setup.client.add_oracle(&setup.admin, &setup.oracle);

setup.env.mock_auths(&[MockAuth {
address: &setup.admin,
invoke: &MockAuthInvoke {
contract: &setup.contract_id,
fn_name: "add_oracle",
args: (&setup.admin, &setup.oracle).into_val(&setup.env),
sub_invokes: &[],
},
}]);
setup.client.add_oracle(&outsider, &setup.oracle);
}

#[test]
#[should_panic]
fn test_remove_oracle_rejects_non_admin_signer() {
let setup = setup_test_env();
let outsider = Address::generate(&setup.env);
setup.env.mock_all_auths();
setup.client.add_oracle(&setup.admin, &setup.oracle);

setup.env.mock_auths(&[MockAuth {
address: &setup.admin,
invoke: &MockAuthInvoke {
contract: &setup.contract_id,
fn_name: "remove_oracle",
args: (&setup.admin, &setup.oracle).into_val(&setup.env),
sub_invokes: &[],
},
}]);
setup.client.remove_oracle(&outsider, &setup.oracle);
}

// ---------------------------------------------------------------------------
// 2. Read-only entrypoints do not require auth
// ---------------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions contracts/predictify-hybrid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1820,7 +1820,7 @@ impl PredictifyHybrid {
/// State-changing paths may emit events through internal managers; read-only query paths emit no events.
/// Set the minimum oracle price-confidence threshold in basis points.
/// Prices from oracles with confidence ratio above this threshold are rejected.
/// `min_confidence_bps` = 0 disables the check.
/// `min_confidence_bps` = 0 disables the check. The default is 5000 (50%).
pub fn set_oracle_confidence_threshold(env: Env, admin: Address, min_confidence_bps: u32) {
admin.require_auth();
if min_confidence_bps > 10_000 {
Expand All @@ -1831,12 +1831,12 @@ impl PredictifyHybrid {
.set(&Symbol::new(&env, "oracle_conf_bps"), &min_confidence_bps);
}

/// Get the configured oracle confidence threshold in bps (0 = disabled).
/// Get the configured oracle confidence threshold in bps (default 5000; 0 = disabled).
pub fn get_oracle_confidence_threshold(env: Env) -> u32 {
env.storage()
.instance()
.get(&Symbol::new(&env, "oracle_conf_bps"))
.unwrap_or(0u32)
.unwrap_or(5_000u32)
}

/// Set configurable weight for a specific oracle source
Expand Down
1 change: 1 addition & 0 deletions contracts/predictify-hybrid/src/reflector_auth_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ fn test_fetch_oracle_result_mock_all_auths_any_caller_passes_auth() {
fn test_set_oracle_confidence_threshold_precise_admin_mock_auth_passes() {
let (env, cid, admin) = setup();
let _market_id = make_reflector_market(&env, &cid, &admin);
assert_eq!(client(&env, &cid).get_oracle_confidence_threshold(), 5_000u32);

env.mock_auths(&[MockAuth {
address: &admin,
Expand Down
Loading