forked from Predictify-org/predictify-contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathadmin.rs
More file actions
4430 lines (4059 loc) · 160 KB
/
Copy pathadmin.rs
File metadata and controls
4430 lines (4059 loc) · 160 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
extern crate alloc;
use alloc::format;
use soroban_sdk::{contracttype, Address, Env, Map, String, Symbol, Vec};
// use alloc::string::ToString; // Unused import
use crate::config::{ConfigManager, ConfigUtils, ContractConfig, Environment};
use crate::err::Error;
use crate::events::EventEmitter;
use crate::extensions::ExtensionManager;
use crate::fees::{FeeConfig, FeeManager};
use crate::markets::MarketStateManager;
// use crate::resolution::MarketResolutionManager;
use crate::audit_trail::{AuditAction, AuditTrailManager};
use alloc::string::ToString;
/// Admin management system for Predictify Hybrid contract
///
/// This module provides a comprehensive admin system with:
/// - Admin initialization and setup functions
/// - Access control and permission validation
/// - Admin role management and hierarchy
/// - Admin action logging and tracking
/// - Admin helper utilities and testing functions
/// - Admin event emission and monitoring
// ===== ADMIN TYPES =====
/// Admin role enumeration
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[contracttype]
pub enum AdminRole {
/// Super admin with all permissions
SuperAdmin,
/// Market admin with market management permissions
MarketAdmin,
/// Config admin with configuration permissions
ConfigAdmin,
/// Fee admin with fee management permissions
FeeAdmin,
/// Read-only admin with view permissions only
ReadOnlyAdmin,
}
/// Severity level for admin broadcasts
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[contracttype]
pub enum Severity {
Info,
Warning,
Critical,
}
/// Admin permission enumeration
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[contracttype]
pub enum AdminPermission {
/// Initialize contract
Initialize,
/// Create markets
CreateMarket,
/// Close markets
CloseMarket,
/// Finalize markets
FinalizeMarket,
/// Extend market duration
ExtendMarket,
/// Update fee configuration
UpdateFees,
/// Update contract configuration
UpdateConfig,
/// Reset configuration
ResetConfig,
/// Collect fees
CollectFees,
/// Manage disputes
ManageDispute,
/// View analytics
ViewAnalytic,
/// Emergency actions
Emergency,
}
/// Admin action record
#[derive(Clone, Debug)]
#[contracttype]
pub struct AdminAction {
pub admin: Address,
pub action: String,
pub target: Option<String>,
pub parameters: Map<String, String>,
pub timestamp: u64,
pub success: bool,
pub error_message: Option<String>,
}
/// Admin role assignment
#[derive(Clone, Debug)]
#[contracttype]
pub struct AdminRoleAssignment {
pub admin: Address,
pub role: AdminRole,
pub assigned_by: Address,
pub assigned_at: u64,
pub permissions: Vec<AdminPermission>,
pub is_active: bool,
}
/// Admin analytics
#[derive(Clone, Debug)]
#[contracttype]
pub struct AdminAnalytics {
pub total_admins: u32,
pub active_admins: u32,
pub total_actions: u32,
pub successful_actions: u32,
pub failed_actions: u32,
pub action_distribution: Map<String, u32>,
pub role_distribution: Map<String, u32>,
pub recent_actions: Vec<AdminAction>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[contracttype]
pub enum AdminActionType {
Added,
Removed,
RoleUpdated,
Activated,
Deactivated,
}
/// Admin analytics result for multi-admin system
#[derive(Clone, Debug)]
#[contracttype]
pub struct AdminAnalyticsResult {
pub total_admins: u32,
pub active_admins: u32,
pub role_distribution: Map<String, u32>,
pub last_updated: u64,
}
/// Multisig configuration for admin operations
#[derive(Clone, Debug)]
#[contracttype]
pub struct MultisigConfig {
pub threshold: u32,
pub total_admins: u32,
pub enabled: bool,
}
/// Pending admin action requiring multisig approval
#[derive(Clone, Debug)]
#[contracttype]
pub struct PendingAdminAction {
pub action_id: u64,
pub action_type: String,
pub target: Address,
pub initiator: Address,
pub approvals: Vec<Address>,
pub created_at: u64,
pub expires_at: u64,
pub executed: bool,
pub data: Map<String, String>,
}
// ===== ADMIN INITIALIZATION =====
/// Admin initialization management
pub struct AdminInitializer;
impl AdminInitializer {
/// Initializes the Predictify Hybrid contract with a primary administrator.
///
/// This function sets up the foundational admin structure for the contract,
/// establishing the primary admin with SuperAdmin privileges and initializing
/// the admin management system. It must be called once after contract deployment.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The address to be granted SuperAdmin privileges
///
/// # Returns
///
/// Returns `Result<(), Error>` where:
/// - `Ok(())` - Admin initialization completed successfully
/// - `Err(Error)` - Specific error if initialization fails
///
/// # Errors
///
/// This function returns specific errors:
/// - `Error::InvalidAddress` - Admin address is invalid or zero
/// - `Error::AlreadyInitialized` - Contract has already been initialized
/// - `Error::StorageError` - Failed to store admin data
/// - Role assignment errors from AdminRoleManager
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address};
/// # use predictify_hybrid::admin::AdminInitializer;
/// # let env = Env::default();
/// # let admin_address = Address::generate(&env);
///
/// match AdminInitializer::initialize(&env, &admin_address) {
/// Ok(()) => {
/// println!("Contract initialized successfully");
/// },
/// Err(e) => {
/// println!("Initialization failed: {:?}", e);
/// }
/// }
/// ```
///
/// # Initialization Process
///
/// The initialization performs these steps:
/// 1. **Address Validation**: Ensures admin address is valid
/// 2. **Storage Setup**: Stores admin address in persistent storage
/// 3. **Role Assignment**: Grants SuperAdmin role to the admin
/// 4. **Event Emission**: Emits admin initialization event
/// 5. **Action Logging**: Records the initialization action
///
/// # Post-Initialization State
///
/// After successful initialization:
/// - Admin has full SuperAdmin privileges
/// - All admin permissions are available to the admin
/// - Admin management system is fully operational
/// - Contract is ready for market creation and management
///
/// # Security
///
/// The admin address should be carefully chosen as it will have complete
/// control over the contract. Consider using a multi-signature wallet
/// or governance contract for production deployments.
pub fn initialize(env: &Env, admin: &Address) -> Result<(), Error> {
// Check for re-initialization attempt (critical security check)
AdminValidator::validate_contract_not_initialized(env)?;
// Validate admin address
AdminValidator::validate_admin_address(env, admin)?;
// Store admin in persistent storage
env.storage()
.persistent()
.set(&Symbol::new(env, "Admin"), admin);
// Set default admin role
AdminRoleManager::assign_role(env, admin, AdminRole::SuperAdmin, admin)?;
// Emit admin initialization event
EventEmitter::emit_admin_initialized(env, admin);
// Log admin action
AdminActionLogger::log_action(env, admin, "initialize", None, Map::new(env), true, None)?;
AuditTrailManager::append_record(
env,
AuditAction::ContractInitialized,
admin.clone(),
Map::new(env),
None,
);
Ok(())
}
/// Initializes the contract with admin and environment-specific configuration.
///
/// This advanced initialization function sets up both admin privileges and
/// applies environment-specific configurations (development, testnet, mainnet).
/// It's ideal for deployment scenarios where specific configurations are needed.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The address to be granted SuperAdmin privileges
/// * `environment` - The target environment configuration to apply
///
/// # Returns
///
/// Returns `Result<(), Error>` where:
/// - `Ok(())` - Admin and configuration initialization completed successfully
/// - `Err(Error)` - Specific error if initialization fails
///
/// # Errors
///
/// This function returns errors from:
/// - `AdminInitializer::initialize()` - Basic admin initialization errors
/// - `ConfigManager::store_config()` - Configuration storage errors
/// - Event emission errors
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address};
/// # use predictify_hybrid::admin::AdminInitializer;
/// # use predictify_hybrid::config::Environment;
/// # let env = Env::default();
/// # let admin_address = Address::generate(&env);
///
/// // Initialize for mainnet deployment
/// match AdminInitializer::initialize_with_config(
/// &env,
/// &admin_address,
/// &Environment::Mainnet
/// ) {
/// Ok(()) => {
/// println!("Contract initialized with mainnet config");
/// },
/// Err(e) => {
/// println!("Initialization failed: {:?}", e);
/// }
/// }
/// ```
///
/// # Environment Configurations
///
/// - **Development**: Relaxed validation, debug features enabled
/// - **Testnet**: Production-like settings with test-friendly parameters
/// - **Mainnet**: Full production settings with strict validation
/// - **Custom**: Defaults to development configuration
///
/// # Configuration Applied
///
/// Environment-specific settings include:
/// - Fee structures and percentages
/// - Market duration limits
/// - Validation thresholds
/// - Oracle timeout settings
/// - Dispute resolution parameters
///
/// # Use Cases
///
/// - **Production Deployment**: Use with `Environment::Mainnet`
/// - **Testing**: Use with `Environment::Testnet` or `Environment::Development`
/// - **CI/CD Pipelines**: Automated deployment with appropriate environment
/// - **Multi-Environment Contracts**: Same contract code, different configs
pub fn initialize_with_config(
env: &Env,
admin: &Address,
environment: &Environment,
) -> Result<(), Error> {
// Initialize basic admin setup
AdminInitializer::initialize(env, admin)?;
let config = match environment {
Environment::Development => ConfigManager::get_development_config(env),
Environment::Testnet => ConfigManager::get_testnet_config(env),
Environment::Mainnet => ConfigManager::get_mainnet_config(env),
Environment::Custom => ConfigManager::get_development_config(env),
};
ConfigManager::store_config(env, &config)?;
// Emit configuration initialization event
EventEmitter::emit_config_initialized(env, admin, environment);
Ok(())
}
/// Validates parameters before contract initialization.
///
/// This function performs pre-initialization validation to ensure the contract
/// can be safely initialized with the provided parameters. It's useful for
/// checking initialization requirements before committing to the initialization.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The proposed admin address to validate
///
/// # Returns
///
/// Returns `Result<(), Error>` where:
/// - `Ok(())` - All parameters are valid for initialization
/// - `Err(Error)` - Specific validation error
///
/// # Errors
///
/// This function returns specific validation errors:
/// - `Error::InvalidAddress` - Admin address is invalid, zero, or malformed
/// - `Error::AlreadyInitialized` - Contract has already been initialized
/// - Address format validation errors
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address};
/// # use predictify_hybrid::admin::AdminInitializer;
/// # let env = Env::default();
/// # let proposed_admin = Address::generate(&env);
///
/// // Validate before initialization
/// match AdminInitializer::validate_initialization_params(&env, &proposed_admin) {
/// Ok(()) => {
/// // Parameters are valid, proceed with initialization
/// AdminInitializer::initialize(&env, &proposed_admin).unwrap();
/// },
/// Err(e) => {
/// println!("Invalid parameters: {:?}", e);
/// }
/// }
/// ```
///
/// # Validation Checks
///
/// The function performs these validations:
/// 1. **Admin Address**: Ensures address is valid and not zero
/// 2. **Contract State**: Verifies contract hasn't been initialized
/// 3. **Address Format**: Validates Stellar address format
/// 4. **Storage Access**: Ensures storage operations will succeed
///
/// # Use Cases
///
/// - **Pre-flight Checks**: Validate before expensive initialization
/// - **UI Validation**: Check parameters in user interfaces
/// - **Deployment Scripts**: Ensure deployment will succeed
/// - **Testing**: Validate test parameters before test execution
/// - **Error Prevention**: Catch issues before state changes
///
/// # Best Practices
///
/// Always call this function before `initialize()` or `initialize_with_config()`
/// to prevent failed initialization attempts that could leave the contract
/// in an inconsistent state.
pub fn validate_initialization_params(env: &Env, admin: &Address) -> Result<(), Error> {
AdminValidator::validate_admin_address(env, admin)?;
AdminValidator::validate_contract_not_initialized(env)?;
Ok(())
}
}
// ===== ADMIN ACCESS CONTROL =====
/// Admin access control management
pub struct AdminAccessControl;
impl AdminAccessControl {
/// Validates that an admin has the required permission for a specific action.
///
/// This function checks if the given admin address has the necessary permission
/// to perform a specific action based on their assigned role. It's the core
/// authorization mechanism for all admin operations in the contract.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The admin address to validate permissions for
/// * `permission` - The specific permission required for the action
///
/// # Returns
///
/// Returns `Result<(), Error>` where:
/// - `Ok(())` - Admin has the required permission
/// - `Err(Error)` - Admin lacks permission or validation failed
///
/// # Errors
///
/// This function returns specific errors:
/// - `Error::Unauthorized` - Admin doesn't have the required permission
/// - `Error::Unauthorized` - Admin role not found or inactive
/// - Role retrieval errors from AdminRoleManager
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address};
/// # use predictify_hybrid::admin::{AdminAccessControl, AdminPermission};
/// # let env = Env::default();
/// # let admin = Address::generate(&env);
///
/// // Check if admin can create markets
/// match AdminAccessControl::validate_permission(
/// &env,
/// &admin,
/// &AdminPermission::CreateMarket
/// ) {
/// Ok(()) => {
/// // Admin has permission, proceed with market creation
/// println!("Admin authorized for market creation");
/// },
/// Err(e) => {
/// println!("Permission denied: {:?}", e);
/// }
/// }
/// ```
///
/// # Permission Hierarchy
///
/// Different admin roles have different permission sets:
/// - **SuperAdmin**: All permissions
/// - **MarketAdmin**: Market-related permissions
/// - **ConfigAdmin**: Configuration permissions
/// - **FeeAdmin**: Fee management permissions
/// - **ReadOnlyAdmin**: View-only permissions
///
/// # Use Cases
///
/// - **Function Guards**: Validate permissions before executing admin functions
/// - **UI Authorization**: Show/hide UI elements based on permissions
/// - **API Endpoints**: Authorize admin API calls
/// - **Batch Operations**: Validate permissions for multiple operations
/// - **Audit Trails**: Log permission checks for security auditing
pub fn validate_permission(
env: &Env,
admin: &Address,
permission: &AdminPermission,
) -> Result<(), Error> {
// Try new multi-admin system first if migrated
if AdminSystemIntegration::is_migrated(env) {
return AdminManager::validate_admin_permission(env, admin, *permission);
}
// Fall back to existing logic
let role = AdminRoleManager::get_admin_role(env, admin)?;
// Check if admin has the required permission
if !AdminRoleManager::has_permission(env, &role, permission)? {
return Err(Error::Unauthorized);
}
Ok(())
}
/// Requires admin authentication and validates admin status.
///
/// This function performs comprehensive admin authentication by verifying
/// the caller's signature and confirming they are a registered admin.
/// It's the fundamental authentication check for all admin operations.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The admin address to authenticate
///
/// # Returns
///
/// Returns `Result<(), Error>` where:
/// - `Ok(())` - Admin is authenticated and authorized
/// - `Err(Error)` - Authentication or authorization failed
///
/// # Errors
///
/// This function returns specific errors:
/// - `Error::AdminNotSet` - No admin has been configured for the contract
/// - `Error::Unauthorized` - Caller is not the registered admin
/// - Authentication errors from Soroban's `require_auth()`
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address};
/// # use predictify_hybrid::admin::AdminAccessControl;
/// # let env = Env::default();
/// # let admin = Address::generate(&env);
///
/// // Authenticate admin before sensitive operation
/// match AdminAccessControl::require_admin_auth(&env, &admin) {
/// Ok(()) => {
/// // Admin is authenticated, proceed with operation
/// println!("Admin authenticated successfully");
/// },
/// Err(e) => {
/// println!("Authentication failed: {:?}", e);
/// return;
/// }
/// }
/// ```
///
/// # Authentication Process
///
/// The authentication performs these checks:
/// 1. **Signature Verification**: Validates the caller's cryptographic signature
/// 2. **Admin Lookup**: Retrieves the stored admin address from contract storage
/// 3. **Address Comparison**: Ensures the caller matches the stored admin
/// 4. **Status Validation**: Confirms admin status is active
///
/// # Security Considerations
///
/// - Uses Soroban's built-in signature verification
/// - Prevents unauthorized access to admin functions
/// - Should be called before any admin-only operations
/// - Protects against address spoofing attacks
///
/// # Use Cases
///
/// - **Function Entry Points**: First check in admin functions
/// - **Batch Operations**: Authenticate once for multiple operations
/// - **API Gateways**: Validate admin API requests
/// - **Emergency Functions**: Ensure only authorized emergency actions
pub fn require_admin_auth(env: &Env, admin: &Address) -> Result<(), Error> {
// Verify admin authentication
admin.require_auth();
// Validate admin exists
let stored_admin: Address = env
.storage()
.persistent()
.get(&Symbol::new(env, "Admin"))
.ok_or(Error::AdminNotSet)?;
if admin != &stored_admin {
return Err(Error::Unauthorized);
}
Ok(())
}
}
// ===== CONTRACT PAUSE AND ADMIN TRANSFER =====
const CONTRACT_PAUSED_KEY: &str = "ContractPaused";
/// Contract-level pause and primary admin transfer.
pub struct ContractPauseManager;
impl ContractPauseManager {
/// Returns true if the contract is currently paused.
pub fn is_contract_paused(env: &Env) -> bool {
env.storage()
.persistent()
.get(&Symbol::new(env, CONTRACT_PAUSED_KEY))
.unwrap_or(false)
}
/// Pause contract operations. Caller must be the current primary admin.
pub fn pause(env: &Env, admin: &Address) -> Result<(), Error> {
AdminAccessControl::require_admin_auth(env, admin)?;
env.storage()
.persistent()
.set(&Symbol::new(env, CONTRACT_PAUSED_KEY), &true);
EventEmitter::emit_contract_paused(env, admin);
AuditTrailManager::append_record(
env,
AuditAction::ContractPaused,
admin.clone(),
Map::new(env),
None,
);
Ok(())
}
/// Unpause contract operations. Caller must be the current primary admin.
pub fn unpause(env: &Env, admin: &Address) -> Result<(), Error> {
AdminAccessControl::require_admin_auth(env, admin)?;
env.storage()
.persistent()
.set(&Symbol::new(env, CONTRACT_PAUSED_KEY), &false);
EventEmitter::emit_contract_unpaused(env, admin);
AuditTrailManager::append_record(
env,
AuditAction::ContractUnpaused,
admin.clone(),
Map::new(env),
None,
);
Ok(())
}
/// Require that the contract is not paused; return Error::InvalidState otherwise.
pub fn require_not_paused(env: &Env) -> Result<(), Error> {
if Self::is_contract_paused(env) {
return Err(Error::InvalidState);
}
Ok(())
}
/// Transfer the primary admin role to a new address. Caller must be the current primary admin.
/// New admin must not be the zero/invalid address.
pub fn transfer_admin(
env: &Env,
current_admin: &Address,
new_admin: &Address,
) -> Result<(), Error> {
AdminAccessControl::require_admin_auth(env, current_admin)?;
if new_admin == current_admin {
return Err(Error::InvalidInput);
}
AdminValidator::validate_admin_address(env, new_admin)?;
env.storage()
.persistent()
.set(&Symbol::new(env, "Admin"), new_admin);
EventEmitter::emit_admin_transferred(env, current_admin, new_admin);
AuditTrailManager::append_record(
env,
AuditAction::AdminTransferred,
current_admin.clone(),
Map::new(env),
None,
);
Ok(())
}
}
impl AdminAccessControl {
/// Validates admin authentication and permissions for a specific action.
///
/// This comprehensive validation function combines authentication and
/// permission checking for a specific action. It's a convenience function
/// that performs complete admin validation in a single call.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The admin address to validate
/// * `action` - String identifier of the action to validate (e.g., "create_market")
///
/// # Returns
///
/// Returns `Result<(), Error>` where:
/// - `Ok(())` - Admin is authenticated and authorized for the action
/// - `Err(Error)` - Authentication, permission, or action mapping failed
///
/// # Errors
///
/// This function returns errors from:
/// - `require_admin_auth()` - Authentication failures
/// - `map_action_to_permission()` - Invalid action string
/// - `validate_permission()` - Permission validation failures
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address};
/// # use predictify_hybrid::admin::AdminAccessControl;
/// # let env = Env::default();
/// # let admin = Address::generate(&env);
///
/// // Validate admin for market creation
/// match AdminAccessControl::validate_admin_for_action(
/// &env,
/// &admin,
/// "create_market"
/// ) {
/// Ok(()) => {
/// // Admin is fully authorized, proceed with market creation
/// println!("Admin authorized for market creation");
/// },
/// Err(e) => {
/// println!("Authorization failed: {:?}", e);
/// }
/// }
/// ```
///
/// # Supported Actions
///
/// Valid action strings include:
/// - `"initialize"` - Contract initialization
/// - `"create_market"` - Market creation
/// - `"close_market"` - Market closure
/// - `"finalize_market"` - Market finalization
/// - `"extend_market"` - Market duration extension
/// - `"update_fees"` - Fee configuration updates
/// - `"update_config"` - Contract configuration updates
/// - `"collect_fees"` - Fee collection
/// - `"manage_disputes"` - Dispute management
/// - `"emergency_actions"` - Emergency operations
///
/// # Validation Process
///
/// The function performs validation in this order:
/// 1. **Authentication**: Verifies admin signature and status
/// 2. **Action Mapping**: Maps action string to permission enum
/// 3. **Permission Check**: Validates admin has required permission
///
/// # Use Cases
///
/// - **Single-Call Validation**: Complete validation in one function call
/// - **Dynamic Actions**: Validate actions determined at runtime
/// - **API Endpoints**: Validate admin API calls with action strings
/// - **Middleware**: Use in middleware for automatic admin validation
pub fn validate_admin_for_action(
env: &Env,
admin: &Address,
action: &str,
) -> Result<(), Error> {
// Require admin authentication
AdminAccessControl::require_admin_auth(env, admin)?;
// Map action to permission
let permission = AdminAccessControl::map_action_to_permission(action)?;
// Validate permission
AdminAccessControl::validate_permission(env, admin, &permission)?;
Ok(())
}
/// Maps action string identifiers to their corresponding permission enums.
///
/// This utility function converts human-readable action strings into
/// the corresponding AdminPermission enum values. It's used to bridge
/// string-based action identifiers with the type-safe permission system.
///
/// # Parameters
///
/// * `action` - String identifier of the action to map
///
/// # Returns
///
/// Returns `Result<AdminPermission, Error>` where:
/// - `Ok(AdminPermission)` - Successfully mapped action to permission
/// - `Err(Error::InvalidInput)` - Action string is not recognized
///
/// # Errors
///
/// This function returns:
/// - `Error::InvalidInput` - Action string doesn't match any known actions
///
/// # Example
///
/// ```rust
/// # use predictify_hybrid::admin::{AdminAccessControl, AdminPermission};
///
/// // Map action string to permission
/// match AdminAccessControl::map_action_to_permission("create_market") {
/// Ok(permission) => {
/// assert_eq!(permission, AdminPermission::CreateMarket);
/// println!("Mapped to CreateMarket permission");
/// },
/// Err(e) => {
/// println!("Invalid action: {:?}", e);
/// }
/// }
///
/// // Handle invalid action
/// match AdminAccessControl::map_action_to_permission("invalid_action") {
/// Ok(_) => unreachable!(),
/// Err(e) => {
/// println!("Expected error for invalid action: {:?}", e);
/// }
/// }
/// ```
///
/// # Action Mapping Table
///
/// | Action String | Permission Enum |
/// |---------------|----------------|
/// | `"initialize"` | `AdminPermission::Initialize` |
/// | `"create_market"` | `AdminPermission::CreateMarket` |
/// | `"close_market"` | `AdminPermission::CloseMarket` |
/// | `"finalize_market"` | `AdminPermission::FinalizeMarket` |
/// | `"extend_market"` | `AdminPermission::ExtendMarket` |
/// | `"update_fees"` | `AdminPermission::UpdateFees` |
/// | `"update_config"` | `AdminPermission::UpdateConfig` |
/// | `"reset_config"` | `AdminPermission::ResetConfig` |
/// | `"collect_fees"` | `AdminPermission::CollectFees` |
/// | `"manage_disputes"` | `AdminPermission::ManageDispute` |
/// | `"view_analytics"` | `AdminPermission::ViewAnalytic` |
/// | `"emergency_actions"` | `AdminPermission::Emergency` |
///
/// # Use Cases
///
/// - **API Integration**: Convert API action parameters to permissions
/// - **Dynamic Validation**: Handle actions determined at runtime
/// - **Configuration**: Map configuration-driven actions to permissions
/// - **Testing**: Validate action-permission mappings in tests
/// - **Debugging**: Convert action strings for logging and debugging
///
/// # Design Notes
///
/// Action strings use snake_case convention to match Rust naming standards.
/// The mapping is case-sensitive and must match exactly. Consider adding
/// case-insensitive mapping if needed for API flexibility.
pub fn map_action_to_permission(action: &str) -> Result<AdminPermission, Error> {
match action {
"initialize" => Ok(AdminPermission::Initialize),
"create_market" => Ok(AdminPermission::CreateMarket),
"close_market" => Ok(AdminPermission::CloseMarket),
"finalize_market" => Ok(AdminPermission::FinalizeMarket),
"extend_market" => Ok(AdminPermission::ExtendMarket),
"update_fees" => Ok(AdminPermission::UpdateFees),
"update_config" => Ok(AdminPermission::UpdateConfig),
"reset_config" => Ok(AdminPermission::ResetConfig),
"collect_fees" => Ok(AdminPermission::CollectFees),
"manage_disputes" => Ok(AdminPermission::ManageDispute),
"view_analytics" => Ok(AdminPermission::ViewAnalytic),
"emergency_actions" => Ok(AdminPermission::Emergency),
"admin_broadcast" => Ok(AdminPermission::Emergency),
_ => Err(Error::InvalidInput),
}
}
}
// ===== ADMIN ROLE MANAGEMENT =====
/// Admin role management
pub struct AdminRoleManager;
impl AdminRoleManager {
/// Assigns a specific admin role to an address with associated permissions.
///
/// This function creates or updates admin role assignments, establishing the
/// permission hierarchy for admin operations. It supports bootstrapping the
/// first admin and subsequent role assignments by authorized admins.
///
/// # Parameters
///
/// * `env` - The Soroban environment for blockchain operations
/// * `admin` - The address to receive the admin role
/// * `role` - The admin role to assign (SuperAdmin, MarketAdmin, etc.)
/// * `assigned_by` - The address performing the role assignment
///
/// # Returns
///
/// Returns `Result<(), Error>` where:
/// - `Ok(())` - Role assigned successfully
/// - `Err(Error)` - Assignment failed due to permissions or validation
///
/// # Errors
///
/// This function returns specific errors:
/// - `Error::Unauthorized` - Assigner lacks Emergency permission
/// - Permission validation errors from AdminAccessControl
/// - Storage operation errors
///
/// # Example
///
/// ```rust
/// # use soroban_sdk::{Env, Address};
/// # use predictify_hybrid::admin::{AdminRoleManager, AdminRole};
/// # let env = Env::default();
/// # let super_admin = Address::generate(&env);
/// # let new_admin = Address::generate(&env);
///
/// // Assign MarketAdmin role to a new admin
/// match AdminRoleManager::assign_role(
/// &env,
/// &new_admin,
/// AdminRole::MarketAdmin,
/// &super_admin
/// ) {
/// Ok(()) => {
/// println!("MarketAdmin role assigned successfully");
/// },
/// Err(e) => {
/// println!("Role assignment failed: {:?}", e);
/// }
/// }
/// ```
///
/// # Role Hierarchy
///
/// Available admin roles with their permission levels:
/// - **SuperAdmin**: All permissions, can assign other roles
/// - **MarketAdmin**: Market creation, closure, finalization, extension
/// - **ConfigAdmin**: Configuration updates and resets
/// - **FeeAdmin**: Fee configuration and collection
/// - **ReadOnlyAdmin**: View-only access to analytics
///
/// # Assignment Process
///
/// The assignment process:
/// 1. **Bootstrap Check**: First assignment bypasses permission validation
/// 2. **Permission Validation**: Subsequent assignments require Emergency permission
/// 3. **Role Creation**: Creates AdminRoleAssignment with timestamp and permissions
/// 4. **Storage Update**: Stores assignment in persistent storage
/// 5. **Event Emission**: Emits role assignment event for monitoring
///
/// # Security
///
/// Only admins with Emergency permission can assign roles to others.
/// The first admin assignment (bootstrapping) bypasses this check to enable
/// initial contract setup.
pub fn assign_role(
env: &Env,
admin: &Address,
role: AdminRole,
assigned_by: &Address,
) -> Result<(), Error> {
// Use a simple fixed key for admin role storage
let key = Symbol::new(env, "admin_role");
// Check if this is the first admin role assignment (bootstrapping)
if !env.storage().persistent().has(&key) {
// No admin role assigned yet, allow bootstrapping without permission check
} else {
// Validate assigner permissions for subsequent assignments
AdminAccessControl::validate_permission(env, assigned_by, &AdminPermission::Emergency)?;
}
// Create role assignment
let assignment = AdminRoleAssignment {
admin: admin.clone(),
role,
assigned_by: assigned_by.clone(),
assigned_at: env.ledger().timestamp(),
permissions: AdminRoleManager::get_permissions_for_role(env, &role),
is_active: true,
};
// Store role assignment
env.storage().persistent().set(&key, &assignment);
// Emit role assignment event
let events_role = match role {
AdminRole::SuperAdmin => crate::events::AdminRole::Owner,
AdminRole::MarketAdmin => crate::events::AdminRole::Admin,
AdminRole::ConfigAdmin => crate::events::AdminRole::Admin,