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

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

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

            
140
10084
    #[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
240
    #[pallet::storage]
146
    pub type GivenFreeCredits<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>;
147

            
148
    /// Refund address
149
106
    #[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
97
    #[pallet::storage]
155
    pub type MaxCorePrice<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, u128, OptionQuery>;
156

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

            
161
1290
    #[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
149
        ) -> DispatchResultWithPostInfo {
173
149
            let account = ensure_signed(origin)?;
174
149
            let parachain_tank = Self::parachain_tank(para_id);
175
149
            T::Currency::transfer(
176
149
                &account,
177
149
                &parachain_tank,
178
149
                credit,
179
149
                ExistenceRequirement::KeepAlive,
180
149
            )?;
181

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

            
188
148
            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
89
        ) -> DispatchResultWithPostInfo {
200
89
            ensure_root(origin)?;
201

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

            
204
88
            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
18
        ) -> DispatchResultWithPostInfo {
216
18
            ensure_root(origin)?;
217

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

            
224
18
            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
22
        ) -> DispatchResultWithPostInfo {
235
22
            T::ManagerOrigin::ensure_origin(origin, &para_id)?;
236

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

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

            
248
16
            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
60
        ) -> DispatchResultWithPostInfo {
260
60
            ensure_root(origin)?;
261

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

            
264
60
            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
34
        ) -> DispatchResultWithPostInfo {
300
34
            T::ManagerOrigin::ensure_origin(origin, &para_id)?;
301

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

            
308
34
            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
20226
        pub fn burn_block_production_free_credit_for_para(
315
20226
            para_id: &ParaId,
316
20226
        ) -> DispatchResultWithPostInfo {
317
20226
            let existing_credits =
318
20226
                BlockProductionCredits::<T>::get(para_id).unwrap_or(BlockNumberFor::<T>::zero());
319
20226

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

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

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

            
333
19826
            Ok(().into())
334
20226
        }
335

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

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

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

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

            
352
2251
            Ok(().into())
353
2287
        }
354

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

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

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

            
375
110
            // Do not update credits if for some reason this para id had more
376
110
            if collator_assignment_existing_credits < collator_assignment_updated_credits {
377
110
                Self::set_free_collator_assignment_credits(
378
110
                    para_id,
379
110
                    collator_assignment_updated_credits,
380
110
                );
381
110
            }
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
110
            GivenFreeCredits::<T>::insert(para_id, ());
386
110

            
387
110
            Weight::default()
388
112
        }
389

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

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

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

            
419
198
            Self::deposit_event(Event::<T>::BlockProductionCreditsSet {
420
198
                para_id: *para_id,
421
198
                credits: free_collator_block_production_credits,
422
198
            });
423
198
        }
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
357
    #[pallet::genesis_build]
460
    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
461
361
        fn build(&self) {
462
627
            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
361
        }
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
402
    fn from(value: (ParaId, BlockProductCredits, u32)) -> Self {
487
402
        Self {
488
402
            para_id: value.0,
489
402
            block_production_credits: value.1,
490
402
            collator_assignment_credits: value.2,
491
402
        }
492
402
    }
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
20222
    fn on_container_author_noted(
523
20222
        _author: &T::AccountId,
524
20222
        _block_number: BlockNumber,
525
20222
        para_id: ParaId,
526
20222
    ) -> Weight {
527
20222
        if Pallet::<T>::burn_block_production_free_credit_for_para(&para_id).is_err() {
528
397
            let (amount_to_charge, _weight) = T::ProvideBlockProductionCost::block_cost(&para_id);
529
397
            match T::Currency::withdraw(
530
397
                &Self::parachain_tank(para_id),
531
397
                amount_to_charge,
532
397
                WithdrawReasons::FEE,
533
397
                ExistenceRequirement::KeepAlive,
534
397
            ) {
535
320
                Err(e) => log::warn!(
536
318
                    "Failed to withdraw block production payment for container chain {}: {:?}",
537
318
                    u32::from(para_id),
538
                    e
539
                ),
540
77
                Ok(imbalance) => {
541
77
                    T::OnChargeForBlock::on_unbalanced(imbalance);
542
77
                }
543
            }
544
19825
        }
545

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

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

            
572
2282
        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
68
            if MaxTip::<T>::get(para_id).is_some() {
576
55
                match T::Currency::withdraw(
577
55
                    &Self::parachain_tank(para_id),
578
55
                    tip,
579
55
                    WithdrawReasons::TIP,
580
55
                    ExistenceRequirement::KeepAlive,
581
55
                ) {
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
54
                    Ok(tip_imbalance) => {
593
54
                        Self::deposit_event(Event::<T>::CollatorAssignmentTipCollected {
594
54
                            para_id,
595
54
                            payer: Self::parachain_tank(para_id),
596
54
                            tip,
597
54
                        });
598
54
                        T::OnChargeForCollatorAssignmentTip::on_unbalanced(tip_imbalance);
599
54
                    }
600
                }
601
13
            }
602
2214
        }
603

            
604
2281
        if let Some(assignment_imbalance) = maybe_assignment_imbalance {
605
31
            T::OnChargeForCollatorAssignment::on_unbalanced(assignment_imbalance);
606
2251
        }
607

            
608
2281
        Ok(T::WeightInfo::on_collators_assigned())
609
2283
    }
610
}
611

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

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

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

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

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