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
1,465 changes: 1,465 additions & 0 deletions contracts/price-oracle-storage/Cargo.lock

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions contracts/price-oracle-storage/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "price-oracle-storage"
version = "0.0.0"
edition = "2021"
publish = false

[lib]
crate-type = ["cdylib", "rlib"]
doctest = false

[dependencies]
soroban-sdk = "20.0.0"

[dev-dependencies]
soroban-sdk = { version = "20.0.0", features = ["testutils"] }

[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true
10 changes: 10 additions & 0 deletions contracts/price-oracle-storage/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.PHONY: build test clean

build:
cargo build --target wasm32-unknown-unknown --release

test:
cargo test

clean:
cargo clean
25 changes: 25 additions & 0 deletions contracts/price-oracle-storage/src/interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use soroban_sdk::{contractclient, Address, Env, Symbol, Vec};
use crate::types::{AssetMeta, PriceData};
use crate::Error;

/// Storage contract interface for price oracle
#[contractclient(name = "PriceOracleStorageClient")]
pub trait PriceOracleStorageTrait {
fn set_admin(env: Env, admin: Address);
fn get_admin(env: Env) -> Result<Address, Error>;
fn is_admin(env: Env, address: Address) -> bool;
fn set_verified_price(env: Env, asset: Symbol, price: PriceData);
fn get_verified_price(env: Env, asset: Symbol) -> Result<PriceData, Error>;
fn set_community_price(env: Env, asset: Symbol, price: PriceData);
fn get_community_price(env: Env, asset: Symbol) -> Result<PriceData, Error>;
fn set_asset_meta(env: Env, asset: Symbol, meta: AssetMeta);
fn get_asset_meta(env: Env, asset: Symbol) -> Result<AssetMeta, Error>;
fn add_asset(env: Env, asset: Symbol) -> Result<(), Error>;
fn get_all_assets(env: Env) -> Vec<Symbol>;
fn get_asset_count(env: Env) -> u32;
fn subscribe(env: Env, callback_contract: Address) -> Result<(), Error>;
fn unsubscribe(env: Env, callback_contract: Address) -> Result<(), Error>;
fn get_subscribers(env: Env) -> Vec<Address>;
fn initialize(env: Env, admin: Address) -> Result<(), Error>;
fn is_initialized(env: Env) -> bool;
}
190 changes: 190 additions & 0 deletions contracts/price-oracle-storage/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#![no_std]

use soroban_sdk::{contract, contractimpl, contracterror, Address, Env, Symbol, Vec};

mod types;
mod interface;

#[cfg(test)]
mod test;

pub use types::{AssetMeta, AssetWeight, DataKey, PriceData};
pub use interface::PriceOracleStorageTrait;

/// Errors for storage operations
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Error {
NotFound = 1,
AlreadyExists = 2,
Unauthorized = 3,
InvalidInput = 4,
}

#[contract]
pub struct PriceOracleStorage;

#[contractimpl]
impl PriceOracleStorageTrait for PriceOracleStorage {
fn set_admin(env: Env, admin: Address) {
env.storage().instance().set(&DataKey::Admin, &admin);
}

fn get_admin(env: Env) -> Result<Address, Error> {
env.storage()
.instance()
.get::<DataKey, Address>(&DataKey::Admin)
.ok_or(Error::NotFound)
}

fn is_admin(env: Env, address: Address) -> bool {
env.storage()
.instance()
.get::<DataKey, Address>(&DataKey::Admin)
.map(|admin| admin == address)
.unwrap_or(false)
}

fn set_verified_price(env: Env, asset: Symbol, price: PriceData) {
env.storage()
.temporary()
.set(&DataKey::VerifiedPrice(asset), &price);
}

fn get_verified_price(env: Env, asset: Symbol) -> Result<PriceData, Error> {
env.storage()
.temporary()
.get::<DataKey, PriceData>(&DataKey::VerifiedPrice(asset))
.ok_or(Error::NotFound)
}

fn set_community_price(env: Env, asset: Symbol, price: PriceData) {
env.storage()
.temporary()
.set(&DataKey::CommunityPrice(asset), &price);
}

fn get_community_price(env: Env, asset: Symbol) -> Result<PriceData, Error> {
env.storage()
.temporary()
.get::<DataKey, PriceData>(&DataKey::CommunityPrice(asset))
.ok_or(Error::NotFound)
}

fn set_asset_meta(env: Env, asset: Symbol, meta: AssetMeta) {
env.storage()
.persistent()
.set(&DataKey::AssetMeta(asset), &meta);
}

fn get_asset_meta(env: Env, asset: Symbol) -> Result<AssetMeta, Error> {
env.storage()
.persistent()
.get::<DataKey, AssetMeta>(&DataKey::AssetMeta(asset))
.ok_or(Error::NotFound)
}

fn add_asset(env: Env, asset: Symbol) -> Result<(), Error> {
let mut assets = env.storage()
.persistent()
.get::<DataKey, Vec<Symbol>>(&DataKey::BaseCurrencyPairs)
.unwrap_or_else(|| Vec::new(&env));

if assets.iter().any(|a| a == asset) {
return Err(Error::AlreadyExists);
}

assets.push_back(asset);
env.storage()
.persistent()
.set(&DataKey::BaseCurrencyPairs, &assets);

Ok(())
}

fn get_all_assets(env: Env) -> Vec<Symbol> {
env.storage()
.persistent()
.get::<DataKey, Vec<Symbol>>(&DataKey::BaseCurrencyPairs)
.unwrap_or_else(|| Vec::new(&env))
}

fn get_asset_count(env: Env) -> u32 {
env.storage()
.persistent()
.get::<DataKey, Vec<Symbol>>(&DataKey::BaseCurrencyPairs)
.map(|assets| assets.len() as u32)
.unwrap_or(0)
}

fn subscribe(env: Env, callback_contract: Address) -> Result<(), Error> {
let mut subscribers = env.storage()
.persistent()
.get::<DataKey, Vec<Address>>(&DataKey::PriceUpdateSubscribers)
.unwrap_or_else(|| Vec::new(&env));

if subscribers.iter().any(|sub| sub == callback_contract) {
return Err(Error::AlreadyExists);
}

subscribers.push_back(callback_contract);
env.storage()
.persistent()
.set(&DataKey::PriceUpdateSubscribers, &subscribers);

Ok(())
}

fn unsubscribe(env: Env, callback_contract: Address) -> Result<(), Error> {
let subscribers = env.storage()
.persistent()
.get::<DataKey, Vec<Address>>(&DataKey::PriceUpdateSubscribers)
.unwrap_or_else(|| Vec::new(&env));

let original_len = subscribers.len();
let filtered = {
let mut f = Vec::new(&env);
for sub in subscribers.iter() {
if sub != callback_contract {
f.push_back(sub);
}
}
f
};

if filtered.len() == original_len {
return Err(Error::NotFound);
}

env.storage()
.persistent()
.set(&DataKey::PriceUpdateSubscribers, &filtered);

Ok(())
}

fn get_subscribers(env: Env) -> Vec<Address> {
env.storage()
.persistent()
.get::<DataKey, Vec<Address>>(&DataKey::PriceUpdateSubscribers)
.unwrap_or_else(|| Vec::new(&env))
}

fn initialize(env: Env, admin: Address) -> Result<(), Error> {
if Self::is_initialized(env.clone()) {
return Err(Error::AlreadyExists);
}

env.storage().instance().set(&DataKey::Initialized, &true);
Self::set_admin(env, admin);

Ok(())
}

fn is_initialized(env: Env) -> bool {
env.storage()
.instance()
.get::<DataKey, bool>(&DataKey::Initialized)
.unwrap_or(false)
}
}
Loading