forked from Predictify-org/predictify-contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.rs
More file actions
2603 lines (2517 loc) · 89.4 KB
/
Copy pathutils.rs
File metadata and controls
2603 lines (2517 loc) · 89.4 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::string::ToString; // Only for primitive types, not soroban_sdk::String
use soroban_sdk::{Address, Env, Map, String, Symbol, Vec};
use crate::errors::Error;
/// Comprehensive utility function system for Predictify Hybrid contract
///
/// This module provides a centralized collection of utility functions with:
/// - Time and date manipulation utilities
/// - String manipulation and formatting utilities
/// - Numeric calculation helpers
/// - Validation utility functions
/// - Conversion utility functions
/// - Testing utility functions
/// - Common helper functions for contract operations
// ===== TIME AND DATE UTILITIES =====
/// Comprehensive time and date utility functions for market lifecycle management.
///
/// This utility class provides essential time-related operations for prediction markets,
/// including duration calculations, timestamp validation, deadline management, and
/// human-readable time formatting. All functions are designed to work with Stellar
/// blockchain timestamps and market timing requirements.
///
/// # Core Functionality
///
/// **Time Conversions:**
/// - Convert days, hours, minutes to seconds
/// - Calculate time differences between timestamps
/// - Format durations in human-readable format
///
/// **Timestamp Validation:**
/// - Check if timestamps are in future or past
/// - Validate deadline status
/// - Ensure duration values are within acceptable ranges
///
/// **Market Timing:**
/// - Calculate time until market deadlines
/// - Validate market duration parameters
/// - Support market extension calculations
///
/// # Example Usage
///
/// ```rust
/// # use soroban_sdk::Env;
/// # use predictify_hybrid::utils::TimeUtils;
/// # let env = Env::default();
///
/// // Convert market duration to seconds
/// let market_duration_days = 30;
/// let duration_seconds = TimeUtils::days_to_seconds(market_duration_days);
/// println!("Market duration: {} seconds", duration_seconds);
///
/// // Check if market has ended
/// let current_time = env.ledger().timestamp();
/// let market_end_time = current_time + TimeUtils::days_to_seconds(7); // 7 days from now
///
/// if TimeUtils::is_deadline_passed(current_time, market_end_time) {
/// println!("Market has ended");
/// } else {
/// let time_remaining = TimeUtils::time_until_deadline(current_time, market_end_time);
/// let formatted_time = TimeUtils::format_duration(&env, time_remaining);
/// println!("Time remaining: {}", formatted_time);
/// }
///
/// // Validate market duration
/// let proposed_duration = 45; // days
/// if TimeUtils::validate_duration(&proposed_duration) {
/// println!("Duration is valid");
/// } else {
/// println!("Duration exceeds maximum allowed");
/// }
/// ```
///
/// # Time Conversion Utilities
///
/// Convert various time units to seconds for blockchain operations:
/// ```rust
/// # use predictify_hybrid::utils::TimeUtils;
///
/// // Common time conversions
/// let one_day = TimeUtils::days_to_seconds(1); // 86,400 seconds
/// let one_hour = TimeUtils::hours_to_seconds(1); // 3,600 seconds
/// let one_minute = TimeUtils::minutes_to_seconds(1); // 60 seconds
///
/// // Market duration examples
/// let short_market = TimeUtils::days_to_seconds(7); // 1 week
/// let medium_market = TimeUtils::days_to_seconds(30); // 1 month
/// let long_market = TimeUtils::days_to_seconds(90); // 3 months
///
/// println!("Short market: {} seconds", short_market);
/// println!("Medium market: {} seconds", medium_market);
/// println!("Long market: {} seconds", long_market);
/// ```
///
/// # Timestamp Validation
///
/// Validate timestamps for market operations:
/// ```rust
/// # use soroban_sdk::Env;
/// # use predictify_hybrid::utils::TimeUtils;
/// # let env = Env::default();
///
/// let current_time = env.ledger().timestamp();
/// let future_time = current_time + TimeUtils::days_to_seconds(30);
/// let past_time = current_time - TimeUtils::days_to_seconds(30);
///
/// // Timestamp validation
/// assert!(TimeUtils::is_future_timestamp(current_time, future_time));
/// assert!(TimeUtils::is_past_timestamp(current_time, past_time));
/// assert!(!TimeUtils::is_deadline_passed(current_time, future_time));
/// assert!(TimeUtils::is_deadline_passed(current_time, past_time));
///
/// // Calculate time differences
/// let diff_future = TimeUtils::time_difference(current_time, future_time);
/// let diff_past = TimeUtils::time_difference(current_time, past_time);
///
/// println!("Time to future: {} seconds", diff_future);
/// println!("Time from past: {} seconds", diff_past);
/// ```
///
/// # Duration Formatting
///
/// Format time durations for user interfaces:
/// ```rust
/// # use soroban_sdk::Env;
/// # use predictify_hybrid::utils::TimeUtils;
/// # let env = Env::default();
///
/// // Format various durations
/// let durations = vec![
/// TimeUtils::minutes_to_seconds(45), // "45m"
/// TimeUtils::hours_to_seconds(2), // "2h 0m"
/// TimeUtils::days_to_seconds(1), // "1d 0h 0m"
/// TimeUtils::days_to_seconds(7) + TimeUtils::hours_to_seconds(12), // "7d 12h 0m"
/// ];
///
/// for duration in durations {
/// let formatted = TimeUtils::format_duration(&env, duration);
/// println!("Duration: {}", formatted);
/// }
/// ```
///
/// # Market Deadline Management
///
/// Manage market deadlines and extensions:
/// ```rust
/// # use soroban_sdk::Env;
/// # use predictify_hybrid::utils::TimeUtils;
/// # let env = Env::default();
///
/// let current_time = env.ledger().timestamp();
/// let market_end = current_time + TimeUtils::days_to_seconds(7);
///
/// // Check time until deadline
/// let time_remaining = TimeUtils::time_until_deadline(current_time, market_end);
/// if time_remaining > 0 {
/// let formatted_remaining = TimeUtils::format_duration(&env, time_remaining);
/// println!("Market ends in: {}", formatted_remaining);
///
/// // Check if extension is needed (less than 24 hours remaining)
/// if time_remaining < TimeUtils::days_to_seconds(1) {
/// println!("Market may need extension for more participation");
/// }
/// } else {
/// println!("Market has ended");
/// }
/// ```
///
/// # Integration Points
///
/// TimeUtils integrates with:
/// - **Market Manager**: Market duration and deadline validation
/// - **Extension System**: Calculate extension durations
/// - **Resolution System**: Timing for oracle resolution
/// - **Event System**: Timestamp formatting for events
/// - **Admin System**: Validate administrative timing operations
/// - **User Interface**: Human-readable time displays
///
/// # Performance Considerations
///
/// All time operations are optimized for blockchain execution:
/// - **Constant Time**: All calculations are O(1) operations
/// - **No External Calls**: Pure mathematical operations
/// - **Memory Efficient**: Minimal memory allocation
/// - **Gas Optimized**: Low computational overhead
pub struct TimeUtils;
impl TimeUtils {
/// Convert days to seconds
pub fn days_to_seconds(days: u32) -> u64 {
days as u64 * 24 * 60 * 60
}
/// Convert hours to seconds
pub fn hours_to_seconds(hours: u32) -> u64 {
hours as u64 * 60 * 60
}
/// Convert minutes to seconds
pub fn minutes_to_seconds(minutes: u32) -> u64 {
minutes as u64 * 60
}
/// Calculate time difference between two timestamps
pub fn time_difference(timestamp1: u64, timestamp2: u64) -> u64 {
if timestamp1 > timestamp2 {
timestamp1 - timestamp2
} else {
timestamp2 - timestamp1
}
}
/// Check if a timestamp is in the future
pub fn is_future_timestamp(current_time: u64, future_time: u64) -> bool {
future_time > current_time
}
/// Check if a timestamp is in the past
pub fn is_past_timestamp(current_time: u64, past_time: u64) -> bool {
past_time < current_time
}
/// Format duration in human-readable format
pub fn format_duration(env: &Env, seconds: u64) -> String {
let days = seconds / (24 * 60 * 60);
let hours = (seconds % (24 * 60 * 60)) / (60 * 60);
let minutes = (seconds % (60 * 60)) / 60;
let mut s = alloc::string::String::new();
if days > 0 {
s.push_str(&days.to_string());
s.push_str("d ");
s.push_str(&hours.to_string());
s.push_str("h ");
s.push_str(&minutes.to_string());
s.push_str("m");
} else if hours > 0 {
s.push_str(&hours.to_string());
s.push_str("h ");
s.push_str(&minutes.to_string());
s.push_str("m");
} else {
s.push_str(&minutes.to_string());
s.push_str("m");
}
String::from_str(env, &s)
}
/// Calculate time until deadline
pub fn time_until_deadline(current_time: u64, deadline: u64) -> u64 {
if deadline > current_time {
deadline - current_time
} else {
0
}
}
/// Check if deadline has passed
pub fn is_deadline_passed(current_time: u64, deadline: u64) -> bool {
current_time >= deadline
}
/// Validate duration (days) is within acceptable range
pub fn validate_duration(days: &u32) -> bool {
*days > 0 && *days <= crate::config::MAX_MARKET_DURATION_DAYS
}
}
// ===== STRING UTILITIES =====
/// Comprehensive string manipulation and formatting utilities for contract operations.
///
/// This utility class provides essential string operations for prediction markets,
/// including validation, formatting, sanitization, and manipulation functions.
/// All operations are designed to work with Soroban SDK String types while
/// maintaining compatibility with blockchain constraints.
///
/// # Core Functionality
///
/// **String Transformation:**
/// - Case conversion (uppercase/lowercase)
/// - Trimming and truncation
/// - String splitting and joining
///
/// **String Validation:**
/// - Length validation with min/max constraints
/// - Content validation and sanitization
/// - Format verification
///
/// **String Analysis:**
/// - Substring searching and matching
/// - Prefix and suffix checking
/// - Content replacement operations
///
/// # Example Usage
///
/// ```rust
/// # use soroban_sdk::{Env, String, Vec};
/// # use predictify_hybrid::utils::StringUtils;
/// # let env = Env::default();
///
/// // String validation for market questions
/// let market_question = String::from_str(&env, "Will Bitcoin reach $100,000?");
///
/// // Validate question length
/// match StringUtils::validate_string_length(&market_question, 10, 200) {
/// Ok(()) => println!("Question length is valid"),
/// Err(e) => println!("Question too short or too long: {:?}", e),
/// }
///
/// // Sanitize user input
/// let sanitized_question = StringUtils::sanitize_string(&market_question);
/// println!("Sanitized question: {}", sanitized_question);
///
/// // String manipulation
/// let trimmed = StringUtils::trim(&market_question);
/// let truncated = StringUtils::truncate(&market_question, 50);
///
/// println!("Original: {}", market_question);
/// println!("Trimmed: {}", trimmed);
/// println!("Truncated: {}", truncated);
/// ```
///
/// # String Validation
///
/// Validate strings for market operations:
/// ```rust
/// # use soroban_sdk::{Env, String};
/// # use predictify_hybrid::utils::StringUtils;
/// # let env = Env::default();
///
/// // Market question validation
/// let questions = vec![
/// String::from_str(&env, "Will BTC hit $100k?"), // Valid
/// String::from_str(&env, "BTC?"), // Too short
/// String::from_str(&env, &"x".repeat(300)), // Too long
/// ];
///
/// for question in questions {
/// match StringUtils::validate_string_length(&question, 10, 200) {
/// Ok(()) => println!("✓ Valid question: {}", question),
/// Err(_) => println!("✗ Invalid question length"),
/// }
/// }
///
/// // Outcome validation
/// let outcomes = vec![
/// String::from_str(&env, "yes"),
/// String::from_str(&env, "no"),
/// String::from_str(&env, "maybe"),
/// ];
///
/// for outcome in outcomes {
/// if StringUtils::validate_string_length(&outcome, 1, 50).is_ok() {
/// println!("Valid outcome: {}", outcome);
/// }
/// }
/// ```
///
/// # String Manipulation
///
/// Transform and manipulate strings:
/// ```rust
/// # use soroban_sdk::{Env, String, Vec};
/// # use predictify_hybrid::utils::StringUtils;
/// # let env = Env::default();
///
/// let original = String::from_str(&env, " Bitcoin Price Prediction ");
///
/// // Basic transformations
/// let uppercase = StringUtils::to_uppercase(&original);
/// let lowercase = StringUtils::to_lowercase(&original);
/// let trimmed = StringUtils::trim(&original);
/// let truncated = StringUtils::truncate(&original, 15);
///
/// println!("Original: '{}'", original);
/// println!("Uppercase: '{}'", uppercase);
/// println!("Lowercase: '{}'", lowercase);
/// println!("Trimmed: '{}'", trimmed);
/// println!("Truncated: '{}'", truncated);
///
/// // String replacement
/// let replaced = StringUtils::replace(&original, "Bitcoin", "BTC");
/// println!("Replaced: '{}'", replaced);
/// ```
///
/// # String Analysis
///
/// Analyze string content and structure:
/// ```rust
/// # use soroban_sdk::{Env, String};
/// # use predictify_hybrid::utils::StringUtils;
/// # let env = Env::default();
///
/// let text = String::from_str(&env, "Will Bitcoin reach $100,000 by 2024?");
///
/// // Content analysis
/// let contains_bitcoin = StringUtils::contains(&text, "Bitcoin");
/// let starts_with_will = StringUtils::starts_with(&text, "Will");
/// let ends_with_question = StringUtils::ends_with(&text, "?");
///
/// println!("Contains 'Bitcoin': {}", contains_bitcoin);
/// println!("Starts with 'Will': {}", starts_with_will);
/// println!("Ends with '?': {}", ends_with_question);
///
/// // Pattern validation for market questions
/// if starts_with_will && ends_with_question {
/// println!("Question follows proper format");
/// } else {
/// println!("Question format needs improvement");
/// }
/// ```
///
/// # String Splitting and Joining
///
/// Split and join strings for data processing:
/// ```rust
/// # use soroban_sdk::{Env, String, Vec};
/// # use predictify_hybrid::utils::StringUtils;
/// # let env = Env::default();
///
/// // Split comma-separated outcomes
/// let outcomes_str = String::from_str(&env, "yes,no,maybe");
/// let outcomes_vec = StringUtils::split(&outcomes_str, ",");
///
/// println!("Split outcomes:");
/// for outcome in outcomes_vec.iter() {
/// println!("- {}", outcome);
/// }
///
/// // Join outcomes back together
/// let mut outcomes = Vec::new(&env);
/// outcomes.push_back(String::from_str(&env, "yes"));
/// outcomes.push_back(String::from_str(&env, "no"));
/// outcomes.push_back(String::from_str(&env, "uncertain"));
///
/// let joined = StringUtils::join(&outcomes, " | ");
/// println!("Joined outcomes: {}", joined);
/// ```
///
/// # String Sanitization
///
/// Sanitize user input for security:
/// ```rust
/// # use soroban_sdk::{Env, String};
/// # use predictify_hybrid::utils::StringUtils;
/// # let env = Env::default();
///
/// // Sanitize potentially unsafe input
/// let unsafe_inputs = vec![
/// String::from_str(&env, "Will BTC <script>alert('hack')</script> reach $100k?"),
/// String::from_str(&env, "Question with special chars: @#$%^&*()"),
/// String::from_str(&env, "Normal question about Bitcoin price?"),
/// ];
///
/// for input in unsafe_inputs {
/// let sanitized = StringUtils::sanitize_string(&input);
/// println!("Original: {}", input);
/// println!("Sanitized: {}", sanitized);
/// println!();
/// }
/// ```
///
/// # Random String Generation
///
/// Generate random strings for testing and IDs:
/// ```rust
/// # use soroban_sdk::Env;
/// # use predictify_hybrid::utils::StringUtils;
/// # let env = Env::default();
///
/// // Generate random strings for testing
/// let random_id = StringUtils::generate_random_string(&env, 10);
/// let random_token = StringUtils::generate_random_string(&env, 32);
///
/// println!("Random ID: {}", random_id);
/// println!("Random token: {}", random_token);
///
/// // Use in market creation for unique identifiers
/// let market_id = StringUtils::generate_random_string(&env, 16);
/// println!("Generated market ID: {}", market_id);
/// ```
///
/// # Integration Points
///
/// StringUtils integrates with:
/// - **Market Creation**: Validate questions and outcomes
/// - **User Input**: Sanitize and validate user-provided data
/// - **Event System**: Format event messages and descriptions
/// - **Admin System**: Validate administrative input
/// - **Oracle System**: Format and validate oracle feed IDs
/// - **Dispute System**: Process dispute reasons and evidence
///
/// # Soroban SDK Limitations
///
/// Note on current implementation limitations:
/// - Some string operations return placeholders due to Soroban SDK constraints
/// - Case conversion operations are simplified
/// - Complex string manipulations may need custom implementations
/// - Future SDK updates may provide enhanced string capabilities
///
/// # Performance Considerations
///
/// String operations are optimized for blockchain execution:
/// - **Memory Efficient**: Minimal string copying
/// - **Gas Optimized**: Simple operations preferred
/// - **Validation First**: Early validation prevents expensive operations
/// - **Immutable Operations**: Preserve original strings when possible
pub struct StringUtils;
impl StringUtils {
/// Convert string to uppercase
pub fn to_uppercase(s: &String) -> String {
let _env = Env::default();
// Can't convert soroban_sdk::String to std::string::String
// Return original string as placeholder
s.clone()
}
/// Convert string to lowercase
pub fn to_lowercase(s: &String) -> String {
let _env = Env::default();
// Can't convert soroban_sdk::String to std::string::String
// Return original string as placeholder
s.clone()
}
/// Trim whitespace from string
pub fn trim(s: &String) -> String {
let _env = Env::default();
// Can't convert soroban_sdk::String to std::string::String
// Return original string as placeholder
s.clone()
}
/// Truncate string to specified length
pub fn truncate(s: &String, _max_length: u32) -> String {
let _env = Env::default();
// Can't convert soroban_sdk::String to std::string::String
// Return original string as placeholder
s.clone()
}
/// Split string by delimiter
pub fn split(s: &String, _delimiter: &str) -> Vec<String> {
let env = Env::default();
// Can't convert soroban_sdk::String to std::string::String
// Return vector with original string as placeholder
let mut result = Vec::new(&env);
result.push_back(s.clone());
result
}
/// Join strings with delimiter
pub fn join(strings: &Vec<String>, delimiter: &str) -> String {
let env = Env::default();
let mut result = alloc::string::String::new();
for (i, _s) in strings.iter().enumerate() {
if i > 0 {
result.push_str(delimiter);
}
// Can't convert soroban_sdk::String to std::string::String
// Skip string conversion
}
String::from_str(&env, &result)
}
/// Check if string contains substring
pub fn contains(_s: &String, _substring: &str) -> bool {
// Can't convert soroban_sdk::String to std::string::String
// Return false as placeholder
false
}
/// Check if string starts with prefix
pub fn starts_with(_s: &String, _prefix: &str) -> bool {
// Can't convert soroban_sdk::String to std::string::String
// Return false as placeholder
false
}
/// Check if string ends with suffix
pub fn ends_with(_s: &String, _suffix: &str) -> bool {
// Can't convert soroban_sdk::String to std::string::String
// Return false as placeholder
false
}
/// Replace substring in string
pub fn replace(s: &String, _old: &str, _new: &str) -> String {
let _env = Env::default();
// Can't convert soroban_sdk::String to std::string::String
// Return original string as placeholder
s.clone()
}
/// Validate string length
pub fn validate_string_length(
s: &String,
min_length: u32,
max_length: u32,
) -> Result<(), Error> {
let len = s.len() as u32;
if len < min_length || len > max_length {
Err(Error::InvalidInput)
} else {
Ok(())
}
}
/// Sanitize string (remove special characters)
pub fn sanitize_string(s: &String) -> String {
let _env = Env::default();
// Can't convert soroban_sdk::String to std::string::String
// Return original string as placeholder
s.clone()
}
/// Generate random string
pub fn generate_random_string(env: &Env, _length: u32) -> String {
// For now, return a placeholder since we can't easily generate random strings
// This is a limitation of the current Soroban SDK
String::from_str(env, "random")
}
}
// ===== NUMERIC UTILITIES =====
/// Comprehensive numeric calculation utilities for financial and mathematical operations.
///
/// This utility class provides essential mathematical operations for prediction markets,
/// including percentage calculations, statistical functions, financial computations,
/// and numeric validation. All operations are optimized for blockchain execution
/// and handle large integer values common in cryptocurrency applications.
///
/// # Core Functionality
///
/// **Basic Mathematics:**
/// - Percentage calculations and conversions
/// - Rounding and clamping operations
/// - Range validation and boundary checking
///
/// **Statistical Operations:**
/// - Weighted averages for stake calculations
/// - Square root approximations
/// - Absolute difference calculations
///
/// **Financial Calculations:**
/// - Simple interest computations
/// - Fee calculations and distributions
/// - Stake and payout calculations
///
/// # Example Usage
///
/// ```rust
/// # use soroban_sdk::{Env, Vec};
/// # use predictify_hybrid::utils::NumericUtils;
/// # let env = Env::default();
///
/// // Calculate market participation percentage
/// let user_stake = 1_000_000; // 1 XLM in stroops
/// let total_stakes = 10_000_000; // 10 XLM total
/// let participation_pct = NumericUtils::calculate_percentage(
/// &user_stake, &100, &total_stakes
/// );
/// println!("User participation: {}%", participation_pct);
///
/// // Validate stake amount is within acceptable range
/// let min_stake = 100_000; // 0.1 XLM
/// let max_stake = 100_000_000; // 100 XLM
///
/// if NumericUtils::is_within_range(&user_stake, &min_stake, &max_stake) {
/// println!("Stake amount is valid");
/// } else {
/// let clamped_stake = NumericUtils::clamp(&user_stake, &min_stake, &max_stake);
/// println!("Stake clamped to: {} stroops", clamped_stake);
/// }
///
/// // Calculate weighted consensus
/// let mut votes = Vec::new(&env);
/// votes.push_back(75); // 75% confidence
/// votes.push_back(80); // 80% confidence
/// votes.push_back(90); // 90% confidence
///
/// let mut weights = Vec::new(&env);
/// weights.push_back(1_000_000); // 1 XLM stake
/// weights.push_back(2_000_000); // 2 XLM stake
/// weights.push_back(3_000_000); // 3 XLM stake
///
/// let weighted_consensus = NumericUtils::weighted_average(&votes, &weights);
/// println!("Weighted consensus: {}%", weighted_consensus);
/// ```
///
/// # Percentage Calculations
///
/// Calculate percentages for various market operations:
/// ```rust
/// # use predictify_hybrid::utils::NumericUtils;
///
/// // Market fee calculations
/// let transaction_amount = 5_000_000; // 5 XLM
/// let fee_rate = 2; // 2%
/// let fee_amount = NumericUtils::calculate_percentage(
/// &fee_rate, &transaction_amount, &100
/// );
/// println!("Transaction fee: {} stroops", fee_amount);
///
/// // Payout distribution calculations
/// let total_pool = 50_000_000; // 50 XLM prize pool
/// let winner_percentage = 80; // Winners get 80%
/// let winner_pool = NumericUtils::calculate_percentage(
/// &winner_percentage, &total_pool, &100
/// );
/// println!("Winner pool: {} stroops", winner_pool);
///
/// // Participation rate calculations
/// let active_users = 150;
/// let total_users = 200;
/// let participation_rate = NumericUtils::calculate_percentage(
/// &active_users, &100, &total_users
/// );
/// println!("Participation rate: {}%", participation_rate);
/// ```
///
/// # Range Operations
///
/// Validate and constrain numeric values:
/// ```rust
/// # use predictify_hybrid::utils::NumericUtils;
///
/// // Stake validation
/// let proposed_stakes = vec![50_000, 1_000_000, 150_000_000, 500_000];
/// let min_stake = 100_000; // 0.1 XLM minimum
/// let max_stake = 100_000_000; // 100 XLM maximum
///
/// for stake in proposed_stakes {
/// if NumericUtils::is_within_range(&stake, &min_stake, &max_stake) {
/// println!("✓ Valid stake: {} stroops", stake);
/// } else {
/// let clamped = NumericUtils::clamp(&stake, &min_stake, &max_stake);
/// println!("✗ Invalid stake {} clamped to {}", stake, clamped);
/// }
/// }
///
/// // Price threshold validation
/// let price_thresholds = vec![0, 50_000_00, 1_000_000_00, -100];
/// let min_price = 1_00; // $0.01 minimum
/// let max_price = 10_000_000_00; // $10M maximum
///
/// for price in price_thresholds {
/// let valid_price = NumericUtils::clamp(&price, &min_price, &max_price);
/// println!("Price {} -> {}", price, valid_price);
/// }
/// ```
///
/// # Statistical Calculations
///
/// Perform statistical operations for market analysis:
/// ```rust
/// # use soroban_sdk::{Env, Vec};
/// # use predictify_hybrid::utils::NumericUtils;
/// # let env = Env::default();
///
/// // Calculate stake-weighted average confidence
/// let mut confidence_scores = Vec::new(&env);
/// confidence_scores.push_back(85); // User 1: 85% confidence
/// confidence_scores.push_back(92); // User 2: 92% confidence
/// confidence_scores.push_back(78); // User 3: 78% confidence
///
/// let mut stake_weights = Vec::new(&env);
/// stake_weights.push_back(1_000_000); // User 1: 1 XLM
/// stake_weights.push_back(5_000_000); // User 2: 5 XLM (higher weight)
/// stake_weights.push_back(2_000_000); // User 3: 2 XLM
///
/// let weighted_confidence = NumericUtils::weighted_average(
/// &confidence_scores, &stake_weights
/// );
/// println!("Market confidence: {}%", weighted_confidence);
///
/// // Calculate price volatility (using absolute differences)
/// let prices = vec![50_000_00, 52_000_00, 48_000_00, 51_000_00];
/// let mut total_volatility = 0;
///
/// for i in 1..prices.len() {
/// let diff = NumericUtils::abs_difference(&prices[i], &prices[i-1]);
/// total_volatility += diff;
/// }
///
/// let avg_volatility = total_volatility / (prices.len() as i128 - 1);
/// println!("Average price volatility: {} cents", avg_volatility);
/// ```
///
/// # Financial Calculations
///
/// Perform financial computations for market economics:
/// ```rust
/// # use predictify_hybrid::utils::NumericUtils;
///
/// // Calculate interest on staked amounts
/// let principal = 10_000_000; // 10 XLM staked
/// let annual_rate = 5; // 5% annual interest
/// let periods = 12; // 12 months
///
/// let interest_earned = NumericUtils::simple_interest(
/// &principal, &annual_rate, &periods
/// );
/// println!("Interest earned: {} stroops", interest_earned);
///
/// // Fee distribution calculations
/// let total_fees = 1_000_000; // 1 XLM in fees
/// let platform_share = 30; // 30% to platform
/// let oracle_share = 20; // 20% to oracle
/// let community_share = 50; // 50% to community
///
/// let platform_fee = NumericUtils::calculate_percentage(
/// &platform_share, &total_fees, &100
/// );
/// let oracle_fee = NumericUtils::calculate_percentage(
/// &oracle_share, &total_fees, &100
/// );
/// let community_fee = NumericUtils::calculate_percentage(
/// &community_share, &total_fees, &100
/// );
///
/// println!("Platform fee: {} stroops", platform_fee);
/// println!("Oracle fee: {} stroops", oracle_fee);
/// println!("Community fee: {} stroops", community_fee);
/// ```
///
/// # Rounding and Approximation
///
/// Handle rounding for display and calculation purposes:
/// ```rust
/// # use predictify_hybrid::utils::NumericUtils;
///
/// // Round stakes to nearest 0.1 XLM (100,000 stroops)
/// let raw_stakes = vec![1_234_567, 2_876_543, 999_999];
/// let rounding_unit = 100_000; // 0.1 XLM
///
/// for stake in raw_stakes {
/// let rounded = NumericUtils::round_to_nearest(&stake, &rounding_unit);
/// println!("Stake {} rounded to {}", stake, rounded);
/// }
///
/// // Calculate square root for standard deviation approximations
/// let variance = 1_000_000; // Variance in price movements
/// let std_deviation = NumericUtils::sqrt(&variance);
/// println!("Standard deviation: {}", std_deviation);
///
/// // Round prices to nearest cent
/// let raw_prices = vec![50_123_45, 75_678_90, 100_001_23];
/// let cent_rounding = 1; // Round to nearest cent
///
/// for price in raw_prices {
/// let rounded_price = NumericUtils::round_to_nearest(&price, ¢_rounding);
/// println!("Price {} rounded to {}", price, rounded_price);
/// }
/// ```
///
/// # Integration Points
///
/// NumericUtils integrates with:
/// - **Market Manager**: Stake and fee calculations
/// - **Resolution System**: Confidence scoring and weighted averages
/// - **Fee Manager**: Fee distribution and percentage calculations
/// - **Oracle System**: Price validation and range checking
/// - **Analytics System**: Statistical calculations and trend analysis
/// - **Payout System**: Winner distribution calculations
///
/// # Performance Considerations
///
/// Numeric operations are optimized for blockchain execution:
/// - **Integer Arithmetic**: All operations use integer math for precision
/// - **Overflow Protection**: Safe arithmetic operations prevent overflow
/// - **Gas Efficient**: Minimal computational overhead
/// - **Memory Optimized**: No dynamic memory allocation in calculations
///
/// # Precision and Accuracy
///
/// All calculations maintain precision for financial operations:
/// - **Stroops Precision**: All amounts in smallest unit (stroops)
/// - **Percentage Precision**: Integer percentages for exact calculations
/// - **Rounding Control**: Explicit rounding behavior
/// - **Range Validation**: Prevent invalid or extreme values
pub struct NumericUtils;
impl NumericUtils {
/// Calculate a percentage of an amount using basis points (1/10000).
pub fn calculate_bps(amount: i128, bps: u32) -> i128 {
if amount <= 0 {
return 0;
}
amount.saturating_mul(bps as i128) / 10000
}
/// Calculates the proportional share of a pool for a specific stake.
pub fn calculate_payout_share(
total_pool: i128,
user_stake: i128,
total_winning_stakes: i128,
) -> i128 {
if total_winning_stakes <= 0 || total_pool <= 0 || user_stake <= 0 {
return 0;
}
total_pool.saturating_mul(user_stake) / total_winning_stakes
}
/// Calculate percentage
pub fn calculate_percentage(percentage: &i128, value: &i128, denominator: &i128) -> i128 {
(*percentage * *value) / *denominator
}
/// Round to nearest multiple
pub fn round_to_nearest(value: &i128, multiple: &i128) -> i128 {
(*value / *multiple) * *multiple
}
/// Clamp value between min and max
pub fn clamp(value: &i128, min: &i128, max: &i128) -> i128 {
if *value < *min {
*min
} else if *value > *max {
*max
} else {
*value
}
}
/// Check if value is within range
pub fn is_within_range(value: &i128, min: &i128, max: &i128) -> bool {
*value >= *min && *value <= *max
}
/// Calculate absolute difference between two values
pub fn abs_difference(a: &i128, b: &i128) -> i128 {
if *a > *b {
*a - *b
} else {
*b - *a
}
}
/// Calculate square root (integer approximation)
pub fn sqrt(value: &i128) -> i128 {
if *value <= 0 {
return 0;
}
let mut x = *value;
let mut y = (*value + 1) / 2;
while y < x {
x = y;
y = (*value / x + x) / 2;
}
x
}
/// Calculate weighted average
pub fn weighted_average(values: &Vec<i128>, weights: &Vec<i128>) -> i128 {
if values.len() != weights.len() || values.len() == 0 {
return 0;
}
let mut total_weight = 0;
let mut weighted_sum = 0;
for i in 0..values.len() {
let value = values.get_unchecked(i);
let weight = weights.get_unchecked(i);
weighted_sum += value * weight;
total_weight += weight;
}
if total_weight == 0 {
0
} else {
weighted_sum / total_weight
}
}
/// Calculate simple interest
pub fn simple_interest(principal: &i128, rate: &i128, periods: &i128) -> i128 {
(*principal * *rate * *periods) / 100
}
/// Convert number to string
pub fn i128_to_string(env: &Env, _value: &i128) -> String {
// For now, return a placeholder since we can't easily convert to string
// This is a limitation of the current Soroban SDK
String::from_str(env, "0")
}
/// Convert string to number
pub fn string_to_i128(_s: &String) -> i128 {
// Can't convert soroban_sdk::String to std::string::String
// Return 0 as placeholder
0
}