1- use crate :: models:: balance:: { DelegationBalance , Validator } ;
1+ use crate :: models:: balance:: { DelegationBalance , DelegatorHistoryEntry , Validator , WithdrawalPhase } ;
2+ use chrono:: { DateTime , Duration , Utc } ;
23use num_bigint:: BigUint ;
34use number_formatter:: BigNumberFormatter ;
45use primitives:: { Asset , Chain , DelegationBase , DelegationState , DelegationValidator } ;
56use std:: str:: FromStr ;
67
78pub fn map_staking_validators ( validators : Vec < Validator > , chain : Chain , apy : Option < f64 > ) -> Vec < DelegationValidator > {
89 let calculated_apy = apy. unwrap_or_else ( || Validator :: max_apr ( validators. clone ( ) ) ) ;
9- validators
10+ let mut result : Vec < DelegationValidator > = validators
1011 . into_iter ( )
1112 . map ( |x| DelegationValidator :: stake ( chain, x. validator_address ( ) , x. name , x. is_active , x. commission , calculated_apy) )
12- . collect ( )
13+ . collect ( ) ;
14+
15+ result. push ( DelegationValidator :: system ( chain) ) ;
16+
17+ result
1318}
1419
15- pub fn map_staking_delegations ( delegations : Vec < DelegationBalance > , chain : Chain ) -> Vec < DelegationBase > {
20+ pub fn map_staking_delegations ( delegations : Vec < DelegationBalance > , history : Vec < DelegatorHistoryEntry > , now : DateTime < Utc > , chain : Chain ) -> Vec < DelegationBase > {
1621 let native_decimals = Asset :: from_chain ( chain) . decimals as u32 ;
17- delegations
22+ let mut result: Vec < DelegationBase > = delegations
23+ . into_iter ( )
24+ . map ( |x| DelegationBase {
25+ asset_id : chain. as_asset_id ( ) ,
26+ state : DelegationState :: Active ,
27+ balance : parse_balance ( & x. amount . to_string ( ) , native_decimals) ,
28+ shares : BigUint :: from ( 0u32 ) ,
29+ rewards : BigUint :: from ( 0u32 ) ,
30+ completion_date : None ,
31+ delegation_id : x. validator_address ( ) ,
32+ validator_id : x. validator_address ( ) ,
33+ } )
34+ . collect ( ) ;
35+
36+ result. extend ( map_pending_withdrawals ( history, now, chain, native_decimals) ) ;
37+ result
38+ }
39+
40+ fn map_pending_withdrawals ( history : Vec < DelegatorHistoryEntry > , now : DateTime < Utc > , chain : Chain , native_decimals : u32 ) -> Vec < DelegationBase > {
41+ let lock_time = chain. config ( ) . stake . as_ref ( ) . map ( |stake| stake. lock_time ) . unwrap_or_default ( ) ;
42+ let lock = Duration :: seconds ( lock_time as i64 ) ;
43+
44+ history
1845 . into_iter ( )
19- . map ( |x| {
20- let balance = BigNumberFormatter :: value_from_amount ( & x. amount . to_string ( ) , native_decimals)
21- . ok ( )
22- . and_then ( |s| BigUint :: from_str ( & s) . ok ( ) )
23- . unwrap_or_default ( ) ;
24- DelegationBase {
46+ . filter_map ( |entry| {
47+ let withdrawal = entry. delta . withdrawal ?;
48+ if withdrawal. phase != WithdrawalPhase :: Initiated {
49+ return None ;
50+ }
51+ let completion_date = DateTime :: from_timestamp_millis ( entry. time as i64 ) ? + lock;
52+ if completion_date <= now {
53+ return None ;
54+ }
55+ Some ( DelegationBase {
2556 asset_id : chain. as_asset_id ( ) ,
26- state : DelegationState :: Active ,
27- balance,
57+ state : DelegationState :: Pending ,
58+ balance : parse_balance ( & withdrawal . amount . to_string ( ) , native_decimals ) ,
2859 shares : BigUint :: from ( 0u32 ) ,
2960 rewards : BigUint :: from ( 0u32 ) ,
30- completion_date : None ,
31- delegation_id : x . validator_address ( ) ,
32- validator_id : x . validator_address ( ) ,
33- }
61+ completion_date : Some ( completion_date ) ,
62+ delegation_id : format ! ( "unstaking_{}" , entry . time ) ,
63+ validator_id : DelegationValidator :: SYSTEM_ID . to_string ( ) ,
64+ } )
3465 } )
3566 . collect ( )
3667}
3768
69+ fn parse_balance ( amount : & str , native_decimals : u32 ) -> BigUint {
70+ BigNumberFormatter :: value_from_amount ( amount, native_decimals)
71+ . ok ( )
72+ . and_then ( |s| BigUint :: from_str ( & s) . ok ( ) )
73+ . unwrap_or_default ( )
74+ }
75+
3876#[ cfg( test) ]
3977mod tests {
4078 use super :: * ;
41- use crate :: models:: balance:: ValidatorStats ;
79+ use crate :: models:: balance:: { DelegatorDelta , DelegatorWithdrawal , ValidatorStats } ;
4280 use primitives:: { Chain , DelegationState } ;
4381
82+ fn withdrawal_entry ( time : u64 , amount : f64 , phase : WithdrawalPhase ) -> DelegatorHistoryEntry {
83+ DelegatorHistoryEntry {
84+ time,
85+ delta : DelegatorDelta {
86+ withdrawal : Some ( DelegatorWithdrawal { amount, phase } ) ,
87+ } ,
88+ }
89+ }
90+
4491 #[ test]
4592 fn test_map_staking_validators ( ) {
4693 let validators = vec ! [ Validator {
@@ -52,13 +99,18 @@ mod tests {
5299 } ] ;
53100
54101 let result = map_staking_validators ( validators, Chain :: HyperCore , None ) ;
55- assert_eq ! ( result. len( ) , 1 ) ;
102+ assert_eq ! ( result. len( ) , 2 ) ;
56103 assert_eq ! ( result[ 0 ] . name, "Test Validator" ) ;
57104 assert_eq ! ( result[ 0 ] . id, "0x5aC99df645F3414876C816Caa18b2d234024b487" ) ;
58105 assert_eq ! ( result[ 0 ] . chain, Chain :: HyperCore ) ;
59106 assert ! ( result[ 0 ] . is_active) ;
60107 assert_eq ! ( result[ 0 ] . commission, 5.0 ) ;
61108 assert_eq ! ( result[ 0 ] . apr, 15.0 ) ; // max_apr * 100
109+
110+ let system = & result[ 1 ] ;
111+ assert_eq ! ( system. id, DelegationValidator :: SYSTEM_ID ) ;
112+ assert_eq ! ( system. name, DelegationValidator :: SYSTEM_NAME ) ;
113+ assert ! ( system. is_active) ;
62114 }
63115
64116 #[ test]
@@ -72,15 +124,16 @@ mod tests {
72124 } ] ;
73125
74126 let result = map_staking_validators ( validators, Chain :: HyperCore , Some ( 10.0 ) ) ;
75- assert_eq ! ( result. len( ) , 1 ) ;
127+ assert_eq ! ( result. len( ) , 2 ) ;
76128 assert_eq ! ( result[ 0 ] . apr, 10.0 ) ; // Uses provided APY
129+ assert_eq ! ( result[ 1 ] . id, DelegationValidator :: SYSTEM_ID ) ;
77130 }
78131
79132 #[ test]
80133 fn test_map_staking_delegations ( ) {
81134 let delegations: Vec < DelegationBalance > = serde_json:: from_str ( include_str ! ( "../../testdata/staking_delegations.json" ) ) . unwrap ( ) ;
82135
83- let result = map_staking_delegations ( delegations, Chain :: HyperCore ) ;
136+ let result = map_staking_delegations ( delegations, vec ! [ ] , Utc :: now ( ) , Chain :: HyperCore ) ;
84137
85138 assert_eq ! ( result. len( ) , 2 ) ;
86139
@@ -98,4 +151,29 @@ mod tests {
98151 assert_eq ! ( delegation2. validator_id, "0xaBCDefF4b3727B83A23697500EEf089020DF2cD2" ) ;
99152 assert_eq ! ( delegation2. balance. to_string( ) , "1814578086" ) ;
100153 }
154+
155+ #[ test]
156+ fn test_map_staking_delegations_pending_withdrawals ( ) {
157+ let now = DateTime :: from_timestamp ( 1_780_000_000 , 0 ) . unwrap ( ) ;
158+ let history = vec ! [
159+ withdrawal_entry( 1_779_913_600_000 , 1.5 , WithdrawalPhase :: Initiated ) ,
160+ withdrawal_entry( 1_779_308_800_000 , 2.0 , WithdrawalPhase :: Initiated ) ,
161+ withdrawal_entry( 1_779_950_000_000 , 3.0 , WithdrawalPhase :: Finalized ) ,
162+ DelegatorHistoryEntry {
163+ time: 1_779_950_000_001 ,
164+ delta: DelegatorDelta { withdrawal: None } ,
165+ } ,
166+ ] ;
167+
168+ let result = map_staking_delegations ( vec ! [ ] , history, now, Chain :: HyperCore ) ;
169+
170+ assert_eq ! ( result. len( ) , 1 ) ;
171+ let pending = & result[ 0 ] ;
172+ assert ! ( matches!( pending. state, DelegationState :: Pending ) ) ;
173+ assert_eq ! ( pending. validator_id, DelegationValidator :: SYSTEM_ID ) ;
174+ assert_eq ! ( pending. balance. to_string( ) , "150000000" ) ;
175+ assert_eq ! ( pending. delegation_id, "unstaking_1779913600000" ) ;
176+ assert_eq ! ( pending. completion_date, Some ( now + Duration :: seconds( 518_400 ) ) ) ;
177+ assert ! ( pending. completion_date. unwrap( ) > now) ;
178+ }
101179}
0 commit comments