Skip to content

Commit 57ae447

Browse files
Merge pull request #405 from phertyameen/feat/oracleRegistry
Implement Soroban Oracle Integration with Data Feeds and Aggregation
2 parents c44dd16 + 9cf5f06 commit 57ae447

File tree

1 file changed

+268
-0
lines changed

1 file changed

+268
-0
lines changed
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
#![no_std]
2+
use soroban_sdk::{contractimpl, contracttype, Address, Env, Vec, Map, BytesN, Symbol, RawVal};
3+
4+
#[derive(Clone)]
5+
#[contracttype]
6+
pub enum OracleType {
7+
Price,
8+
Weather,
9+
IoT,
10+
Verification,
11+
Market,
12+
Random,
13+
Custom,
14+
}
15+
16+
#[derive(Clone)]
17+
#[contracttype]
18+
pub enum FeedStatus {
19+
Active,
20+
Paused,
21+
Deprecated,
22+
}
23+
24+
#[derive(Clone)]
25+
#[contracttype]
26+
pub enum AggregationMethod {
27+
Median,
28+
Mean,
29+
WeightedAverage,
30+
MajorityVote,
31+
TrimmedMean,
32+
}
33+
34+
#[derive(Clone)]
35+
#[contracttype]
36+
pub struct OracleInfo {
37+
pub oracle: Address,
38+
pub name: Symbol,
39+
pub oracle_type: OracleType,
40+
pub data_types: Vec<Symbol>,
41+
pub accuracy: u32,
42+
pub avg_response_time: u64,
43+
pub total_requests: u64,
44+
pub success_count: u64,
45+
pub failure_count: u64,
46+
pub reputation: u64,
47+
pub stake: u64,
48+
pub registered_at: u64,
49+
pub active: bool,
50+
pub last_updated: u64,
51+
}
52+
53+
#[derive(Clone)]
54+
#[contracttype]
55+
pub struct DataFeed {
56+
pub feed_id: u64,
57+
pub name: Symbol,
58+
pub data_type: Symbol,
59+
pub update_frequency: u64,
60+
pub aggregation_method: AggregationMethod,
61+
pub min_oracles: u32,
62+
pub current_value: i128,
63+
pub last_updated: u64,
64+
pub deviation_threshold: u64,
65+
pub status: FeedStatus,
66+
pub subscribed_oracles: Vec<Address>,
67+
}
68+
69+
#[derive(Clone)]
70+
#[contracttype]
71+
pub struct DataPoint {
72+
pub point_id: u64,
73+
pub oracle: Address,
74+
pub value: i128,
75+
pub confidence: u32,
76+
pub observed_at: u64,
77+
pub submitted_at: u64,
78+
pub metadata: Symbol,
79+
}
80+
81+
#[derive(Clone)]
82+
#[contracttype]
83+
pub struct OracleRequest {
84+
pub request_id: u64,
85+
pub requester: Address,
86+
pub request_type: Symbol,
87+
pub required_oracles: u32,
88+
pub responses: u32,
89+
pub created_at: u64,
90+
pub deadline: u64,
91+
pub min_confidence: u32,
92+
pub fulfilled: bool,
93+
}
94+
95+
#[derive(Clone)]
96+
#[contracttype]
97+
pub struct OracleResponse {
98+
pub oracle: Address,
99+
pub value: i128,
100+
pub confidence: u32,
101+
pub timestamp: u64,
102+
}
103+
104+
pub struct OracleContract;
105+
106+
#[contractimpl]
107+
impl OracleContract {
108+
// Initialize admin
109+
pub fn init(env: Env, admin: Address) {
110+
env.storage().set(&Symbol::short("admin"), &admin);
111+
env.storage().set(&Symbol::short("feed_counter"), &0u64);
112+
env.storage().set(&Symbol::short("request_counter"), &0u64);
113+
env.storage().set(&Symbol::short("point_counter"), &0u64);
114+
}
115+
116+
fn get_admin(env: &Env) -> Address {
117+
env.storage().get_unchecked::<Symbol, Address>(&Symbol::short("admin")).unwrap()
118+
}
119+
120+
fn assert_admin(env: &Env, caller: &Address) {
121+
let admin = Self::get_admin(env);
122+
if caller != &admin {
123+
panic!("Not admin");
124+
}
125+
}
126+
127+
// Register an oracle
128+
pub fn register_oracle(
129+
env: Env,
130+
oracle: Address,
131+
name: Symbol,
132+
oracle_type: OracleType,
133+
data_types: Vec<Symbol>,
134+
stake: u64,
135+
) {
136+
let now = env.ledger().timestamp();
137+
let info = OracleInfo {
138+
oracle: oracle.clone(),
139+
name,
140+
oracle_type,
141+
data_types,
142+
accuracy: 100,
143+
avg_response_time: 0,
144+
total_requests: 0,
145+
success_count: 0,
146+
failure_count: 0,
147+
reputation: stake,
148+
stake,
149+
registered_at: now,
150+
active: true,
151+
last_updated: now,
152+
};
153+
env.storage().set(&Self::oracle_key(&oracle), &info);
154+
}
155+
156+
pub fn deactivate_oracle(env: Env, admin: Address, oracle: Address) {
157+
Self::assert_admin(&env, &admin);
158+
let mut info: OracleInfo = env.storage().get_unchecked(&Self::oracle_key(&oracle)).unwrap();
159+
info.active = false;
160+
env.storage().set(&Self::oracle_key(&oracle), &info);
161+
}
162+
163+
fn oracle_key(oracle: &Address) -> Symbol {
164+
Symbol::short(&format!("oracle_{}", oracle.to_string()))
165+
}
166+
167+
// Create a feed
168+
pub fn create_feed(
169+
env: Env,
170+
admin: Address,
171+
name: Symbol,
172+
data_type: Symbol,
173+
update_frequency: u64,
174+
aggregation_method: AggregationMethod,
175+
min_oracles: u32,
176+
deviation_threshold: u64,
177+
) -> u64 {
178+
Self::assert_admin(&env, &admin);
179+
let mut feed_counter: u64 = env.storage().get_unchecked(&Symbol::short("feed_counter")).unwrap();
180+
feed_counter += 1;
181+
let feed = DataFeed {
182+
feed_id: feed_counter,
183+
name,
184+
data_type,
185+
update_frequency,
186+
aggregation_method,
187+
min_oracles,
188+
current_value: 0,
189+
last_updated: 0,
190+
deviation_threshold,
191+
status: FeedStatus::Active,
192+
subscribed_oracles: Vec::new(&env),
193+
};
194+
env.storage().set(&Symbol::short(&format!("feed_{}", feed_counter)), &feed);
195+
env.storage().set(&Symbol::short("feed_counter"), &feed_counter);
196+
feed_counter
197+
}
198+
199+
pub fn subscribe_oracle(env: Env, feed_id: u64, oracle: Address) {
200+
let mut feed: DataFeed = env.storage().get_unchecked(&Symbol::short(&format!("feed_{}", feed_id))).unwrap();
201+
feed.subscribed_oracles.push_back(oracle);
202+
env.storage().set(&Symbol::short(&format!("feed_{}", feed_id)), &feed);
203+
}
204+
205+
// Submit a data point
206+
pub fn submit_data_point(
207+
env: Env,
208+
feed_id: u64,
209+
oracle: Address,
210+
value: i128,
211+
confidence: u32,
212+
metadata: Symbol,
213+
) {
214+
if confidence > 100 {
215+
panic!("Invalid confidence");
216+
}
217+
let mut point_counter: u64 = env.storage().get_unchecked(&Symbol::short("point_counter")).unwrap();
218+
point_counter += 1;
219+
220+
let now = env.ledger().timestamp();
221+
let point = DataPoint {
222+
point_id: point_counter,
223+
oracle: oracle.clone(),
224+
value,
225+
confidence,
226+
observed_at: now,
227+
submitted_at: now,
228+
metadata,
229+
};
230+
let mut points: Vec<DataPoint> = env.storage().get(&Symbol::short(&format!("points_{}", feed_id))).unwrap_or(Vec::new(&env));
231+
points.push_back(point);
232+
env.storage().set(&Symbol::short(&format!("points_{}", feed_id)), &points);
233+
env.storage().set(&Symbol::short("point_counter"), &point_counter);
234+
235+
// Aggregate if enough points
236+
let feed: DataFeed = env.storage().get_unchecked(&Symbol::short(&format!("feed_{}", feed_id))).unwrap();
237+
if points.len() >= feed.min_oracles as usize {
238+
Self::aggregate_feed(env, feed_id);
239+
}
240+
}
241+
242+
fn aggregate_feed(env: Env, feed_id: u64) {
243+
let points: Vec<DataPoint> = env.storage().get_unchecked(&Symbol::short(&format!("points_{}", feed_id))).unwrap();
244+
if points.len() == 0 {
245+
return;
246+
}
247+
let mut sum: i128 = 0;
248+
for p in points.iter() {
249+
sum += p.value;
250+
}
251+
let aggregated = sum / points.len() as i128;
252+
let mut feed: DataFeed = env.storage().get_unchecked(&Symbol::short(&format!("feed_{}", feed_id))).unwrap();
253+
let old_value = feed.current_value;
254+
feed.current_value = aggregated;
255+
feed.last_updated = env.ledger().timestamp();
256+
env.storage().set(&Symbol::short(&format!("feed_{}", feed_id)), &feed);
257+
258+
// clear points
259+
env.storage().set(&Symbol::short(&format!("points_{}", feed_id)), &Vec::new(&env));
260+
261+
// Check deviation
262+
let deviation = if old_value != 0 { ((aggregated - old_value).abs() * 100) / old_value.abs() } else { 0 };
263+
if deviation as u64 >= feed.deviation_threshold {
264+
// Emit event (placeholder)
265+
env.events().publish((Symbol::short("DeviationTriggered"),), (feed_id, old_value, aggregated));
266+
}
267+
}
268+
}

0 commit comments

Comments
 (0)