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
7293
#[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
616
    #[pallet::error]
88
    pub enum Error<T> {
89
        InsufficientFundsToPurchaseCredits,
90
        InsufficientCredits,
91
        CreditPriceTooExpensive,
92
    }
93

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

            
97
    #[pallet::event]
98
15367
    #[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
30937
    #[pallet::storage]
137
    pub type BlockProductionCredits<T: Config> =
138
        StorageMap<_, Blake2_128Concat, ParaId, BlockNumberFor<T>, OptionQuery>;
139

            
140
7490
    #[pallet::storage]
141
    pub type CollatorAssignmentCredits<T: Config> =
142
        StorageMap<_, Blake2_128Concat, ParaId, u32, OptionQuery>;
143

            
144
    /// List of para ids that have already been given free credits
145
210
    #[pallet::storage]
146
    pub type GivenFreeCredits<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>;
147

            
148
    /// Refund address
149
82
    #[pallet::storage]
150
    pub type RefundAddress<T: Config> =
151
        StorageMap<_, Blake2_128Concat, ParaId, T::AccountId, OptionQuery>;
152

            
153
    /// Max core price for parathread in relay chain currency
154
77
    #[pallet::storage]
155
    pub type MaxCorePrice<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, u128, OptionQuery>;
156

            
157
    /// Max tip for collator assignment on congestion
158
8325
    #[pallet::storage]
159
    pub type MaxTip<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, BalanceOf<T>, OptionQuery>;
160

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

            
182
126
            Self::deposit_event(Event::<T>::CreditsPurchased {
183
126
                para_id,
184
126
                payer: account,
185
126
                credit,
186
126
            });
187
126

            
188
126
            Ok(().into())
189
        }
190

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

            
202
72
            Self::set_free_block_production_credits(&para_id, free_block_credits);
203
72

            
204
72
            Ok(().into())
205
        }
206

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

            
218
12
            if given_free_credits {
219
                GivenFreeCredits::<T>::insert(para_id, ());
220
12
            } else {
221
12
                GivenFreeCredits::<T>::remove(para_id);
222
12
            }
223

            
224
12
            Ok(().into())
225
        }
226

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

            
237
12
            if let Some(refund_address) = refund_address.clone() {
238
11
                RefundAddress::<T>::insert(para_id, refund_address.clone());
239
11
            } else {
240
1
                RefundAddress::<T>::remove(para_id);
241
1
            }
242

            
243
12
            Self::deposit_event(Event::<T>::RefundAddressUpdated {
244
12
                para_id,
245
12
                refund_address,
246
12
            });
247
12

            
248
12
            Ok(().into())
249
        }
250

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

            
262
46
            Self::set_free_collator_assignment_credits(&para_id, free_collator_assignment_credits);
263
46

            
264
46
            Ok(().into())
265
        }
266

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

            
277
2
            if let Some(max_core_price) = max_core_price {
278
2
                MaxCorePrice::<T>::insert(para_id, max_core_price);
279
2
            } else {
280
                MaxCorePrice::<T>::remove(para_id);
281
            }
282

            
283
2
            Self::deposit_event(Event::<T>::MaxCorePriceUpdated {
284
2
                para_id,
285
2
                max_core_price,
286
2
            });
287
2

            
288
2
            Ok(().into())
289
        }
290

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

            
302
32
            if let Some(max_tip) = max_tip {
303
32
                MaxTip::<T>::insert(para_id, max_tip);
304
32
            } else {
305
                MaxTip::<T>::remove(para_id);
306
            }
307

            
308
32
            Ok(().into())
309
        }
310
    }
311

            
312
    impl<T: Config> Pallet<T> {
313
        /// Burn a credit for the given para. Deducts one credit if possible, errors otherwise.
314
13494
        pub fn burn_block_production_free_credit_for_para(
315
13494
            para_id: &ParaId,
316
13494
        ) -> DispatchResultWithPostInfo {
317
13494
            let existing_credits =
318
13494
                BlockProductionCredits::<T>::get(para_id).unwrap_or(BlockNumberFor::<T>::zero());
319
13494

            
320
13494
            ensure!(
321
13494
                existing_credits >= 1u32.into(),
322
272
                Error::<T>::InsufficientCredits,
323
            );
324

            
325
13222
            let updated_credits = existing_credits.saturating_sub(1u32.into());
326
13222
            BlockProductionCredits::<T>::insert(para_id, updated_credits);
327
13222

            
328
13222
            Self::deposit_event(Event::<T>::BlockProductionCreditBurned {
329
13222
                para_id: *para_id,
330
13222
                credits_remaining: updated_credits,
331
13222
            });
332
13222

            
333
13222
            Ok(().into())
334
13494
        }
335

            
336
        /// Burn a credit for the given para. Deducts one credit if possible, errors otherwise.
337
1667
        pub fn burn_collator_assignment_free_credit_for_para(
338
1667
            para_id: &ParaId,
339
1667
        ) -> DispatchResultWithPostInfo {
340
1667
            let existing_credits = CollatorAssignmentCredits::<T>::get(para_id).unwrap_or(0u32);
341
1667

            
342
1667
            ensure!(existing_credits >= 1u32, Error::<T>::InsufficientCredits,);
343

            
344
1637
            let updated_credits = existing_credits.saturating_sub(1u32);
345
1637
            CollatorAssignmentCredits::<T>::insert(para_id, updated_credits);
346
1637

            
347
1637
            Self::deposit_event(Event::<T>::CollatorAssignmentCreditBurned {
348
1637
                para_id: *para_id,
349
1637
                credits_remaining: updated_credits,
350
1637
            });
351
1637

            
352
1637
            Ok(().into())
353
1667
        }
354

            
355
100
        pub fn give_free_credits(para_id: &ParaId) -> Weight {
356
100
            if GivenFreeCredits::<T>::contains_key(para_id) {
357
                // This para id has already received free credits
358
2
                return Weight::default();
359
98
            }
360
98

            
361
98
            // Set number of credits to FreeBlockProductionCredits
362
98
            let block_production_existing_credits =
363
98
                BlockProductionCredits::<T>::get(para_id).unwrap_or(BlockNumberFor::<T>::zero());
364
98
            let block_production_updated_credits = T::FreeBlockProductionCredits::get();
365
98
            // Do not update credits if for some reason this para id had more
366
98
            if block_production_existing_credits < block_production_updated_credits {
367
98
                Self::set_free_block_production_credits(para_id, block_production_updated_credits);
368
98
            }
369

            
370
            // Set number of credits to FreeCollatorAssignmentCredits
371
98
            let collator_assignment_existing_credits =
372
98
                CollatorAssignmentCredits::<T>::get(para_id).unwrap_or(0u32);
373
98
            let collator_assignment_updated_credits = T::FreeCollatorAssignmentCredits::get();
374
98

            
375
98
            // Do not update credits if for some reason this para id had more
376
98
            if collator_assignment_existing_credits < collator_assignment_updated_credits {
377
98
                Self::set_free_collator_assignment_credits(
378
98
                    para_id,
379
98
                    collator_assignment_updated_credits,
380
98
                );
381
98
            }
382

            
383
            // We only allow to call this function once per para id, even if it didn't actually
384
            // receive all the free credits
385
98
            GivenFreeCredits::<T>::insert(para_id, ());
386
98

            
387
98
            Weight::default()
388
100
        }
389

            
390
148
        pub fn set_free_collator_assignment_credits(
391
148
            para_id: &ParaId,
392
148
            free_collator_assignment_credits: u32,
393
148
        ) {
394
148
            if free_collator_assignment_credits.is_zero() {
395
33
                CollatorAssignmentCredits::<T>::remove(para_id);
396
115
            } else {
397
115
                CollatorAssignmentCredits::<T>::insert(para_id, free_collator_assignment_credits);
398
115
            }
399

            
400
148
            Self::deposit_event(Event::<T>::CollatorAssignmentCreditsSet {
401
148
                para_id: *para_id,
402
148
                credits: free_collator_assignment_credits,
403
148
            });
404
148
        }
405

            
406
170
        pub fn set_free_block_production_credits(
407
170
            para_id: &ParaId,
408
170
            free_collator_block_production_credits: BlockNumberFor<T>,
409
170
        ) {
410
170
            if free_collator_block_production_credits.is_zero() {
411
53
                BlockProductionCredits::<T>::remove(para_id);
412
117
            } else {
413
117
                BlockProductionCredits::<T>::insert(
414
117
                    para_id,
415
117
                    free_collator_block_production_credits,
416
117
                );
417
117
            }
418

            
419
170
            Self::deposit_event(Event::<T>::BlockProductionCreditsSet {
420
170
                para_id: *para_id,
421
170
                credits: free_collator_block_production_credits,
422
170
            });
423
170
        }
424

            
425
        pub fn free_block_production_credits(para_id: ParaId) -> Option<BlockNumberFor<T>> {
426
            BlockProductionCredits::<T>::get(para_id)
427
        }
428

            
429
        pub fn free_collator_assignment_credits(para_id: ParaId) -> Option<u32> {
430
            CollatorAssignmentCredits::<T>::get(para_id)
431
        }
432

            
433
        pub fn given_free_credits(para_id: ParaId) -> Option<()> {
434
            GivenFreeCredits::<T>::get(para_id)
435
        }
436

            
437
        pub fn refund_address(para_id: ParaId) -> Option<T::AccountId> {
438
            RefundAddress::<T>::get(para_id)
439
        }
440

            
441
        pub fn max_tip(para_id: ParaId) -> Option<BalanceOf<T>> {
442
            MaxTip::<T>::get(para_id)
443
        }
444
    }
445

            
446
    #[pallet::genesis_config]
447
    pub struct GenesisConfig<T: Config> {
448
        pub para_id_credits: Vec<FreeCreditGenesisParams<BlockNumberFor<T>>>,
449
    }
450

            
451
    impl<T: Config> Default for GenesisConfig<T> {
452
4
        fn default() -> Self {
453
4
            Self {
454
4
                para_id_credits: Default::default(),
455
4
            }
456
4
        }
457
    }
458

            
459
326
    #[pallet::genesis_build]
460
    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
461
330
        fn build(&self) {
462
596
            for para_id_credits in &self.para_id_credits {
463
266
                BlockProductionCredits::<T>::insert(
464
266
                    para_id_credits.para_id,
465
266
                    para_id_credits.block_production_credits,
466
266
                );
467
266
                CollatorAssignmentCredits::<T>::insert(
468
266
                    para_id_credits.para_id,
469
266
                    para_id_credits.collator_assignment_credits,
470
266
                );
471
266
            }
472
330
        }
473
    }
474
}
475

            
476
// Params to be set in genesis
477
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Serialize, Deserialize)]
478
pub struct FreeCreditGenesisParams<BlockProductCredits> {
479
    pub para_id: ParaId,
480
    pub block_production_credits: BlockProductCredits,
481
    pub collator_assignment_credits: u32,
482
}
483
impl<BlockProductCredits> From<(ParaId, BlockProductCredits, u32)>
484
    for FreeCreditGenesisParams<BlockProductCredits>
485
{
486
794
    fn from(value: (ParaId, BlockProductCredits, u32)) -> Self {
487
794
        Self {
488
794
            para_id: value.0,
489
794
            block_production_credits: value.1,
490
794
            collator_assignment_credits: value.2,
491
794
        }
492
794
    }
493
}
494

            
495
/// Balance used by this pallet
496
pub type BalanceOf<T> =
497
    <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
498

            
499
pub type CurrencyOf<T> = <T as Config>::Currency;
500
/// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type.
501
pub type NegativeImbalanceOf<T> =
502
    <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
503
/// Handler for fee charging. This will be invoked when fees need to be deducted from the fee
504
/// account for a given paraId.
505

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

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

            
518
impl<T: Config> AuthorNotingHook<T::AccountId> for Pallet<T> {
519
    // This hook is called when pallet_author_noting sees that the block number of a container chain has increased.
520
    // Currently we always charge 1 credit, even if a container chain produced more that 1 block in between tanssi
521
    // blocks.
522
13490
    fn on_container_author_noted(
523
13490
        _author: &T::AccountId,
524
13490
        _block_number: BlockNumber,
525
13490
        para_id: ParaId,
526
13490
    ) -> Weight {
527
13490
        if Pallet::<T>::burn_block_production_free_credit_for_para(&para_id).is_err() {
528
269
            let (amount_to_charge, _weight) = T::ProvideBlockProductionCost::block_cost(&para_id);
529
269
            match T::Currency::withdraw(
530
269
                &Self::parachain_tank(para_id),
531
269
                amount_to_charge,
532
269
                WithdrawReasons::FEE,
533
269
                ExistenceRequirement::KeepAlive,
534
269
            ) {
535
214
                Err(e) => log::warn!(
536
212
                    "Failed to withdraw block production payment for container chain {}: {:?}",
537
212
                    u32::from(para_id),
538
                    e
539
                ),
540
55
                Ok(imbalance) => {
541
55
                    T::OnChargeForBlock::on_unbalanced(imbalance);
542
55
                }
543
            }
544
13221
        }
545

            
546
13490
        T::WeightInfo::on_container_author_noted()
547
13490
    }
548
}
549

            
550
impl<T: Config> CollatorAssignmentHook<BalanceOf<T>> for Pallet<T> {
551
    // is_parathread parameter for future use to apply different logic
552
1663
    fn on_collators_assigned(
553
1663
        para_id: ParaId,
554
1663
        maybe_tip: Option<&BalanceOf<T>>,
555
1663
        _is_parathread: bool,
556
1663
    ) -> Result<Weight, DispatchError> {
557
        // Withdraw assignment fee
558
1662
        let maybe_assignment_imbalance =
559
1663
            if Pallet::<T>::burn_collator_assignment_free_credit_for_para(&para_id).is_err() {
560
27
                let (amount_to_charge, _weight) =
561
27
                    T::ProvideCollatorAssignmentCost::collator_assignment_cost(&para_id);
562
27
                Some(T::Currency::withdraw(
563
27
                    &Self::parachain_tank(para_id),
564
27
                    amount_to_charge,
565
27
                    WithdrawReasons::FEE,
566
27
                    ExistenceRequirement::KeepAlive,
567
27
                )?)
568
            } else {
569
1636
                None
570
            };
571

            
572
1662
        if let Some(&tip) = maybe_tip {
573
            // Only charge the tip to the paras that had a max tip set
574
            // (aka were willing to tip for being assigned a collator)
575
64
            if MaxTip::<T>::get(para_id).is_some() {
576
51
                match T::Currency::withdraw(
577
51
                    &Self::parachain_tank(para_id),
578
51
                    tip,
579
51
                    WithdrawReasons::TIP,
580
51
                    ExistenceRequirement::KeepAlive,
581
51
                ) {
582
1
                    Err(e) => {
583
                        // Return assignment imbalance to tank on error
584
1
                        if let Some(assignment_imbalance) = maybe_assignment_imbalance {
585
1
                            T::Currency::resolve_creating(
586
1
                                &Self::parachain_tank(para_id),
587
1
                                assignment_imbalance,
588
1
                            );
589
1
                        }
590
1
                        return Err(e);
591
                    }
592
50
                    Ok(tip_imbalance) => {
593
50
                        Self::deposit_event(Event::<T>::CollatorAssignmentTipCollected {
594
50
                            para_id,
595
50
                            payer: Self::parachain_tank(para_id),
596
50
                            tip,
597
50
                        });
598
50
                        T::OnChargeForCollatorAssignmentTip::on_unbalanced(tip_imbalance);
599
50
                    }
600
                }
601
13
            }
602
1598
        }
603

            
604
1661
        if let Some(assignment_imbalance) = maybe_assignment_imbalance {
605
25
            T::OnChargeForCollatorAssignment::on_unbalanced(assignment_imbalance);
606
1637
        }
607

            
608
1661
        Ok(T::WeightInfo::on_collators_assigned())
609
1663
    }
610
}
611

            
612
impl<T: Config> CollatorAssignmentTip<BalanceOf<T>> for Pallet<T> {
613
4565
    fn get_para_tip(para_id: ParaId) -> Option<BalanceOf<T>> {
614
4565
        MaxTip::<T>::get(para_id)
615
4565
    }
616
}
617

            
618
impl<T: Config> Pallet<T> {
619
    /// Derive a derivative account ID from the paraId.
620
4234
    pub fn parachain_tank(para_id: ParaId) -> T::AccountId {
621
4234
        let entropy = (b"modlpy/serpayment", para_id).using_encoded(blake2_256);
622
4234
        Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
623
4234
            .expect("infinite length input; no invalid inputs for type; qed")
624
4234
    }
625

            
626
    /// Hook to perform things on deregister
627
55
    pub fn para_deregistered(para_id: ParaId) {
628
55
        // Drain the para-id account from tokens
629
55
        let parachain_tank_balance = T::Currency::total_balance(&Self::parachain_tank(para_id));
630
55
        if !parachain_tank_balance.is_zero() {
631
10
            if let Ok(imbalance) = T::Currency::withdraw(
632
10
                &Self::parachain_tank(para_id),
633
10
                parachain_tank_balance,
634
10
                WithdrawReasons::FEE,
635
10
                ExistenceRequirement::AllowDeath,
636
10
            ) {
637
10
                if let Some(address) = RefundAddress::<T>::get(para_id) {
638
5
                    T::Currency::resolve_creating(&address, imbalance);
639
5
                } else {
640
5
                    // Burn for now, we might be able to pass something to do with this
641
5
                    drop(imbalance);
642
5
                }
643
            }
644
45
        }
645

            
646
        // Clean refund addres
647
55
        RefundAddress::<T>::remove(para_id);
648
55

            
649
55
        // Clean credits
650
55
        BlockProductionCredits::<T>::remove(para_id);
651
55
        CollatorAssignmentCredits::<T>::remove(para_id);
652
55
        MaxTip::<T>::remove(para_id);
653
55
        MaxCorePrice::<T>::remove(para_id);
654
55
    }
655
}