-
Notifications
You must be signed in to change notification settings - Fork 54
Expand file tree
/
Copy pathoracle_contract.rs
More file actions
133 lines (107 loc) · 4.19 KB
/
Copy pathoracle_contract.rs
File metadata and controls
133 lines (107 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Decentralized Oracle Contract Interface
//!
//! This is a sample Soroban smart contract implementing a decentralized oracle
//! for price feeds. It provides a standard interface for querying asset prices.
//!
//! The contract maintains a mapping of asset pairs to their prices, updated by
//! authorized oracles. Prices are stored as i128 values representing the price
//! with appropriate decimal precision (e.g., 1000000 for $1.00 with 6 decimals).
//!
//! Standard Interface:
//! - get_price(base_asset: Symbol, quote_asset: Symbol) -> i128
//! - set_price(base_asset: Symbol, quote_asset: Symbol, price: i128)
//! - get_supported_assets() -> Vec<Symbol>
//!
//! Security considerations:
//! - Only authorized oracles can update prices
//! - Prices should be updated regularly to remain relevant
//! - Consider using multiple oracles for decentralization
#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, vec, Env, Symbol, Vec, Address};
#[contracttype]
pub enum DataKey {
Price(Symbol, Symbol), // (base_asset, quote_asset) -> price
AuthorizedOracle(Address),
SupportedAssets,
}
#[contract]
pub struct OracleContract;
#[contractimpl]
impl OracleContract {
/// Initialize the oracle contract with an initial authorized oracle
pub fn initialize(env: Env, admin: Address) {
env.storage().instance().set(&DataKey::AuthorizedOracle(admin), &true);
}
/// Get the price of base_asset in terms of quote_asset
/// Returns the price as i128 (e.g., 1000000 = $1.00 with 6 decimals)
pub fn get_price(env: Env, base_asset: Symbol, quote_asset: Symbol) -> i128 {
env.storage().instance()
.get(&DataKey::Price(base_asset, quote_asset))
.unwrap_or(0)
}
/// Set the price for an asset pair (only authorized oracles)
pub fn set_price(env: Env, oracle: Address, base_asset: Symbol, quote_asset: Symbol, price: i128) {
// Check if oracle is authorized
oracle.require_auth();
let is_authorized: bool = env.storage().instance()
.get(&DataKey::AuthorizedOracle(oracle))
.unwrap_or(false);
if !is_authorized {
panic!("Unauthorized oracle");
}
env.storage().instance().set(&DataKey::Price(base_asset, quote_asset), &price);
// Update supported assets list
let mut assets: Vec<Symbol> = env.storage().instance()
.get(&DataKey::SupportedAssets)
.unwrap_or(vec![&env]);
if !assets.contains(&base_asset) {
assets.push_back(base_asset);
}
if !assets.contains("e_asset) {
assets.push_back(quote_asset);
}
env.storage().instance().set(&DataKey::SupportedAssets, &assets);
}
/// Get list of supported assets
pub fn get_supported_assets(env: Env) -> Vec<Symbol> {
env.storage().instance()
.get(&DataKey::SupportedAssets)
.unwrap_or(vec![&env])
}
/// Add an authorized oracle (admin only)
pub fn add_oracle(env: Env, admin: Address, new_oracle: Address) {
admin.require_auth();
let is_admin: bool = env.storage().instance()
.get(&DataKey::AuthorizedOracle(admin))
.unwrap_or(false);
if !is_admin {
panic!("Unauthorized admin");
}
env.storage().instance().set(&DataKey::AuthorizedOracle(new_oracle), &true);
}
}
#[cfg(test)]
mod test {
use super::*;
use soroban_sdk::testutils::{Address as _, Ledger};
#[test]
fn test_oracle() {
let env = Env::default();
env.mock_all_auths();
let admin = Address::generate(&env);
let oracle = Address::generate(&env);
let contract_id = env.register_contract(None, OracleContract);
let client = OracleContractClient::new(&env, &contract_id);
// Initialize
client.initialize(&admin);
// Add oracle
client.add_oracle(&admin, &oracle);
// Set price
let base = symbol_short!("USD");
let quote = symbol_short!("XLM");
client.set_price(&oracle, &base, "e, &1000000);
// Get price
let price = client.get_price(&base, "e);
assert_eq!(price, 1000000);
}
}