The Predictify Hybrid contract now features a comprehensive, organized type system that centralizes all data structures and provides better organization, validation, and maintainability. This document outlines the architecture, usage patterns, and best practices for working with the types system.
Types are organized into logical categories for better understanding and maintenance:
- Oracle Types - Oracle providers, configurations, and data structures
- Market Types - Market data structures and state management
- Price Types - Price data and validation structures
- Validation Types - Input validation and business logic types
- Utility Types - Helper types and conversion utilities
OracleProvider Enum
pub enum OracleProvider {
BandProtocol,
DIA,
Reflector,
Pyth,
}OracleConfig Struct
pub struct OracleConfig {
pub provider: OracleProvider,
pub feed_id: String,
pub threshold: i128,
pub comparison: String,
}Market Struct
pub struct Market {
pub admin: Address,
pub question: String,
pub outcomes: Vec<String>,
pub end_time: u64,
pub oracle_config: OracleConfig,
// ... other fields
}PythPrice Struct
pub struct PythPrice {
pub price: i128,
pub conf: u64,
pub expo: i32,
pub publish_time: u64,
}ReflectorPriceData Struct
pub struct ReflectorPriceData {
pub price: i128,
pub timestamp: u64,
}use types::{OracleProvider, OracleConfig};
let oracle_config = OracleConfig::new(
OracleProvider::Pyth,
String::from_str(&env, "BTC/USD"),
2500000, // $25,000 threshold
String::from_str(&env, "gt"), // greater than
);
// Validate the configuration
oracle_config.validate(&env)?;use types::{Market, OracleConfig, OracleProvider};
let market = Market::new(
&env,
admin,
question,
outcomes,
end_time,
oracle_config,
);
// Validate market parameters
market.validate(&env)?;use types::MarketState;
let state = MarketState::from_market(&market, current_time);
if state.is_active() {
// Market is accepting votes
} else if state.has_ended() {
// Market has ended
} else if state.is_resolved() {
// Market is resolved
}use types::OracleResult;
let result = OracleResult::price(2500000);
if result.is_available() {
if let Some(price) = result.get_price() {
// Use the price
}
}All types include built-in validation methods:
// Oracle configuration validation
oracle_config.validate(&env)?;
// Market validation
market.validate(&env)?;
// Price validation
pyth_price.validate()?;The types module provides validation helper functions:
use types::validation;
// Validate oracle provider
validation::validate_oracle_provider(&OracleProvider::Pyth)?;
// Validate price
validation::validate_price(2500000)?;
// Validate stake
validation::validate_stake(stake, min_stake)?;
// Validate duration
validation::validate_duration(30)?;use types::conversion;
// Convert string to oracle provider
let provider = conversion::string_to_oracle_provider("pyth")
.ok_or(Error::InvalidOracleConfig)?;
// Convert oracle provider to string
let provider_name = conversion::oracle_provider_to_string(&provider);
// Validate comparison operator
conversion::validate_comparison(&comparison, &env)?;// Check if market is active
if market.is_active(current_time) {
// Accept votes
}
// Check if market has ended
if market.has_ended(current_time) {
// Resolve market
}
// Check if market is resolved
if market.is_resolved() {
// Allow claims
}// Get user's vote
let user_vote = market.get_user_vote(&user);
// Get user's stake
let user_stake = market.get_user_stake(&user);
// Check if user has claimed
let has_claimed = market.has_user_claimed(&user);
// Get user's dispute stake
let dispute_stake = market.get_user_dispute_stake(&user);// Add vote and stake
market.add_vote(user, outcome, stake);
// Add dispute stake
market.add_dispute_stake(user, stake);
// Mark user as claimed
market.mark_claimed(user);
// Set oracle result
market.set_oracle_result(result);
// Set winning outcome
market.set_winning_outcome(outcome);
// Mark fees as collected
market.mark_fees_collected();// Get total dispute stakes
let total_disputes = market.total_dispute_stakes();
// Get winning stake total
let winning_total = market.winning_stake_total();// Check if provider is supported
if oracle_provider.is_supported() {
// Use the provider
}
// Get provider name
let name = oracle_provider.name();
// Get default feed format
let format = oracle_provider.default_feed_format();// Check comparison operators
if oracle_config.is_greater_than(&env) {
// Handle greater than comparison
} else if oracle_config.is_less_than(&env) {
// Handle less than comparison
} else if oracle_config.is_equal_to(&env) {
// Handle equal to comparison
}let pyth_price = PythPrice::new(2500000, 1000, -2, timestamp);
// Get price in cents
let price_cents = pyth_price.price_in_cents();
// Check if price is stale
if pyth_price.is_stale(current_time, max_age) {
// Handle stale price
}
// Validate price data
pyth_price.validate()?;let reflector_price = ReflectorPriceData::new(2500000, timestamp);
// Get price in cents
let price_cents = reflector_price.price_in_cents();
// Check if price is stale
if reflector_price.is_stale(current_time, max_age) {
// Handle stale price
}
// Validate price data
reflector_price.validate()?;let params = MarketCreationParams::new(
admin,
question,
outcomes,
duration_days,
oracle_config,
);
// Validate all parameters
params.validate(&env)?;
// Calculate end time
let end_time = params.calculate_end_time(&env);let vote_params = VoteParams::new(user, outcome, stake);
// Validate vote parameters
vote_params.validate(&env, &market)?;// ❌ Don't skip validation
let market = Market::new(&env, admin, question, outcomes, end_time, oracle_config);
// ✅ Always validate
let market = Market::new(&env, admin, question, outcomes, end_time, oracle_config);
market.validate(&env)?;// ❌ Manual state checking
if current_time < market.end_time && market.winning_outcome.is_none() {
// Market is active
}
// ✅ Use type-safe methods
if market.is_active(current_time) {
// Market is active
}// ❌ Manual calculations
let mut total = 0;
for (user, outcome) in market.votes.iter() {
if &outcome == winning_outcome {
total += market.stakes.get(user.clone()).unwrap_or(0);
}
}
// ✅ Use built-in methods
let total = market.winning_stake_total();// ❌ Manual validation
if stake < min_stake {
return Err(Error::InsufficientStake);
}
// ✅ Use validation helpers
validation::validate_stake(stake, min_stake)?;// ❌ Direct access
let price = oracle_result.price;
// ✅ Safe access
if let Some(price) = oracle_result.get_price() {
// Use the price
}The types module includes comprehensive tests:
#[test]
fn test_oracle_provider() {
let provider = OracleProvider::Pyth;
assert_eq!(provider.name(), "Pyth Network");
assert!(provider.is_supported());
}
#[test]
fn test_market_creation() {
let market = Market::new(&env, admin, question, outcomes, end_time, oracle_config);
assert!(market.is_active(current_time));
assert!(!market.is_resolved());
}
#[test]
fn test_validation_helpers() {
assert!(validation::validate_oracle_provider(&OracleProvider::Pyth).is_ok());
assert!(validation::validate_price(2500000).is_ok());
}-
Replace direct struct creation:
// Old let market = Market { /* fields */ }; // New let market = Market::new(&env, admin, question, outcomes, end_time, oracle_config);
-
Use validation methods:
// Old if threshold <= 0 { return Err(Error::InvalidThreshold); } // New oracle_config.validate(&env)?;
-
Use type-safe operations:
// Old if current_time < market.end_time { /* active */ } // New if market.is_active(current_time) { /* active */ }
| Type | Purpose | Key Methods |
|---|---|---|
OracleProvider |
Oracle service enumeration | name(), is_supported(), default_feed_format() |
OracleConfig |
Oracle configuration | new(), validate(), is_supported(), is_greater_than() |
PythPrice |
Pyth price data | new(), price_in_cents(), is_stale(), validate() |
ReflectorPriceData |
Reflector price data | new(), price_in_cents(), is_stale(), validate() |
| Type | Purpose | Key Methods |
|---|---|---|
Market |
Market data structure | new(), validate(), is_active(), add_vote() |
MarketState |
Market state enumeration | from_market(), is_active(), has_ended() |
MarketCreationParams |
Market creation parameters | new(), validate(), calculate_end_time() |
VoteParams |
Vote parameters | new(), validate() |
| Type | Purpose | Key Methods |
|---|---|---|
OracleResult |
Oracle result wrapper | price(), unavailable(), is_available(), get_price() |
ReflectorAsset |
Reflector asset types | stellar(), other(), is_stellar(), is_other() |
| Function | Purpose | Parameters |
|---|---|---|
validate_oracle_provider() |
Validate oracle provider | provider: &OracleProvider |
validate_price() |
Validate price value | price: i128 |
validate_stake() |
Validate stake amount | stake: i128, min_stake: i128 |
validate_duration() |
Validate duration | duration_days: u32 |
| Function | Purpose | Parameters |
|---|---|---|
string_to_oracle_provider() |
Convert string to provider | s: &str |
oracle_provider_to_string() |
Convert provider to string | provider: &OracleProvider |
validate_comparison() |
Validate comparison operator | comparison: &String, env: &Env |
- Type Serialization: Proper serialization/deserialization support
- Type Metrics: Collection and reporting of type usage statistics
- Type Validation: Enhanced validation with custom rules
- Type Events: Event emission for type state changes
- Type Localization: Support for multiple languages in type messages
The new types system provides a robust foundation for managing data structures in the Predictify Hybrid contract. By following the patterns and best practices outlined in this document, developers can create more maintainable, type-safe, and well-organized code.