@@ -172,6 +172,7 @@ mod types;
172
172
use frame_support:: pallet;
173
173
174
174
pub use crate :: { default_weights:: WeightInfo , pallet:: * } ;
175
+ use types:: ReplacedDelegator ;
175
176
176
177
#[ pallet]
177
178
pub mod pallet {
@@ -1391,9 +1392,17 @@ pub mod pallet {
1391
1392
) -> DispatchResultWithPostInfo {
1392
1393
let acc = ensure_signed ( origin) ?;
1393
1394
let collator = T :: Lookup :: lookup ( collator) ?;
1395
+
1396
+ // check balance
1397
+ ensure ! (
1398
+ pallet_balances:: Pallet :: <T >:: free_balance( acc. clone( ) ) >= amount. into( ) ,
1399
+ pallet_balances:: Error :: <T >:: InsufficientBalance
1400
+ ) ;
1401
+
1394
1402
// first delegation
1395
1403
ensure ! ( <DelegatorState <T >>:: get( & acc) . is_none( ) , Error :: <T >:: AlreadyDelegating ) ;
1396
1404
ensure ! ( amount >= T :: MinDelegatorStake :: get( ) , Error :: <T >:: NomStakeBelowMin ) ;
1405
+
1397
1406
// cannot be a collator candidate and delegator with same AccountId
1398
1407
ensure ! ( !Self :: is_active_candidate( & acc) . is_some( ) , Error :: <T >:: CandidateExists ) ;
1399
1408
ensure ! (
@@ -1431,12 +1440,15 @@ pub mod pallet {
1431
1440
. map_err ( |_| Error :: < T > :: MaxCollatorsPerDelegatorExceeded ) ?;
1432
1441
1433
1442
let old_total = state. total ;
1434
- // update state and potentially kick a delegator with less staked amount
1435
- state = if num_delegations_pre_insertion == T :: MaxDelegatorsPerCollator :: get ( ) {
1443
+
1444
+ // update state and potentially prepare kicking a delegator with less staked
1445
+ // amount
1446
+ let ( state, maybe_kicked_delegator) = if num_delegations_pre_insertion == T :: MaxDelegatorsPerCollator :: get ( )
1447
+ {
1436
1448
Self :: do_update_delegator ( delegation, state) ?
1437
1449
} else {
1438
1450
state. total = state. total . saturating_add ( amount) ;
1439
- state
1451
+ ( state, None )
1440
1452
} ;
1441
1453
let new_total = state. total ;
1442
1454
@@ -1446,11 +1458,16 @@ pub mod pallet {
1446
1458
Self :: update_top_candidates ( collator. clone ( ) , old_total, new_total) ;
1447
1459
}
1448
1460
1461
+ // *** No Fail beyond this point ***
1462
+
1449
1463
// update states
1450
1464
<CandidatePool < T > >:: insert ( & collator, state) ;
1451
1465
<DelegatorState < T > >:: insert ( & acc, delegator_state) ;
1452
1466
<LastDelegation < T > >:: insert ( & acc, delegation_counter) ;
1453
1467
1468
+ // update or clear storage of potentially kicked delegator
1469
+ Self :: update_kicked_delegator_storage ( maybe_kicked_delegator) ;
1470
+
1454
1471
// update candidates for next round
1455
1472
let ( num_collators, num_delegators, _, _) = Self :: update_total_stake ( ) ;
1456
1473
@@ -1520,6 +1537,14 @@ pub mod pallet {
1520
1537
let acc = ensure_signed ( origin) ?;
1521
1538
let collator = T :: Lookup :: lookup ( collator) ?;
1522
1539
let mut delegator = <DelegatorState < T > >:: get ( & acc) . ok_or ( Error :: < T > :: NotYetDelegating ) ?;
1540
+
1541
+ // check balance
1542
+ ensure ! (
1543
+ pallet_balances:: Pallet :: <T >:: free_balance( acc. clone( ) )
1544
+ >= delegator. total. saturating_add( amount) . into( ) ,
1545
+ pallet_balances:: Error :: <T >:: InsufficientBalance
1546
+ ) ;
1547
+
1523
1548
// delegation after first
1524
1549
ensure ! ( amount >= T :: MinDelegation :: get( ) , Error :: <T >:: DelegationBelowMin ) ;
1525
1550
ensure ! (
@@ -1535,9 +1560,10 @@ pub mod pallet {
1535
1560
let num_delegations_pre_insertion: u32 = state. delegators . len ( ) . saturated_into ( ) ;
1536
1561
ensure ! ( !state. is_leaving( ) , Error :: <T >:: CannotDelegateIfLeaving ) ;
1537
1562
1538
- // attempt to insert delegator and check for uniqueness
1539
- // NOTE: excess is handled below because we support replacing a delegator with
1540
- // fewer stake
1563
+ // attempt to insert delegation, check for uniqueness and update total delegated
1564
+ // amount
1565
+ // NOTE: excess is handled below because we support replacing a delegator
1566
+ // with fewer stake
1541
1567
ensure ! (
1542
1568
delegator
1543
1569
. add_delegation( Stake {
@@ -1561,12 +1587,14 @@ pub mod pallet {
1561
1587
1562
1588
let old_total = state. total ;
1563
1589
1564
- // update state and potentially kick a delegator with less staked amount
1565
- state = if num_delegations_pre_insertion == T :: MaxDelegatorsPerCollator :: get ( ) {
1590
+ // update state and potentially prepare kicking a delegator with less staked
1591
+ // amount
1592
+ let ( state, maybe_kicked_delegator) = if num_delegations_pre_insertion == T :: MaxDelegatorsPerCollator :: get ( )
1593
+ {
1566
1594
Self :: do_update_delegator ( delegation, state) ?
1567
1595
} else {
1568
1596
state. total = state. total . saturating_add ( amount) ;
1569
- state
1597
+ ( state, None )
1570
1598
} ;
1571
1599
let new_total = state. total ;
1572
1600
@@ -1576,11 +1604,16 @@ pub mod pallet {
1576
1604
Self :: update_top_candidates ( collator. clone ( ) , old_total, new_total) ;
1577
1605
}
1578
1606
1607
+ // *** No Fail beyond this point ***
1608
+
1579
1609
// Update states
1580
1610
<CandidatePool < T > >:: insert ( & collator, state) ;
1581
1611
<DelegatorState < T > >:: insert ( & acc, delegator) ;
1582
1612
<LastDelegation < T > >:: insert ( & acc, delegation_counter) ;
1583
1613
1614
+ // update or clear storage of potentially kicked delegator
1615
+ Self :: update_kicked_delegator_storage ( maybe_kicked_delegator) ;
1616
+
1584
1617
// update candidates for next round
1585
1618
let ( num_collators, num_delegators, _, _) = Self :: update_total_stake ( ) ;
1586
1619
@@ -2012,19 +2045,49 @@ pub mod pallet {
2012
2045
Ok ( ( ) )
2013
2046
}
2014
2047
2015
- fn kick_delegator ( delegation : & StakeOf < T > , collator : & T :: AccountId ) -> DispatchResult {
2048
+ /// Check for remaining delegations of the delegator which has been
2049
+ /// removed from the given collator.
2050
+ ///
2051
+ /// Returns the removed delegator's address and
2052
+ /// * Either the updated delegator state if other delegations are still
2053
+ /// remaining
2054
+ /// * Or `None`, signalling the delegator state should be cleared once
2055
+ /// the transaction cannot fail anymore.
2056
+ fn prep_kick_delegator (
2057
+ delegation : & StakeOf < T > ,
2058
+ collator : & T :: AccountId ,
2059
+ ) -> Result < ReplacedDelegator < T > , DispatchError > {
2016
2060
let mut state = <DelegatorState < T > >:: get ( & delegation. owner ) . ok_or ( Error :: < T > :: DelegatorNotFound ) ?;
2017
2061
state. rm_delegation ( collator) ;
2062
+
2018
2063
// we don't unlock immediately
2019
2064
Self :: prep_unstake ( & delegation. owner , delegation. amount , true ) ?;
2020
2065
2021
- // clear storage if no delegations are remaining
2066
+ // return state if not empty for later removal after all checks have passed
2022
2067
if state. delegations . is_empty ( ) {
2023
- <DelegatorState < T > >:: remove ( & delegation. owner ) ;
2068
+ Ok ( ReplacedDelegator {
2069
+ who : delegation. owner . clone ( ) ,
2070
+ state : None ,
2071
+ } )
2024
2072
} else {
2025
- <DelegatorState < T > >:: insert ( & delegation. owner , state) ;
2073
+ Ok ( ReplacedDelegator {
2074
+ who : delegation. owner . clone ( ) ,
2075
+ state : Some ( state) ,
2076
+ } )
2077
+ }
2078
+ }
2079
+
2080
+ /// Either clear the storage of a kicked delegator or update its
2081
+ /// delegation state if it still contains other delegations.
2082
+ fn update_kicked_delegator_storage ( delegator : Option < ReplacedDelegator < T > > ) {
2083
+ match delegator {
2084
+ Some ( ReplacedDelegator {
2085
+ who,
2086
+ state : Some ( state) ,
2087
+ } ) => DelegatorState :: < T > :: insert ( who, state) ,
2088
+ Some ( ReplacedDelegator { who, .. } ) => DelegatorState :: < T > :: remove ( who) ,
2089
+ _ => ( ) ,
2026
2090
}
2027
- Ok ( ( ) )
2028
2091
}
2029
2092
2030
2093
/// Select the top `MaxSelectedCandidates` many collators in terms of
@@ -2125,7 +2188,9 @@ pub mod pallet {
2125
2188
/// amount is at most the minimum staked value of the original delegator
2126
2189
/// set, an error is returned.
2127
2190
///
2128
- /// Returns the old delegation that is updated, if any.
2191
+ /// Returns a tuple which contains the updated candidate state as well
2192
+ /// as the potentially replaced delegation which will be used later when
2193
+ /// updating the storage of the replaced delegator.
2129
2194
///
2130
2195
/// Emits `DelegationReplaced` if the stake exceeds one of the current
2131
2196
/// delegations.
@@ -2135,10 +2200,17 @@ pub mod pallet {
2135
2200
/// bounded by `MaxDelegatorsPerCollator`.
2136
2201
/// - Reads/Writes: 0
2137
2202
/// # </weight>
2203
+ #[ allow( clippy:: type_complexity) ]
2138
2204
fn do_update_delegator (
2139
2205
stake : Stake < T :: AccountId , BalanceOf < T > > ,
2140
2206
mut state : Candidate < T :: AccountId , BalanceOf < T > , T :: MaxDelegatorsPerCollator > ,
2141
- ) -> Result < CandidateOf < T , T :: MaxDelegatorsPerCollator > , DispatchError > {
2207
+ ) -> Result <
2208
+ (
2209
+ CandidateOf < T , T :: MaxDelegatorsPerCollator > ,
2210
+ Option < ReplacedDelegator < T > > ,
2211
+ ) ,
2212
+ DispatchError ,
2213
+ > {
2142
2214
// attempt to replace the last element of the set
2143
2215
let stake_to_remove = state
2144
2216
. delegators
@@ -2159,7 +2231,7 @@ pub mod pallet {
2159
2231
state. total = state. total . saturating_sub ( stake_to_remove. amount ) ;
2160
2232
2161
2233
// update storage of kicked delegator
2162
- Self :: kick_delegator ( & stake_to_remove, & state. id ) ?;
2234
+ let kicked_delegator = Self :: prep_kick_delegator ( & stake_to_remove, & state. id ) ?;
2163
2235
2164
2236
Self :: deposit_event ( Event :: DelegationReplaced (
2165
2237
stake. owner ,
@@ -2169,9 +2241,11 @@ pub mod pallet {
2169
2241
state. id . clone ( ) ,
2170
2242
state. total ,
2171
2243
) ) ;
2172
- }
2173
2244
2174
- Ok ( state)
2245
+ Ok ( ( state, Some ( kicked_delegator) ) )
2246
+ } else {
2247
+ Ok ( ( state, None ) )
2248
+ }
2175
2249
}
2176
2250
2177
2251
/// Either set or increase the BalanceLock of target account to
0 commit comments