Skip to content
Open
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
130 changes: 130 additions & 0 deletions contracts/predictify-hybrid/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,86 @@ pub struct MarketCreatedEvent {
pub timestamp: u64,
}

/// Event emitted when a new prediction event is successfully created.
///
/// This event provides comprehensive information about newly created prediction events,
/// including event parameters, outcomes, administrative details, oracle configuration,
/// and timing. Essential for tracking event creation activity and building event indices.
///
/// # Event Data
///
/// Contains all critical event creation parameters:
/// - Event identification and description details
/// - Available outcomes for prediction
/// - Administrative and timing information
/// - Oracle provider information
/// - Creation timestamp for chronological ordering
///
/// # Example Usage
///
/// ```rust
/// # use soroban_sdk::{Env, Address, Symbol, String, Vec};
/// # use predictify_hybrid::events::EventCreatedEvent;
/// # let env = Env::default();
/// # let admin = Address::generate(&env);
///
/// // Event creation event data
/// let event = EventCreatedEvent {
/// event_id: Symbol::new(&env, "evt_btc_100k"),
/// description: String::from_str(&env, "Will Bitcoin reach $100,000 by end of 2024?"),
/// outcomes: vec![
/// &env,
/// String::from_str(&env, "Yes"),
/// String::from_str(&env, "No")
/// ],
/// creator: admin.clone(),
/// end_time: 1735689600, // Dec 31, 2024
/// oracle_provider: String::from_str(&env, "Reflector"),
/// timestamp: env.ledger().timestamp(),
/// };
///
/// // Event provides complete event context
/// println!("New event: {}", event.description.to_string());
/// println!("Event ID: {}", event.event_id.to_string());
/// println!("Outcomes: {} options", event.outcomes.len());
/// println!("Ends: {}", event.end_time);
/// println!("Oracle: {}", event.oracle_provider.to_string());
/// ```
///
/// # Integration Points
///
/// - **Event Indexing**: Build searchable event directories
/// - **Activity Feeds**: Display recent event creation activity
/// - **Analytics**: Track event creation patterns and trends
/// - **Notifications**: Alert users about new events in categories of interest
/// - **Audit Trails**: Maintain complete record of event creation
///
/// # Event Timing
///
/// Emitted immediately after successful event creation, providing:
/// - Real-time notification of new events
/// - Chronological ordering via timestamp
/// - Immediate availability for user interfaces
/// - Historical record for analytics and reporting
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EventCreatedEvent {
/// Unique event ID
pub event_id: Symbol,
/// Event description/question
pub description: String,
/// Available outcomes for prediction
pub outcomes: Vec<String>,
/// Event creator (admin)
pub creator: Address,
/// Event end time (Unix timestamp)
pub end_time: u64,
/// Oracle provider type (e.g., "Reflector", "Pyth")
pub oracle_provider: String,
/// Creation timestamp
pub timestamp: u64,
}

/// Event emitted when a user successfully casts a vote on a prediction market.
///
/// This event captures all details of voting activity, including voter identity,
Expand Down Expand Up @@ -1237,6 +1317,56 @@ impl EventEmitter {
Self::store_event(env, &symbol_short!("mkt_crt"), &event);
}

/// Emit event created event when a new prediction event is created.
///
/// This function emits an event when an admin successfully creates a new
/// prediction event, recording all relevant details for transparency and indexing.
///
/// # Parameters
///
/// - `env` - Soroban environment
/// - `event_id` - Unique event identifier
/// - `description` - Event description/question
/// - `outcomes` - Vector of possible outcomes
/// - `creator` - Admin address that created the event
/// - `end_time` - Unix timestamp when event ends
/// - `oracle_provider` - Name of the oracle provider (e.g., "Reflector")
///
/// # Example
///
/// ```rust
/// EventEmitter::emit_event_created(
/// &env,
/// &event_id,
/// &description,
/// &outcomes,
/// &admin,
/// end_time,
/// &String::from_str(&env, "Reflector"),
/// );
/// ```
pub fn emit_event_created(
env: &Env,
event_id: &Symbol,
description: &String,
outcomes: &Vec<String>,
creator: &Address,
end_time: u64,
oracle_provider: &String,
) {
let event = EventCreatedEvent {
event_id: event_id.clone(),
description: description.clone(),
outcomes: outcomes.clone(),
creator: creator.clone(),
end_time,
oracle_provider: oracle_provider.clone(),
timestamp: env.ledger().timestamp(),
};

Self::store_event(env, &symbol_short!("evt_crt"), &event);
}

/// Emit vote cast event
pub fn emit_vote_cast(
env: &Env,
Expand Down
189 changes: 189 additions & 0 deletions contracts/predictify-hybrid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,195 @@ impl PredictifyHybrid {
market_id
}

/// Creates a new prediction event with specified parameters and oracle configuration.
///
/// This function allows authorized administrators to create prediction events
/// with custom descriptions, possible outcomes, end time, and oracle integration.
/// Each event gets a unique identifier and is stored in persistent contract storage.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The administrator address creating the event (must be authorized)
/// * `description` - The event description/question (must be non-empty, 10-500 chars)
/// * `end_time` - Unix timestamp when the event ends (must be in the future)
/// * `outcomes` - Vector of possible outcomes (minimum 2 required, all non-empty)
/// * `oracle_config` - Configuration for oracle integration (Reflector, Pyth, etc.)
///
/// # Returns
///
/// Returns a unique `Symbol` that serves as the event identifier for all future operations.
///
/// # Panics
///
/// This function will panic with specific errors if:
/// - `Error::Unauthorized` - Caller is not the contract admin
/// - `Error::InvalidQuestion` - Description is empty or invalid length
/// - `Error::InvalidOutcomes` - Less than 2 outcomes or any outcome is empty
/// - `Error::InvalidDuration` - End time is not in the future
/// - `Error::InvalidOracleConfig` - Oracle configuration is invalid
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address, String, Vec};
/// # use predictify_hybrid::{PredictifyHybrid, OracleConfig, OracleProvider};
/// # let env = Env::default();
/// # let admin = Address::generate(&env);
///
/// let description = String::from_str(&env, "Will Bitcoin reach $100,000 by 2024?");
/// let outcomes = vec![
/// String::from_str(&env, "Yes"),
/// String::from_str(&env, "No")
/// ];
/// let end_time = env.ledger().timestamp() + (30 * 24 * 60 * 60); // 30 days
/// let oracle_config = OracleConfig {
/// provider: OracleProvider::Reflector,
/// feed_id: String::from_str(&env, "BTC/USD"),
/// threshold: 100_000_00,
/// comparison: String::from_str(&env, "gt"),
/// };
///
/// let event_id = PredictifyHybrid::create_event(
/// env.clone(),
/// admin,
/// description,
/// end_time,
/// outcomes,
/// oracle_config
/// );
/// ```
///
/// # Event State
///
/// New events are created in `EventStatus::Active` state, allowing immediate betting.
/// The event will automatically transition to `EventStatus::Pending` when the end time expires.
///
/// # Security
///
/// - Only authenticated admins can create events
/// - All input parameters are validated before storage
/// - End time must be strictly in the future
/// - Oracle configuration is validated for supported providers
pub fn create_event(
env: Env,
admin: Address,
description: String,
end_time: u64,
outcomes: Vec<String>,
oracle_config: OracleConfig,
) -> Symbol {
// Authenticate that the caller is the admin
admin.require_auth();

// Verify the caller is an admin
let stored_admin: Address = env
.storage()
.persistent()
.get(&Symbol::new(&env, "Admin"))
.unwrap_or_else(|| {
panic!("Admin not set");
});

if admin != stored_admin {
panic_with_error!(env, Error::Unauthorized);
}

// Validate description
if description.is_empty() {
panic_with_error!(env, Error::InvalidQuestion);
}
if description.len() < 10 {
panic_with_error!(env, Error::InvalidQuestion);
}

// Validate outcomes
if outcomes.len() < 2 {
panic_with_error!(env, Error::InvalidOutcomes);
}

// Validate each outcome is non-empty
for outcome in outcomes.iter() {
if outcome.is_empty() {
panic_with_error!(env, Error::InvalidOutcome);
}
}

// Validate end time is in the future
if end_time <= env.ledger().timestamp() {
panic_with_error!(env, Error::InvalidDuration);
}

// Validate oracle configuration
if let Err(e) = oracle_config.validate(&env) {
panic_with_error!(env, e);
}

// Generate unique event ID
let event_id = storage::EventStorage::generate_event_id(&env, &admin);

// Create the event
let event = types::Event::new(
&env,
event_id.clone(),
admin.clone(),
description.clone(),
end_time,
outcomes.clone(),
oracle_config.clone(),
);

// Store the event
if let Err(e) = storage::EventStorage::store_event(&env, &event) {
panic_with_error!(env, e);
}

// Get oracle provider name
let oracle_provider = match oracle_config.provider {
types::OracleProvider::Reflector => String::from_str(&env, "Reflector"),
types::OracleProvider::Pyth => String::from_str(&env, "Pyth"),
types::OracleProvider::BandProtocol => String::from_str(&env, "BandProtocol"),
types::OracleProvider::DIA => String::from_str(&env, "DIA"),
};

// Emit event created event
EventEmitter::emit_event_created(
&env,
&event_id,
&description,
&outcomes,
&admin,
end_time,
&oracle_provider,
);

event_id
}

/// Retrieves a prediction event by its unique identifier.
///
/// This function provides read-only access to event data including
/// description, outcomes, status, and oracle configuration.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `event_id` - Unique identifier of the event to retrieve
///
/// # Returns
///
/// Returns the `Event` struct if found, panics if event doesn't exist.
///
/// # Panics
///
/// This function will panic with `Error::MarketNotFound` if the event doesn't exist.
pub fn get_event(env: Env, event_id: Symbol) -> types::Event {
match storage::EventStorage::get_event(&env, &event_id) {
Ok(event) => event,
Err(e) => panic_with_error!(env, e),
}
}

/// Allows users to vote on a market outcome by staking tokens.
///
/// This function enables users to participate in prediction markets by voting
Expand Down
Loading
Loading