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