1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
//! # Services Payment pallet
18
//!
19
//! This pallet allows for block creation services to be paid for by a
20
//! containerChain.
21

            
22
#![cfg_attr(not(feature = "std"), no_std)]
23

            
24
use {
25
    cumulus_primitives_core::ParaId,
26
    frame_support::{
27
        pallet_prelude::*,
28
        sp_runtime::{traits::Zero, Saturating},
29
        traits::{
30
            tokens::ExistenceRequirement, Currency, EnsureOriginWithArg, OnUnbalanced,
31
            WithdrawReasons,
32
        },
33
    },
34
    frame_system::pallet_prelude::*,
35
    scale_info::prelude::vec::Vec,
36
    serde::{Deserialize, Serialize},
37
    sp_io::hashing::blake2_256,
38
    sp_runtime::{traits::TrailingZeroInput, DispatchError},
39
    tp_traits::{AuthorNotingHook, BlockNumber, CollatorAssignmentHook, CollatorAssignmentTip},
40
};
41

            
42
#[cfg(any(test, feature = "runtime-benchmarks"))]
43
mod benchmarks;
44
#[cfg(test)]
45
mod mock;
46

            
47
#[cfg(test)]
48
mod tests;
49
pub mod weights;
50
pub use weights::WeightInfo;
51

            
52
pub use pallet::*;
53

            
54
#[frame_support::pallet]
55
pub mod pallet {
56
    use super::*;
57

            
58
    #[pallet::config]
59
    pub trait Config: frame_system::Config {
60
        /// The overarching event type.
61
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
62
        /// Handlers for fees
63
        type OnChargeForBlock: OnUnbalanced<NegativeImbalanceOf<Self>>;
64
        type OnChargeForCollatorAssignment: OnUnbalanced<NegativeImbalanceOf<Self>>;
65
        type OnChargeForCollatorAssignmentTip: OnUnbalanced<NegativeImbalanceOf<Self>>;
66

            
67
        /// Currency type for fee payment
68
        type Currency: Currency<Self::AccountId>;
69
        /// Provider of a block cost which can adjust from block to block
70
        type ProvideBlockProductionCost: ProvideBlockProductionCost<Self>;
71
        /// Provider of a block cost which can adjust from block to block
72
        type ProvideCollatorAssignmentCost: ProvideCollatorAssignmentCost<Self>;
73

            
74
        /// The maximum number of block production credits that can be accumulated
75
        #[pallet::constant]
76
        type FreeBlockProductionCredits: Get<BlockNumberFor<Self>>;
77

            
78
        /// The maximum number of collator assigment production credits that can be accumulated
79
        #[pallet::constant]
80
        type FreeCollatorAssignmentCredits: Get<u32>;
81
        /// Owner of the container chain, can call some only-owner methods
82
        type ManagerOrigin: EnsureOriginWithArg<Self::RuntimeOrigin, ParaId>;
83

            
84
        type WeightInfo: WeightInfo;
85
    }
86

            
87
181
    #[pallet::error]
88
    pub enum Error<T> {
89
        InsufficientFundsToPurchaseCredits,
90
        InsufficientCredits,
91
        CreditPriceTooExpensive,
92
    }
93

            
94
13620
    #[pallet::pallet]
95
    pub struct Pallet<T>(PhantomData<T>);
96

            
97
    #[pallet::event]
98
7948
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
99
    pub enum Event<T: Config> {
100
1
        CreditsPurchased {
101
            para_id: ParaId,
102
            payer: T::AccountId,
103
            credit: BalanceOf<T>,
104
        },
105
        BlockProductionCreditBurned {
106
            para_id: ParaId,
107
            credits_remaining: BlockNumberFor<T>,
108
        },
109
        CollatorAssignmentCreditBurned {
110
            para_id: ParaId,
111
            credits_remaining: u32,
112
        },
113
        CollatorAssignmentTipCollected {
114
            para_id: ParaId,
115
            payer: T::AccountId,
116
            tip: BalanceOf<T>,
117
        },
118
        BlockProductionCreditsSet {
119
            para_id: ParaId,
120
            credits: BlockNumberFor<T>,
121
        },
122
        RefundAddressUpdated {
123
            para_id: ParaId,
124
            refund_address: Option<T::AccountId>,
125
        },
126
2
        MaxCorePriceUpdated {
127
            para_id: ParaId,
128
            max_core_price: Option<u128>,
129
        },
130
        CollatorAssignmentCreditsSet {
131
            para_id: ParaId,
132
            credits: u32,
133
        },
134
    }
135

            
136
16088
    #[pallet::storage]
137
    #[pallet::getter(fn free_block_production_credits)]
138
    pub type BlockProductionCredits<T: Config> =
139
        StorageMap<_, Blake2_128Concat, ParaId, BlockNumberFor<T>, OptionQuery>;
140

            
141
5233
    #[pallet::storage]
142
    #[pallet::getter(fn free_collator_assignment_credits)]
143
    pub type CollatorAssignmentCredits<T: Config> =
144
        StorageMap<_, Blake2_128Concat, ParaId, u32, OptionQuery>;
145

            
146
    /// List of para ids that have already been given free credits
147
242
    #[pallet::storage]
148
    #[pallet::getter(fn given_free_credits)]
149
    pub type GivenFreeCredits<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>;
150

            
151
    /// Refund address
152
56
    #[pallet::storage]
153
    #[pallet::getter(fn refund_address)]
154
    pub type RefundAddress<T: Config> =
155
        StorageMap<_, Blake2_128Concat, ParaId, T::AccountId, OptionQuery>;
156

            
157
    /// Max core price for parathread in relay chain currency
158
67
    #[pallet::storage]
159
    pub type MaxCorePrice<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, u128, OptionQuery>;
160

            
161
    /// Max tip for collator assignment on congestion
162
5793
    #[pallet::storage]
163
    #[pallet::getter(fn max_tip)]
164
    pub type MaxTip<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, BalanceOf<T>, OptionQuery>;
165

            
166
364
    #[pallet::call]
167
    impl<T: Config> Pallet<T>
168
    where
169
        BlockNumberFor<T>: Into<BalanceOf<T>>,
170
    {
171
        #[pallet::call_index(0)]
172
        #[pallet::weight(T::WeightInfo::purchase_credits())]
173
        pub fn purchase_credits(
174
            origin: OriginFor<T>,
175
            para_id: ParaId,
176
            credit: BalanceOf<T>,
177
74
        ) -> DispatchResultWithPostInfo {
178
74
            let account = ensure_signed(origin)?;
179
74
            let parachain_tank = Self::parachain_tank(para_id);
180
74
            T::Currency::transfer(
181
74
                &account,
182
74
                &parachain_tank,
183
74
                credit,
184
74
                ExistenceRequirement::KeepAlive,
185
74
            )?;
186

            
187
73
            Self::deposit_event(Event::<T>::CreditsPurchased {
188
73
                para_id,
189
73
                payer: account,
190
73
                credit,
191
73
            });
192
73

            
193
73
            Ok(().into())
194
        }
195

            
196
        /// Set the number of block production credits for this para_id without paying for them.
197
        /// Can only be called by root.
198
        #[pallet::call_index(1)]
199
        #[pallet::weight(T::WeightInfo::set_block_production_credits())]
200
        pub fn set_block_production_credits(
201
            origin: OriginFor<T>,
202
            para_id: ParaId,
203
            free_block_credits: BlockNumberFor<T>,
204
45
        ) -> DispatchResultWithPostInfo {
205
45
            ensure_root(origin)?;
206

            
207
44
            Self::set_free_block_production_credits(&para_id, free_block_credits);
208
44

            
209
44
            Ok(().into())
210
        }
211

            
212
        /// Helper to set and cleanup the `GivenFreeCredits` storage.
213
        /// Can only be called by root.
214
        #[pallet::call_index(2)]
215
        #[pallet::weight(T::WeightInfo::set_given_free_credits())]
216
        pub fn set_given_free_credits(
217
            origin: OriginFor<T>,
218
            para_id: ParaId,
219
            given_free_credits: bool,
220
6
        ) -> DispatchResultWithPostInfo {
221
6
            ensure_root(origin)?;
222

            
223
6
            if given_free_credits {
224
                GivenFreeCredits::<T>::insert(para_id, ());
225
6
            } else {
226
6
                GivenFreeCredits::<T>::remove(para_id);
227
6
            }
228

            
229
6
            Ok(().into())
230
        }
231

            
232
        /// Call index to set the refund address for non-spent tokens
233
        #[pallet::call_index(3)]
234
        #[pallet::weight(T::WeightInfo::set_refund_address())]
235
        pub fn set_refund_address(
236
            origin: OriginFor<T>,
237
            para_id: ParaId,
238
            refund_address: Option<T::AccountId>,
239
10
        ) -> DispatchResultWithPostInfo {
240
10
            T::ManagerOrigin::ensure_origin(origin, &para_id)?;
241

            
242
8
            if let Some(refund_address) = refund_address.clone() {
243
7
                RefundAddress::<T>::insert(para_id, refund_address.clone());
244
7
            } else {
245
1
                RefundAddress::<T>::remove(para_id);
246
1
            }
247

            
248
8
            Self::deposit_event(Event::<T>::RefundAddressUpdated {
249
8
                para_id,
250
8
                refund_address,
251
8
            });
252
8

            
253
8
            Ok(().into())
254
        }
255

            
256
        /// Set the number of block production credits for this para_id without paying for them.
257
        /// Can only be called by root.
258
        #[pallet::call_index(4)]
259
        #[pallet::weight(T::WeightInfo::set_block_production_credits())]
260
        pub fn set_collator_assignment_credits(
261
            origin: OriginFor<T>,
262
            para_id: ParaId,
263
            free_collator_assignment_credits: u32,
264
28
        ) -> DispatchResultWithPostInfo {
265
28
            ensure_root(origin)?;
266

            
267
28
            Self::set_free_collator_assignment_credits(&para_id, free_collator_assignment_credits);
268
28

            
269
28
            Ok(().into())
270
        }
271

            
272
        /// Max core price for parathread in relay chain currency
273
        #[pallet::call_index(5)]
274
        #[pallet::weight(T::WeightInfo::set_max_core_price())]
275
        pub fn set_max_core_price(
276
            origin: OriginFor<T>,
277
            para_id: ParaId,
278
            max_core_price: Option<u128>,
279
2
        ) -> DispatchResultWithPostInfo {
280
2
            T::ManagerOrigin::ensure_origin(origin, &para_id)?;
281

            
282
2
            if let Some(max_core_price) = max_core_price {
283
2
                MaxCorePrice::<T>::insert(para_id, max_core_price);
284
2
            } else {
285
                MaxCorePrice::<T>::remove(para_id);
286
            }
287

            
288
2
            Self::deposit_event(Event::<T>::MaxCorePriceUpdated {
289
2
                para_id,
290
2
                max_core_price,
291
2
            });
292
2

            
293
2
            Ok(().into())
294
        }
295

            
296
        /// Set the maximum tip a container chain is willing to pay to be assigned a collator on congestion.
297
        /// Can only be called by container chain manager.
298
        #[pallet::call_index(6)]
299
        #[pallet::weight(T::WeightInfo::set_max_tip())]
300
        pub fn set_max_tip(
301
            origin: OriginFor<T>,
302
            para_id: ParaId,
303
            max_tip: Option<BalanceOf<T>>,
304
16
        ) -> DispatchResultWithPostInfo {
305
16
            T::ManagerOrigin::ensure_origin(origin, &para_id)?;
306

            
307
16
            if let Some(max_tip) = max_tip {
308
16
                MaxTip::<T>::insert(para_id, max_tip);
309
16
            } else {
310
                MaxTip::<T>::remove(para_id);
311
            }
312

            
313
16
            Ok(().into())
314
        }
315
    }
316

            
317
    impl<T: Config> Pallet<T> {
318
        /// Burn a credit for the given para. Deducts one credit if possible, errors otherwise.
319
6584
        pub fn burn_block_production_free_credit_for_para(
320
6584
            para_id: &ParaId,
321
6584
        ) -> DispatchResultWithPostInfo {
322
6584
            let existing_credits =
323
6584
                BlockProductionCredits::<T>::get(para_id).unwrap_or(BlockNumberFor::<T>::zero());
324
6584

            
325
6584
            ensure!(
326
6584
                existing_credits >= 1u32.into(),
327
147
                Error::<T>::InsufficientCredits,
328
            );
329

            
330
6437
            let updated_credits = existing_credits.saturating_sub(1u32.into());
331
6437
            BlockProductionCredits::<T>::insert(para_id, updated_credits);
332
6437

            
333
6437
            Self::deposit_event(Event::<T>::BlockProductionCreditBurned {
334
6437
                para_id: *para_id,
335
6437
                credits_remaining: updated_credits,
336
6437
            });
337
6437

            
338
6437
            Ok(().into())
339
6584
        }
340

            
341
        /// Burn a credit for the given para. Deducts one credit if possible, errors otherwise.
342
1107
        pub fn burn_collator_assignment_free_credit_for_para(
343
1107
            para_id: &ParaId,
344
1107
        ) -> DispatchResultWithPostInfo {
345
1107
            let existing_credits = CollatorAssignmentCredits::<T>::get(para_id).unwrap_or(0u32);
346
1107

            
347
1107
            ensure!(existing_credits >= 1u32, Error::<T>::InsufficientCredits,);
348

            
349
1079
            let updated_credits = existing_credits.saturating_sub(1u32);
350
1079
            CollatorAssignmentCredits::<T>::insert(para_id, updated_credits);
351
1079

            
352
1079
            Self::deposit_event(Event::<T>::CollatorAssignmentCreditBurned {
353
1079
                para_id: *para_id,
354
1079
                credits_remaining: updated_credits,
355
1079
            });
356
1079

            
357
1079
            Ok(().into())
358
1107
        }
359

            
360
120
        pub fn give_free_credits(para_id: &ParaId) -> Weight {
361
120
            if GivenFreeCredits::<T>::contains_key(para_id) {
362
                // This para id has already received free credits
363
4
                return Weight::default();
364
116
            }
365
116

            
366
116
            // Set number of credits to FreeBlockProductionCredits
367
116
            let block_production_existing_credits =
368
116
                BlockProductionCredits::<T>::get(para_id).unwrap_or(BlockNumberFor::<T>::zero());
369
116
            let block_production_updated_credits = T::FreeBlockProductionCredits::get();
370
116
            // Do not update credits if for some reason this para id had more
371
116
            if block_production_existing_credits < block_production_updated_credits {
372
116
                Self::set_free_block_production_credits(para_id, block_production_updated_credits);
373
116
            }
374

            
375
            // Set number of credits to FreeCollatorAssignmentCredits
376
116
            let collator_assignment_existing_credits =
377
116
                CollatorAssignmentCredits::<T>::get(para_id).unwrap_or(0u32);
378
116
            let collator_assignment_updated_credits = T::FreeCollatorAssignmentCredits::get();
379
116

            
380
116
            // Do not update credits if for some reason this para id had more
381
116
            if collator_assignment_existing_credits < collator_assignment_updated_credits {
382
116
                Self::set_free_collator_assignment_credits(
383
116
                    para_id,
384
116
                    collator_assignment_updated_credits,
385
116
                );
386
116
            }
387

            
388
            // We only allow to call this function once per para id, even if it didn't actually
389
            // receive all the free credits
390
116
            GivenFreeCredits::<T>::insert(para_id, ());
391
116

            
392
116
            Weight::default()
393
120
        }
394

            
395
148
        pub fn set_free_collator_assignment_credits(
396
148
            para_id: &ParaId,
397
148
            free_collator_assignment_credits: u32,
398
148
        ) {
399
148
            if free_collator_assignment_credits.is_zero() {
400
20
                CollatorAssignmentCredits::<T>::remove(para_id);
401
128
            } else {
402
128
                CollatorAssignmentCredits::<T>::insert(para_id, free_collator_assignment_credits);
403
128
            }
404

            
405
148
            Self::deposit_event(Event::<T>::CollatorAssignmentCreditsSet {
406
148
                para_id: *para_id,
407
148
                credits: free_collator_assignment_credits,
408
148
            });
409
148
        }
410

            
411
160
        pub fn set_free_block_production_credits(
412
160
            para_id: &ParaId,
413
160
            free_collator_block_production_credits: BlockNumberFor<T>,
414
160
        ) {
415
160
            if free_collator_block_production_credits.is_zero() {
416
31
                BlockProductionCredits::<T>::remove(para_id);
417
129
            } else {
418
129
                BlockProductionCredits::<T>::insert(
419
129
                    para_id,
420
129
                    free_collator_block_production_credits,
421
129
                );
422
129
            }
423

            
424
160
            Self::deposit_event(Event::<T>::BlockProductionCreditsSet {
425
160
                para_id: *para_id,
426
160
                credits: free_collator_block_production_credits,
427
160
            });
428
160
        }
429
    }
430

            
431
    #[pallet::genesis_config]
432
    pub struct GenesisConfig<T: Config> {
433
        pub para_id_credits: Vec<FreeCreditGenesisParams<BlockNumberFor<T>>>,
434
    }
435

            
436
    impl<T: Config> Default for GenesisConfig<T> {
437
3
        fn default() -> Self {
438
3
            Self {
439
3
                para_id_credits: Default::default(),
440
3
            }
441
3
        }
442
    }
443

            
444
502
    #[pallet::genesis_build]
445
    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
446
254
        fn build(&self) {
447
428
            for para_id_credits in &self.para_id_credits {
448
174
                BlockProductionCredits::<T>::insert(
449
174
                    para_id_credits.para_id,
450
174
                    para_id_credits.block_production_credits,
451
174
                );
452
174
                CollatorAssignmentCredits::<T>::insert(
453
174
                    para_id_credits.para_id,
454
174
                    para_id_credits.collator_assignment_credits,
455
174
                );
456
174
            }
457
254
        }
458
    }
459
}
460

            
461
// Params to be set in genesis
462
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Serialize, Deserialize)]
463
pub struct FreeCreditGenesisParams<BlockProductCredits> {
464
    pub para_id: ParaId,
465
    pub block_production_credits: BlockProductCredits,
466
    pub collator_assignment_credits: u32,
467
}
468
impl<BlockProductCredits> From<(ParaId, BlockProductCredits, u32)>
469
    for FreeCreditGenesisParams<BlockProductCredits>
470
{
471
218
    fn from(value: (ParaId, BlockProductCredits, u32)) -> Self {
472
218
        Self {
473
218
            para_id: value.0,
474
218
            block_production_credits: value.1,
475
218
            collator_assignment_credits: value.2,
476
218
        }
477
218
    }
478
}
479

            
480
/// Balance used by this pallet
481
pub type BalanceOf<T> =
482
    <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
483

            
484
pub type CurrencyOf<T> = <T as Config>::Currency;
485
/// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type.
486
pub type NegativeImbalanceOf<T> =
487
    <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
488
/// Handler for fee charging. This will be invoked when fees need to be deducted from the fee
489
/// account for a given paraId.
490

            
491
/// Returns the cost for a given block credit at the current time. This can be a complex operation,
492
/// so it also returns the weight it consumes. (TODO: or just rely on benchmarking)
493
pub trait ProvideBlockProductionCost<T: Config> {
494
    fn block_cost(para_id: &ParaId) -> (BalanceOf<T>, Weight);
495
}
496

            
497
/// Returns the cost for a given block credit at the current time. This can be a complex operation,
498
/// so it also returns the weight it consumes. (TODO: or just rely on benchmarking)
499
pub trait ProvideCollatorAssignmentCost<T: Config> {
500
    fn collator_assignment_cost(para_id: &ParaId) -> (BalanceOf<T>, Weight);
501
}
502

            
503
impl<T: Config> AuthorNotingHook<T::AccountId> for Pallet<T> {
504
    // This hook is called when pallet_author_noting sees that the block number of a container chain has increased.
505
    // Currently we always charge 1 credit, even if a container chain produced more that 1 block in between tanssi
506
    // blocks.
507
6580
    fn on_container_author_noted(
508
6580
        _author: &T::AccountId,
509
6580
        _block_number: BlockNumber,
510
6580
        para_id: ParaId,
511
6580
    ) -> Weight {
512
6580
        if Pallet::<T>::burn_block_production_free_credit_for_para(&para_id).is_err() {
513
144
            let (amount_to_charge, _weight) = T::ProvideBlockProductionCost::block_cost(&para_id);
514
144
            match T::Currency::withdraw(
515
144
                &Self::parachain_tank(para_id),
516
144
                amount_to_charge,
517
144
                WithdrawReasons::FEE,
518
144
                ExistenceRequirement::KeepAlive,
519
144
            ) {
520
108
                Err(e) => log::warn!(
521
106
                    "Failed to withdraw block production payment for container chain {}: {:?}",
522
106
                    u32::from(para_id),
523
                    e
524
                ),
525
36
                Ok(imbalance) => {
526
36
                    T::OnChargeForBlock::on_unbalanced(imbalance);
527
36
                }
528
            }
529
6436
        }
530

            
531
6580
        T::WeightInfo::on_container_author_noted()
532
6580
    }
533
}
534

            
535
impl<T: Config> CollatorAssignmentHook<BalanceOf<T>> for Pallet<T> {
536
    // is_parathread parameter for future use to apply different logic
537
1103
    fn on_collators_assigned(
538
1103
        para_id: ParaId,
539
1103
        maybe_tip: Option<&BalanceOf<T>>,
540
1103
        _is_parathread: bool,
541
1103
    ) -> Result<Weight, DispatchError> {
542
        // Withdraw assignment fee
543
1102
        let maybe_assignment_imbalance =
544
1103
            if Pallet::<T>::burn_collator_assignment_free_credit_for_para(&para_id).is_err() {
545
25
                let (amount_to_charge, _weight) =
546
25
                    T::ProvideCollatorAssignmentCost::collator_assignment_cost(&para_id);
547
25
                Some(T::Currency::withdraw(
548
25
                    &Self::parachain_tank(para_id),
549
25
                    amount_to_charge,
550
25
                    WithdrawReasons::FEE,
551
25
                    ExistenceRequirement::KeepAlive,
552
25
                )?)
553
            } else {
554
1078
                None
555
            };
556

            
557
1102
        if let Some(&tip) = maybe_tip {
558
            // Only charge the tip to the paras that had a max tip set
559
            // (aka were willing to tip for being assigned a collator)
560
50
            if MaxTip::<T>::get(para_id).is_some() {
561
42
                match T::Currency::withdraw(
562
42
                    &Self::parachain_tank(para_id),
563
42
                    tip,
564
42
                    WithdrawReasons::TIP,
565
42
                    ExistenceRequirement::KeepAlive,
566
42
                ) {
567
1
                    Err(e) => {
568
                        // Return assignment imbalance to tank on error
569
1
                        if let Some(assignment_imbalance) = maybe_assignment_imbalance {
570
1
                            T::Currency::resolve_creating(
571
1
                                &Self::parachain_tank(para_id),
572
1
                                assignment_imbalance,
573
1
                            );
574
1
                        }
575
1
                        return Err(e);
576
                    }
577
41
                    Ok(tip_imbalance) => {
578
41
                        Self::deposit_event(Event::<T>::CollatorAssignmentTipCollected {
579
41
                            para_id,
580
41
                            payer: Self::parachain_tank(para_id),
581
41
                            tip,
582
41
                        });
583
41
                        T::OnChargeForCollatorAssignmentTip::on_unbalanced(tip_imbalance);
584
41
                    }
585
                }
586
8
            }
587
1052
        }
588

            
589
1101
        if let Some(assignment_imbalance) = maybe_assignment_imbalance {
590
23
            T::OnChargeForCollatorAssignment::on_unbalanced(assignment_imbalance);
591
1079
        }
592

            
593
1101
        Ok(T::WeightInfo::on_collators_assigned())
594
1103
    }
595
}
596

            
597
impl<T: Config> CollatorAssignmentTip<BalanceOf<T>> for Pallet<T> {
598
3128
    fn get_para_tip(para_id: ParaId) -> Option<BalanceOf<T>> {
599
3128
        MaxTip::<T>::get(para_id)
600
3128
    }
601
}
602

            
603
impl<T: Config> Pallet<T> {
604
    /// Derive a derivative account ID from the paraId.
605
2957
    pub fn parachain_tank(para_id: ParaId) -> T::AccountId {
606
2957
        let entropy = (b"modlpy/serpayment", para_id).using_encoded(blake2_256);
607
2957
        Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
608
2957
            .expect("infinite length input; no invalid inputs for type; qed")
609
2957
    }
610

            
611
    /// Hook to perform things on deregister
612
37
    pub fn para_deregistered(para_id: ParaId) {
613
37
        // Drain the para-id account from tokens
614
37
        let parachain_tank_balance = T::Currency::total_balance(&Self::parachain_tank(para_id));
615
37
        if !parachain_tank_balance.is_zero() {
616
6
            if let Ok(imbalance) = T::Currency::withdraw(
617
6
                &Self::parachain_tank(para_id),
618
6
                parachain_tank_balance,
619
6
                WithdrawReasons::FEE,
620
6
                ExistenceRequirement::AllowDeath,
621
6
            ) {
622
6
                if let Some(address) = RefundAddress::<T>::get(para_id) {
623
3
                    T::Currency::resolve_creating(&address, imbalance);
624
3
                } else {
625
3
                    // Burn for now, we might be able to pass something to do with this
626
3
                    drop(imbalance);
627
3
                }
628
            }
629
31
        }
630

            
631
        // Clean refund addres
632
37
        RefundAddress::<T>::remove(para_id);
633
37

            
634
37
        // Clean credits
635
37
        BlockProductionCredits::<T>::remove(para_id);
636
37
        CollatorAssignmentCredits::<T>::remove(para_id);
637
37
        MaxTip::<T>::remove(para_id);
638
37
        MaxCorePrice::<T>::remove(para_id);
639
37
    }
640
}