11#![ no_std]
2+ use access_control:: Role ;
23use predifi_errors:: PrediFiError ;
34use soroban_sdk:: { contract, contractevent, contractimpl, contracttype, token, Address , Env } ;
45
@@ -70,6 +71,7 @@ pub enum DataKey {
7071 FeeBps , // Fee in basis points (1/100 of a percent)
7172 Treasury , // Protocol treasury address
7273 CollectedFees ( u64 ) , // PoolId -> Collected fee amount
74+ AccessControlAddress , // Access control contract address
7375}
7476
7577#[ contracttype]
@@ -84,6 +86,49 @@ pub struct PredifiContract;
8486
8587#[ contractimpl]
8688impl PredifiContract {
89+ /// Get the access control contract address
90+ ///
91+ /// # Returns
92+ /// The address of the access control contract
93+ ///
94+ /// # Errors
95+ /// * `NotInitialized` - If access control contract is not set
96+ fn get_access_control_address ( env : & Env ) -> Result < Address , PrediFiError > {
97+ env. storage ( )
98+ . instance ( )
99+ . get ( & DataKey :: AccessControlAddress )
100+ . ok_or ( PrediFiError :: NotInitialized )
101+ }
102+
103+ /// Get the access control client
104+ ///
105+ /// # Returns
106+ /// The AccessControlClient instance
107+ fn get_access_control_client ( env : & Env ) -> access_control:: AccessControlClient < ' _ > {
108+ let access_control_addr = Self :: get_access_control_address ( env) . unwrap ( ) ;
109+ access_control:: AccessControlClient :: new ( env, & access_control_addr)
110+ }
111+
112+ /// Set the access control contract address (only callable once)
113+ ///
114+ /// # Arguments
115+ /// * `access_control_address` - The address of the access control contract
116+ ///
117+ /// # Errors
118+ /// * `AlreadyInitialized` - If access control contract is already set
119+ pub fn set_access_control (
120+ env : Env ,
121+ access_control_address : Address ,
122+ ) -> Result < ( ) , PrediFiError > {
123+ if env. storage ( ) . instance ( ) . has ( & DataKey :: AccessControlAddress ) {
124+ return Err ( PrediFiError :: AlreadyInitialized ) ;
125+ }
126+ env. storage ( )
127+ . instance ( )
128+ . set ( & DataKey :: AccessControlAddress , & access_control_address) ;
129+ Ok ( ( ) )
130+ }
131+
87132 /// Initialize the contract.
88133 ///
89134 /// Sets up the initial pool ID counter, fee basis points, and treasury address.
@@ -107,14 +152,24 @@ impl PredifiContract {
107152 /// Set the protocol fee in basis points.
108153 ///
109154 /// # Arguments
155+ /// * `caller` - The address calling the function
110156 /// * `fee_bps` - Fee in basis points (e.g., 100 = 1%)
111- pub fn set_fee_bps ( env : Env , fee_bps : u32 ) {
112- // TODO: Add access control to restrict who can call this
157+ ///
158+ /// # Errors
159+ /// * `InsufficientPermissions` - If caller doesn't have Admin role
160+ pub fn set_fee_bps ( env : Env , caller : Address , fee_bps : u32 ) -> Result < ( ) , PrediFiError > {
161+ // Check if caller has Admin role
162+ let access_control_client = Self :: get_access_control_client ( & env) ;
163+ if !access_control_client. has_role ( & caller, & Role :: Admin ) {
164+ return Err ( PrediFiError :: InsufficientPermissions ) ;
165+ }
166+
113167 env. storage ( ) . instance ( ) . set ( & DataKey :: FeeBps , & fee_bps) ;
114168 SetFeeBpsEvent {
115169 new_fee_bps : fee_bps,
116170 }
117171 . publish ( & env) ;
172+ Ok ( ( ) )
118173 }
119174
120175 /// Get the current protocol fee in basis points.
@@ -128,14 +183,24 @@ impl PredifiContract {
128183 /// Set the treasury address.
129184 ///
130185 /// # Arguments
186+ /// * `caller` - The address calling the function
131187 /// * `treasury` - New treasury address
132- pub fn set_treasury ( env : Env , treasury : Address ) {
133- // TODO: Add access control to restrict who can call this
188+ ///
189+ /// # Errors
190+ /// * `InsufficientPermissions` - If caller doesn't have Admin role
191+ pub fn set_treasury ( env : Env , caller : Address , treasury : Address ) -> Result < ( ) , PrediFiError > {
192+ // Check if caller has Admin role
193+ let access_control_client = Self :: get_access_control_client ( & env) ;
194+ if !access_control_client. has_role ( & caller, & Role :: Admin ) {
195+ return Err ( PrediFiError :: InsufficientPermissions ) ;
196+ }
197+
134198 env. storage ( ) . instance ( ) . set ( & DataKey :: Treasury , & treasury) ;
135199 SetTreasuryEvent {
136200 new_treasury : treasury. clone ( ) ,
137201 }
138202 . publish ( & env) ;
203+ Ok ( ( ) )
139204 }
140205
141206 /// Get the treasury address.
@@ -221,6 +286,7 @@ impl PredifiContract {
221286 /// Resolve a prediction pool with the final outcome.
222287 ///
223288 /// # Arguments
289+ /// * `caller` - The address calling the function
224290 /// * `pool_id` - ID of the pool to resolve
225291 /// * `outcome` - The winning outcome number
226292 ///
@@ -229,7 +295,19 @@ impl PredifiContract {
229295 /// * `PoolAlreadyResolved` - If the pool has already been resolved
230296 /// * `PoolNotExpired` - If the pool end time hasn't been reached
231297 /// * `ResolutionWindowExpired` - If the resolution window has passed
232- pub fn resolve_pool ( env : Env , pool_id : u64 , outcome : u32 ) -> Result < ( ) , PrediFiError > {
298+ /// * `InsufficientPermissions` - If caller doesn't have Oracle role
299+ pub fn resolve_pool (
300+ env : Env ,
301+ caller : Address ,
302+ pool_id : u64 ,
303+ outcome : u32 ,
304+ ) -> Result < ( ) , PrediFiError > {
305+ // Check if caller has Oracle role
306+ let access_control_client = Self :: get_access_control_client ( & env) ;
307+ if !access_control_client. has_role ( & caller, & Role :: Oracle ) {
308+ return Err ( PrediFiError :: InsufficientPermissions ) ;
309+ }
310+
233311 let mut pool: Pool = env
234312 . storage ( )
235313 . instance ( )
@@ -278,6 +356,7 @@ impl PredifiContract {
278356 /// Place a prediction on a pool.
279357 ///
280358 /// # Arguments
359+ /// * `caller` - The address calling the function (should match user)
281360 /// * `user` - Address of the user placing the prediction
282361 /// * `pool_id` - ID of the pool
283362 /// * `amount` - Amount to stake (must be positive)
@@ -289,13 +368,21 @@ impl PredifiContract {
289368 /// * `PredictionTooLate` - If pool has already ended
290369 /// * `PoolAlreadyResolved` - If pool is already resolved
291370 /// * `PredictionAlreadyExists` - If user already has a prediction on this pool
371+ /// * `InsufficientPermissions` - If caller doesn't have User role or doesn't match user
292372 pub fn place_prediction (
293373 env : Env ,
374+ caller : Address ,
294375 user : Address ,
295376 pool_id : u64 ,
296377 amount : i128 ,
297378 outcome : u32 ,
298379 ) -> Result < ( ) , PrediFiError > {
380+ // Check if caller has User role and matches the user address
381+ let access_control_client = Self :: get_access_control_client ( & env) ;
382+ if !access_control_client. has_role ( & caller, & Role :: User ) || caller != user {
383+ return Err ( PrediFiError :: InsufficientPermissions ) ;
384+ }
385+
299386 user. require_auth ( ) ;
300387
301388 // Validate amount
0 commit comments