use soroban_sdk::{
contract, contractimpl, contracterror, Env, Symbol, Address, Vec
};
#[derive(Clone)]
pub struct PriceData {
pub price: i128,
pub timestamp: u64,
pub source: Address,
}
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
Unauthorized = 1,
StalePrice = 2,
}
#[contract]
pub struct OracleContract;
#[contractimpl]
impl OracleContract {
const PRICE_TTL_THRESHOLD: u32 = 50; // ~4 minutes
const PRICE_TTL_EXTEND_TO: u32 = 100; // ~8 minutes
const MAX_PRICE_AGE: u64 = 600; // 10 minutes in seconds
/// Submit a price update (authorized sources only)
pub fn submit_price(
env: Env,
source: Address,
symbol: Symbol,
price: i128,
) -> Result<(), Error> {
source.require_auth();
let price_data = PriceData {
price,
timestamp: env.ledger().timestamp(),
source,
};
env.storage().temporary().set(&symbol, &price_data);
env.storage().temporary().extend_ttl(
&symbol,
Self::PRICE_TTL_THRESHOLD,
Self::PRICE_TTL_EXTEND_TO,
);
Ok(())
}
/// Get current price (returns error if stale)
pub fn get_price(env: Env, symbol: Symbol) -> Result<i128, Error> {
let price_data: Option<PriceData> = env.storage().temporary().get(&symbol);
match price_data {
Some(data) => {
let current_time = env.ledger().timestamp();
let age = current_time - data.timestamp;
if age > Self::MAX_PRICE_AGE {
Err(Error::StalePrice)
} else {
Ok(data.price)
}
}
None => Err(Error::StalePrice),
}
}
/// Get price with metadata
pub fn get_price_data(env: Env, symbol: Symbol) -> Option<PriceData> {
env.storage().temporary().get(&symbol)
}
/// Submit multiple prices in batch
pub fn submit_prices(
env: Env,
source: Address,
symbols: Vec<Symbol>,
prices: Vec<i128>,
) -> Result<(), Error> {
source.require_auth();
let timestamp = env.ledger().timestamp();
for i in 0..symbols.len() {
let symbol = symbols.get(i).unwrap();
let price = prices.get(i).unwrap();
let price_data = PriceData {
price,
timestamp,
source: source.clone(),
};
env.storage().temporary().set(&symbol, &price_data);
env.storage().temporary().extend_ttl(
&symbol,
Self::PRICE_TTL_THRESHOLD,
Self::PRICE_TTL_EXTEND_TO,
);
}
Ok(())
}
}