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
101 changes: 101 additions & 0 deletions contract/src/conditional.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#![allow(unused_imports)]
use soroban_sdk::{Address, Env, Symbol, Vec};

use crate::config;
use crate::errors::InsightArenaError;
use crate::market;
use crate::storage_types::{ConditionalMarket, DataKey, Market};
use crate::ttl;

// ── Constants ─────────────────────────────────────────────────────────────────

/// Maximum depth of conditional market chains
pub const MAX_CONDITIONAL_DEPTH: u32 = 5;

/// Maximum number of conditional markets per parent
pub const MAX_CONDITIONALS_PER_PARENT: u32 = 50;

// ── Conditional Market Creation ──────────────────────────────────────────────

use crate::market::CreateMarketParams;

pub fn create_conditional_market(
env: &Env,
creator: Address,
parent_market_id: u64,
required_outcome: Symbol,
params: CreateMarketParams,
) -> Result<u64, InsightArenaError> {
let parent_market: Market = env
.storage()
.persistent()
.get(&DataKey::Market(parent_market_id))
.ok_or(InsightArenaError::MarketNotFound)?;

if parent_market.is_resolved {
return Err(InsightArenaError::MarketExpired);
}

if !parent_market.outcome_options.contains(required_outcome.clone()) {
return Err(InsightArenaError::InvalidOutcome);
}

let mut depth = 1;
if let Some(parent_cond) = env
.storage()
.persistent()
.get::<_, ConditionalMarket>(&DataKey::ConditionalMarket(parent_market_id))
{
depth = parent_cond.conditional_depth + 1;
if depth > MAX_CONDITIONAL_DEPTH {
return Err(InsightArenaError::ConditionalDepthExceeded);
}
}

let market_id = crate::market::create_market(env, creator, params)?;

let conditional_market = ConditionalMarket::new(
market_id,
parent_market_id,
required_outcome,
depth,
env.ledger().timestamp(),
);
env.storage()
.persistent()
.set(&DataKey::ConditionalMarket(market_id), &conditional_market);

let children_key = DataKey::ConditionalChildren(parent_market_id);
let mut children: Vec<u64> = env
.storage()
.persistent()
.get(&children_key)
.unwrap_or_else(|| Vec::new(env));
children.push_back(market_id);
env.storage().persistent().set(&children_key, &children);

env.storage().persistent().set(&DataKey::ConditionalParent(market_id), &parent_market_id);

Ok(market_id)
}

// TODO: validate_conditional_params
// TODO: link_conditional_market

// ── Activation Logic ──────────────────────────────────────────────────────────

// TODO: check_conditional_activation
// TODO: activate_conditional_market
// TODO: deactivate_conditional_market

// ── Query Functions ───────────────────────────────────────────────────────────

// TODO: get_conditional_markets
// TODO: get_parent_market
// TODO: get_conditional_chain
// TODO: is_conditional_market

// ── Helper Functions ──────────────────────────────────────────────────────────

// TODO: calculate_conditional_depth
// TODO: validate_no_circular_dependency
4 changes: 4 additions & 0 deletions contract/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,8 @@ pub enum InsightArenaError {
/// A supplied argument fails basic validation that is not covered by a more
/// specific error code (e.g. empty strings, zero-length outcome lists).
InvalidInput = 102,

// ── Conditional Markets ───────────────────────────────────────────────────
/// The maximum allowed depth for nested conditional markets has been exceeded.
ConditionalDepthExceeded = 103,
}
13 changes: 13 additions & 0 deletions contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![allow(non_snake_case)]

pub mod analytics;
pub mod conditional;
pub mod config;
pub mod dispute;
pub mod errors;
Expand Down Expand Up @@ -173,6 +174,18 @@ impl InsightArenaContract {
market::cancel_market(&env, caller, market_id)
}

// ── Conditional Markets ───────────────────────────────────────────────────

pub fn create_conditional_market(
env: Env,
creator: Address,
parent_market_id: u64,
required_outcome: Symbol,
params: CreateMarketParams,
) -> Result<u64, InsightArenaError> {
conditional::create_conditional_market(&env, creator, parent_market_id, required_outcome, params)
}

// ── Dispute ───────────────────────────────────────────────────────────────

/// File a dispute within the market's post-resolution dispute window.
Expand Down
53 changes: 53 additions & 0 deletions contract/src/storage_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ pub enum DataKey {
SwapHistory(u64),
/// Keyed by market_id. Stores rolling 24h pool volume.
PoolVolume(u64),

// Conditional Market keys
ConditionalMarket(u64), // market_id -> ConditionalMarket
ConditionalChildren(u64), // parent_market_id -> Vec<u64>
ConditionalParent(u64), // market_id -> u64 (parent_market_id)
ConditionalChain(u64), // market_id -> ConditionalChain
ConditionalDepth(u64), // market_id -> u32
}

#[contracttype]
Expand Down Expand Up @@ -517,3 +524,49 @@ impl InviteCode {
}
}
}

// ── Conditional Market Types ──────────────────────────────────────────────────

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct ConditionalMarket {
pub market_id: u64,
pub parent_market_id: u64,
pub required_outcome: Symbol,
pub is_activated: bool,
pub activation_time: Option<u64>,
pub conditional_depth: u32,
pub created_at: u64,
}

impl ConditionalMarket {
pub fn new(
market_id: u64,
parent_market_id: u64,
required_outcome: Symbol,
conditional_depth: u32,
created_at: u64,
) -> Self {
Self {
market_id,
parent_market_id,
required_outcome,
is_activated: false,
activation_time: None,
conditional_depth,
created_at,
}
}

pub fn activate(&mut self, activation_time: u64) {
self.is_activated = true;
self.activation_time = Some(activation_time);
}
}

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct ConditionalChain {
pub market_ids: Vec<u64>,
pub depth: u32,
}
Loading
Loading