1use crate::{
21 storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, Event, HoldReason,
22 Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET,
23};
24
25use alloc::vec::Vec;
26use core::{fmt::Debug, marker::PhantomData};
27use frame_support::{
28 ensure,
29 traits::{
30 fungible::{Mutate, MutateHold},
31 tokens::{
32 Fortitude, Fortitude::Polite, Precision, Preservation, Restriction, WithdrawConsequence,
33 },
34 Get,
35 },
36 DefaultNoBound, RuntimeDebugNoBound,
37};
38use sp_runtime::{
39 traits::{Saturating, Zero},
40 DispatchError, FixedPointNumber, FixedU128,
41};
42
43pub type DepositOf<T> = Deposit<BalanceOf<T>>;
45
46pub type Meter<T> = RawMeter<T, ReservingExt, Root>;
48
49pub type NestedMeter<T> = RawMeter<T, ReservingExt, Nested>;
51
52pub type GenericMeter<T, S> = RawMeter<T, ReservingExt, S>;
56
57pub trait Ext<T: Config> {
61 fn check_limit(
73 origin: &T::AccountId,
74 limit: Option<BalanceOf<T>>,
75 min_leftover: BalanceOf<T>,
76 ) -> Result<BalanceOf<T>, DispatchError>;
77 fn charge(
85 origin: &T::AccountId,
86 contract: &T::AccountId,
87 amount: &DepositOf<T>,
88 state: &ContractState<T>,
89 ) -> Result<(), DispatchError>;
90}
91
92pub enum ReservingExt {}
96
97pub trait State: private::Sealed {}
101
102#[derive(Default, Debug)]
104pub struct Root;
105
106#[derive(DefaultNoBound, RuntimeDebugNoBound)]
109pub enum Nested {
110 #[default]
111 DerivedLimit,
112 OwnLimit,
113}
114
115impl State for Root {}
116impl State for Nested {}
117
118#[derive(DefaultNoBound, RuntimeDebugNoBound)]
120pub struct RawMeter<T: Config, E, S: State + Default + Debug> {
121 limit: BalanceOf<T>,
123 total_deposit: DepositOf<T>,
125 own_contribution: Contribution<T>,
127 charges: Vec<Charge<T>>,
132 nested: S,
134 _phantom: PhantomData<E>,
136}
137
138#[derive(Default, RuntimeDebugNoBound)]
140pub struct Diff {
141 pub bytes_added: u32,
143 pub bytes_removed: u32,
145 pub items_added: u32,
147 pub items_removed: u32,
149}
150
151impl Diff {
152 pub fn update_contract<T: Config>(&self, info: Option<&mut ContractInfo<T>>) -> DepositOf<T> {
161 let per_byte = T::DepositPerByte::get();
162 let per_item = T::DepositPerItem::get();
163 let bytes_added = self.bytes_added.saturating_sub(self.bytes_removed);
164 let items_added = self.items_added.saturating_sub(self.items_removed);
165 let mut bytes_deposit = Deposit::Charge(per_byte.saturating_mul((bytes_added).into()));
166 let mut items_deposit = Deposit::Charge(per_item.saturating_mul((items_added).into()));
167
168 let info = if let Some(info) = info {
170 info
171 } else {
172 debug_assert_eq!(self.bytes_removed, 0);
173 debug_assert_eq!(self.items_removed, 0);
174 return bytes_deposit.saturating_add(&items_deposit)
175 };
176
177 let bytes_removed = self.bytes_removed.saturating_sub(self.bytes_added);
179 let items_removed = self.items_removed.saturating_sub(self.items_added);
180 let ratio = FixedU128::checked_from_rational(bytes_removed, info.storage_bytes)
181 .unwrap_or_default()
182 .min(FixedU128::from_u32(1));
183 bytes_deposit = bytes_deposit
184 .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_byte_deposit)));
185 let ratio = FixedU128::checked_from_rational(items_removed, info.storage_items)
186 .unwrap_or_default()
187 .min(FixedU128::from_u32(1));
188 items_deposit = items_deposit
189 .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_item_deposit)));
190
191 info.storage_bytes =
193 info.storage_bytes.saturating_add(bytes_added).saturating_sub(bytes_removed);
194 info.storage_items =
195 info.storage_items.saturating_add(items_added).saturating_sub(items_removed);
196 match &bytes_deposit {
197 Deposit::Charge(amount) =>
198 info.storage_byte_deposit = info.storage_byte_deposit.saturating_add(*amount),
199 Deposit::Refund(amount) =>
200 info.storage_byte_deposit = info.storage_byte_deposit.saturating_sub(*amount),
201 }
202 match &items_deposit {
203 Deposit::Charge(amount) =>
204 info.storage_item_deposit = info.storage_item_deposit.saturating_add(*amount),
205 Deposit::Refund(amount) =>
206 info.storage_item_deposit = info.storage_item_deposit.saturating_sub(*amount),
207 }
208
209 bytes_deposit.saturating_add(&items_deposit)
210 }
211}
212
213impl Diff {
214 fn saturating_add(&self, rhs: &Self) -> Self {
215 Self {
216 bytes_added: self.bytes_added.saturating_add(rhs.bytes_added),
217 bytes_removed: self.bytes_removed.saturating_add(rhs.bytes_removed),
218 items_added: self.items_added.saturating_add(rhs.items_added),
219 items_removed: self.items_removed.saturating_add(rhs.items_removed),
220 }
221 }
222}
223
224#[derive(RuntimeDebugNoBound, Clone, PartialEq, Eq)]
228pub enum ContractState<T: Config> {
229 Alive,
230 Terminated { beneficiary: AccountIdOf<T> },
231}
232
233#[derive(RuntimeDebugNoBound, Clone)]
243struct Charge<T: Config> {
244 contract: T::AccountId,
245 amount: DepositOf<T>,
246 state: ContractState<T>,
247}
248
249#[derive(RuntimeDebugNoBound)]
251enum Contribution<T: Config> {
252 Alive(Diff),
254 Checked(DepositOf<T>),
257 Terminated { deposit: DepositOf<T>, beneficiary: AccountIdOf<T> },
261}
262
263impl<T: Config> Contribution<T> {
264 fn update_contract(&self, info: Option<&mut ContractInfo<T>>) -> DepositOf<T> {
266 match self {
267 Self::Alive(diff) => diff.update_contract::<T>(info),
268 Self::Terminated { deposit, beneficiary: _ } | Self::Checked(deposit) =>
269 deposit.clone(),
270 }
271 }
272}
273
274impl<T: Config> Default for Contribution<T> {
275 fn default() -> Self {
276 Self::Alive(Default::default())
277 }
278}
279
280impl<T, E, S> RawMeter<T, E, S>
282where
283 T: Config,
284 E: Ext<T>,
285 S: State + Default + Debug,
286{
287 pub fn nested(&self, limit: BalanceOf<T>) -> RawMeter<T, E, Nested> {
294 debug_assert!(matches!(self.contract_state(), ContractState::Alive));
295 let limit = self.available().min(limit);
298 if limit.is_zero() {
299 RawMeter { limit: self.available(), ..Default::default() }
300 } else {
301 RawMeter { limit, nested: Nested::OwnLimit, ..Default::default() }
302 }
303 }
304
305 pub fn absorb(
321 &mut self,
322 absorbed: RawMeter<T, E, Nested>,
323 contract: &T::AccountId,
324 info: Option<&mut ContractInfo<T>>,
325 ) {
326 let own_deposit = absorbed.own_contribution.update_contract(info);
327 self.total_deposit = self
328 .total_deposit
329 .saturating_add(&absorbed.total_deposit)
330 .saturating_add(&own_deposit);
331 self.charges.extend_from_slice(&absorbed.charges);
332 if !own_deposit.is_zero() {
333 self.charges.push(Charge {
334 contract: contract.clone(),
335 amount: own_deposit,
336 state: absorbed.contract_state(),
337 });
338 }
339 }
340
341 fn available(&self) -> BalanceOf<T> {
343 self.total_deposit.available(&self.limit)
344 }
345
346 fn contract_state(&self) -> ContractState<T> {
348 match &self.own_contribution {
349 Contribution::Terminated { deposit: _, beneficiary } =>
350 ContractState::Terminated { beneficiary: beneficiary.clone() },
351 _ => ContractState::Alive,
352 }
353 }
354}
355
356impl<T, E> RawMeter<T, E, Root>
358where
359 T: Config,
360 E: Ext<T>,
361{
362 pub fn new(
366 origin: &Origin<T>,
367 limit: Option<BalanceOf<T>>,
368 min_leftover: BalanceOf<T>,
369 ) -> Result<Self, DispatchError> {
370 return match origin {
372 Origin::Root => Ok(Self {
373 limit: limit.unwrap_or(T::DefaultDepositLimit::get()),
374 ..Default::default()
375 }),
376 Origin::Signed(o) => {
377 let limit = E::check_limit(o, limit, min_leftover)?;
378 Ok(Self { limit, ..Default::default() })
379 },
380 }
381 }
382
383 pub fn try_into_deposit(self, origin: &Origin<T>) -> Result<DepositOf<T>, DispatchError> {
390 let origin = match origin {
392 Origin::Root => return Ok(Deposit::Charge(Zero::zero())),
393 Origin::Signed(o) => o,
394 };
395 for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_))) {
396 E::charge(origin, &charge.contract, &charge.amount, &charge.state)?;
397 }
398 for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Charge(_))) {
399 E::charge(origin, &charge.contract, &charge.amount, &charge.state)?;
400 }
401 Ok(self.total_deposit)
402 }
403}
404
405impl<T, E> RawMeter<T, E, Nested>
407where
408 T: Config,
409 E: Ext<T>,
410{
411 pub fn charge(&mut self, diff: &Diff) {
413 match &mut self.own_contribution {
414 Contribution::Alive(own) => *own = own.saturating_add(diff),
415 _ => panic!("Charge is never called after termination; qed"),
416 };
417 }
418
419 pub fn charge_deposit(&mut self, contract: T::AccountId, amount: DepositOf<T>) {
426 self.total_deposit = self.total_deposit.saturating_add(&amount);
427 self.charges.push(Charge { contract, amount, state: ContractState::Alive });
428 }
429
430 pub fn charge_instantiate(
434 &mut self,
435 origin: &T::AccountId,
436 contract: &T::AccountId,
437 contract_info: &mut ContractInfo<T>,
438 code_info: &CodeInfo<T>,
439 ) -> Result<(), DispatchError> {
440 debug_assert!(matches!(self.contract_state(), ContractState::Alive));
441
442 let ed = Pallet::<T>::min_balance();
444 self.total_deposit = Deposit::Charge(ed);
445 T::Currency::transfer(origin, contract, ed, Preservation::Preserve)?;
446
447 System::<T>::inc_consumers(contract)?;
451
452 let deposit = contract_info.update_base_deposit(&code_info);
453 let deposit = Deposit::Charge(deposit);
454
455 self.charge_deposit(contract.clone(), deposit);
456 Ok(())
457 }
458
459 pub fn terminate(&mut self, info: &ContractInfo<T>, beneficiary: T::AccountId) {
465 debug_assert!(matches!(self.contract_state(), ContractState::Alive));
466 self.own_contribution = Contribution::Terminated {
467 deposit: Deposit::Refund(info.total_deposit()),
468 beneficiary,
469 };
470 }
471
472 pub fn enforce_limit(
482 &mut self,
483 info: Option<&mut ContractInfo<T>>,
484 ) -> Result<(), DispatchError> {
485 let deposit = self.own_contribution.update_contract(info);
486 let total_deposit = self.total_deposit.saturating_add(&deposit);
487 if matches!(self.contract_state(), ContractState::Alive) {
489 self.own_contribution = Contribution::Checked(deposit);
490 }
491 if let Deposit::Charge(amount) = total_deposit {
492 if amount > self.limit {
493 return Err(<Error<T>>::StorageDepositLimitExhausted.into())
494 }
495 }
496 Ok(())
497 }
498
499 pub fn enforce_subcall_limit(
502 &mut self,
503 info: Option<&mut ContractInfo<T>>,
504 ) -> Result<(), DispatchError> {
505 match self.nested {
506 Nested::OwnLimit => self.enforce_limit(info),
507 Nested::DerivedLimit => Ok(()),
508 }
509 }
510}
511
512impl<T: Config> Ext<T> for ReservingExt {
513 fn check_limit(
514 origin: &T::AccountId,
515 limit: Option<BalanceOf<T>>,
516 min_leftover: BalanceOf<T>,
517 ) -> Result<BalanceOf<T>, DispatchError> {
518 let max = T::Currency::reducible_balance(origin, Preservation::Preserve, Polite)
522 .saturating_sub(min_leftover)
523 .saturating_sub(Pallet::<T>::min_balance());
524 let default = max.min(T::DefaultDepositLimit::get());
525 let limit = limit.unwrap_or(default);
526 ensure!(
527 limit <= max &&
528 matches!(T::Currency::can_withdraw(origin, limit), WithdrawConsequence::Success),
529 <Error<T>>::StorageDepositNotEnoughFunds,
530 );
531 Ok(limit)
532 }
533
534 fn charge(
535 origin: &T::AccountId,
536 contract: &T::AccountId,
537 amount: &DepositOf<T>,
538 state: &ContractState<T>,
539 ) -> Result<(), DispatchError> {
540 match amount {
541 Deposit::Charge(amount) | Deposit::Refund(amount) if amount.is_zero() => return Ok(()),
542 Deposit::Charge(amount) => {
543 T::Currency::transfer_and_hold(
546 &HoldReason::StorageDepositReserve.into(),
547 origin,
548 contract,
549 *amount,
550 Precision::Exact,
551 Preservation::Preserve,
552 Fortitude::Polite,
553 )?;
554
555 Pallet::<T>::deposit_event(Event::StorageDepositTransferredAndHeld {
556 from: origin.clone(),
557 to: contract.clone(),
558 amount: *amount,
559 });
560 },
561 Deposit::Refund(amount) => {
562 let transferred = T::Currency::transfer_on_hold(
563 &HoldReason::StorageDepositReserve.into(),
564 contract,
565 origin,
566 *amount,
567 Precision::BestEffort,
568 Restriction::Free,
569 Fortitude::Polite,
570 )?;
571
572 Pallet::<T>::deposit_event(Event::StorageDepositTransferredAndReleased {
573 from: contract.clone(),
574 to: origin.clone(),
575 amount: transferred,
576 });
577
578 if transferred < *amount {
579 log::error!(
583 target: LOG_TARGET,
584 "Failed to repatriate full storage deposit {:?} from contract {:?} to origin {:?}. Transferred {:?}.",
585 amount, contract, origin, transferred,
586 );
587 }
588 },
589 }
590 if let ContractState::<T>::Terminated { beneficiary } = state {
591 System::<T>::dec_consumers(&contract);
592 T::Currency::transfer(
594 &contract,
595 &beneficiary,
596 T::Currency::reducible_balance(&contract, Preservation::Expendable, Polite),
597 Preservation::Expendable,
598 )?;
599 }
600 Ok(())
601 }
602}
603
604mod private {
605 pub trait Sealed {}
606 impl Sealed for super::Root {}
607 impl Sealed for super::Nested {}
608}
609
610#[cfg(test)]
611mod tests {
612 use super::*;
613 use crate::{
614 exec::AccountIdOf,
615 tests::{Test, ALICE, BOB, CHARLIE},
616 };
617 use frame_support::parameter_types;
618 use pretty_assertions::assert_eq;
619
620 type TestMeter = RawMeter<Test, TestExt, Root>;
621
622 parameter_types! {
623 static TestExtTestValue: TestExt = Default::default();
624 }
625
626 #[derive(Debug, PartialEq, Eq, Clone)]
627 struct LimitCheck {
628 origin: AccountIdOf<Test>,
629 limit: BalanceOf<Test>,
630 min_leftover: BalanceOf<Test>,
631 }
632
633 #[derive(Debug, PartialEq, Eq, Clone)]
634 struct Charge {
635 origin: AccountIdOf<Test>,
636 contract: AccountIdOf<Test>,
637 amount: DepositOf<Test>,
638 state: ContractState<Test>,
639 }
640
641 #[derive(Default, Debug, PartialEq, Eq, Clone)]
642 pub struct TestExt {
643 limit_checks: Vec<LimitCheck>,
644 charges: Vec<Charge>,
645 }
646
647 impl TestExt {
648 fn clear(&mut self) {
649 self.limit_checks.clear();
650 self.charges.clear();
651 }
652 }
653
654 impl Ext<Test> for TestExt {
655 fn check_limit(
656 origin: &AccountIdOf<Test>,
657 limit: Option<BalanceOf<Test>>,
658 min_leftover: BalanceOf<Test>,
659 ) -> Result<BalanceOf<Test>, DispatchError> {
660 let limit = limit.unwrap_or(42);
661 TestExtTestValue::mutate(|ext| {
662 ext.limit_checks
663 .push(LimitCheck { origin: origin.clone(), limit, min_leftover })
664 });
665 Ok(limit)
666 }
667
668 fn charge(
669 origin: &AccountIdOf<Test>,
670 contract: &AccountIdOf<Test>,
671 amount: &DepositOf<Test>,
672 state: &ContractState<Test>,
673 ) -> Result<(), DispatchError> {
674 TestExtTestValue::mutate(|ext| {
675 ext.charges.push(Charge {
676 origin: origin.clone(),
677 contract: contract.clone(),
678 amount: amount.clone(),
679 state: state.clone(),
680 })
681 });
682 Ok(())
683 }
684 }
685
686 fn clear_ext() {
687 TestExtTestValue::mutate(|ext| ext.clear())
688 }
689
690 struct ChargingTestCase {
691 origin: Origin<Test>,
692 deposit: DepositOf<Test>,
693 expected: TestExt,
694 }
695
696 #[derive(Default)]
697 struct StorageInfo {
698 bytes: u32,
699 items: u32,
700 bytes_deposit: BalanceOf<Test>,
701 items_deposit: BalanceOf<Test>,
702 }
703
704 fn new_info(info: StorageInfo) -> ContractInfo<Test> {
705 ContractInfo::<Test> {
706 trie_id: Default::default(),
707 code_hash: Default::default(),
708 storage_bytes: info.bytes,
709 storage_items: info.items,
710 storage_byte_deposit: info.bytes_deposit,
711 storage_item_deposit: info.items_deposit,
712 storage_base_deposit: Default::default(),
713 delegate_dependencies: Default::default(),
714 }
715 }
716
717 #[test]
718 fn new_reserves_balance_works() {
719 clear_ext();
720
721 TestMeter::new(&Origin::from_account_id(ALICE), Some(1_000), 0).unwrap();
722
723 assert_eq!(
724 TestExtTestValue::get(),
725 TestExt {
726 limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }],
727 ..Default::default()
728 }
729 )
730 }
731
732 #[test]
733 fn empty_charge_works() {
734 clear_ext();
735
736 let mut meter = TestMeter::new(&Origin::from_account_id(ALICE), Some(1_000), 0).unwrap();
737 assert_eq!(meter.available(), 1_000);
738
739 let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
741 nested0.charge(&Default::default());
742 meter.absorb(nested0, &BOB, None);
743
744 assert_eq!(
745 TestExtTestValue::get(),
746 TestExt {
747 limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }],
748 ..Default::default()
749 }
750 )
751 }
752
753 #[test]
754 fn charging_works() {
755 let test_cases = vec![
756 ChargingTestCase {
757 origin: Origin::<Test>::from_account_id(ALICE),
758 deposit: Deposit::Refund(28),
759 expected: TestExt {
760 limit_checks: vec![LimitCheck { origin: ALICE, limit: 100, min_leftover: 0 }],
761 charges: vec![
762 Charge {
763 origin: ALICE,
764 contract: CHARLIE,
765 amount: Deposit::Refund(10),
766 state: ContractState::Alive,
767 },
768 Charge {
769 origin: ALICE,
770 contract: CHARLIE,
771 amount: Deposit::Refund(20),
772 state: ContractState::Alive,
773 },
774 Charge {
775 origin: ALICE,
776 contract: BOB,
777 amount: Deposit::Charge(2),
778 state: ContractState::Alive,
779 },
780 ],
781 },
782 },
783 ChargingTestCase {
784 origin: Origin::<Test>::Root,
785 deposit: Deposit::Charge(0),
786 expected: TestExt { limit_checks: vec![], charges: vec![] },
787 },
788 ];
789
790 for test_case in test_cases {
791 clear_ext();
792
793 let mut meter = TestMeter::new(&test_case.origin, Some(100), 0).unwrap();
794 assert_eq!(meter.available(), 100);
795
796 let mut nested0_info = new_info(StorageInfo {
797 bytes: 100,
798 items: 5,
799 bytes_deposit: 100,
800 items_deposit: 10,
801 });
802 let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
803 nested0.charge(&Diff {
804 bytes_added: 108,
805 bytes_removed: 5,
806 items_added: 1,
807 items_removed: 2,
808 });
809 nested0.charge(&Diff { bytes_removed: 99, ..Default::default() });
810
811 let mut nested1_info = new_info(StorageInfo {
812 bytes: 100,
813 items: 10,
814 bytes_deposit: 100,
815 items_deposit: 20,
816 });
817 let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
818 nested1.charge(&Diff { items_removed: 5, ..Default::default() });
819 nested0.absorb(nested1, &CHARLIE, Some(&mut nested1_info));
820
821 let mut nested2_info = new_info(StorageInfo {
822 bytes: 100,
823 items: 7,
824 bytes_deposit: 100,
825 items_deposit: 20,
826 });
827 let mut nested2 = nested0.nested(BalanceOf::<Test>::zero());
828 nested2.charge(&Diff { items_removed: 7, ..Default::default() });
829 nested0.absorb(nested2, &CHARLIE, Some(&mut nested2_info));
830
831 nested0.enforce_limit(Some(&mut nested0_info)).unwrap();
832 meter.absorb(nested0, &BOB, Some(&mut nested0_info));
833
834 assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit);
835
836 assert_eq!(nested0_info.extra_deposit(), 112);
837 assert_eq!(nested1_info.extra_deposit(), 110);
838 assert_eq!(nested2_info.extra_deposit(), 100);
839
840 assert_eq!(TestExtTestValue::get(), test_case.expected)
841 }
842 }
843
844 #[test]
845 fn termination_works() {
846 let test_cases = vec![
847 ChargingTestCase {
848 origin: Origin::<Test>::from_account_id(ALICE),
849 deposit: Deposit::Refund(108),
850 expected: TestExt {
851 limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }],
852 charges: vec![
853 Charge {
854 origin: ALICE,
855 contract: CHARLIE,
856 amount: Deposit::Refund(120),
857 state: ContractState::Terminated { beneficiary: CHARLIE },
858 },
859 Charge {
860 origin: ALICE,
861 contract: BOB,
862 amount: Deposit::Charge(12),
863 state: ContractState::Alive,
864 },
865 ],
866 },
867 },
868 ChargingTestCase {
869 origin: Origin::<Test>::Root,
870 deposit: Deposit::Charge(0),
871 expected: TestExt { limit_checks: vec![], charges: vec![] },
872 },
873 ];
874
875 for test_case in test_cases {
876 clear_ext();
877
878 let mut meter = TestMeter::new(&test_case.origin, Some(1_000), 0).unwrap();
879 assert_eq!(meter.available(), 1_000);
880
881 let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
882 nested0.charge(&Diff {
883 bytes_added: 5,
884 bytes_removed: 1,
885 items_added: 3,
886 items_removed: 1,
887 });
888 nested0.charge(&Diff { items_added: 2, ..Default::default() });
889
890 let mut nested1_info = new_info(StorageInfo {
891 bytes: 100,
892 items: 10,
893 bytes_deposit: 100,
894 items_deposit: 20,
895 });
896 let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
897 nested1.charge(&Diff { items_removed: 5, ..Default::default() });
898 nested1.charge(&Diff { bytes_added: 20, ..Default::default() });
899 nested1.terminate(&nested1_info, CHARLIE);
900 nested0.enforce_limit(Some(&mut nested1_info)).unwrap();
901 nested0.absorb(nested1, &CHARLIE, None);
902
903 meter.absorb(nested0, &BOB, None);
904 assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit);
905 assert_eq!(TestExtTestValue::get(), test_case.expected)
906 }
907 }
908}