1616
1717use super :: { CodeHash , Config , ContractAddressFor , Event , RawEvent , Trait ,
1818 TrieId , BalanceOf , ContractInfo , TrieIdGenerator } ;
19- use crate :: gas:: { Gas , GasMeter , Token } ;
20- use crate :: rent;
21- use crate :: storage;
19+ use crate :: { gas:: { Gas , GasMeter , Token } , rent, storage, Error , ContractInfoOf } ;
2220use bitflags:: bitflags;
23-
2421use sp_std:: prelude:: * ;
25- use sp_runtime:: traits:: { Bounded , Zero , Convert } ;
22+ use sp_runtime:: traits:: { Bounded , Zero , Convert , Saturating } ;
2623use frame_support:: {
2724 dispatch:: DispatchError ,
2825 traits:: { ExistenceRequirement , Currency , Time , Randomness } ,
2926 weights:: Weight ,
27+ ensure, StorageMap ,
3028} ;
3129
3230pub type AccountIdOf < T > = <T as frame_system:: Trait >:: AccountId ;
@@ -46,6 +44,15 @@ bitflags! {
4644 }
4745}
4846
47+ /// Describes whether we deal with a contract or a plain account.
48+ pub enum TransactorKind {
49+ /// Transaction was initiated from a plain account. That can be either be through a
50+ /// signed transaction or through RPC.
51+ PlainAccount ,
52+ /// The call was initiated by a contract account.
53+ Contract ,
54+ }
55+
4956/// Output of a contract call or instantiation which ran to completion.
5057#[ cfg_attr( test, derive( PartialEq , Eq , Debug ) ) ]
5158pub struct ExecReturnValue {
@@ -320,6 +327,7 @@ where
320327 Err ( "contract has been evicted" ) ?
321328 } ;
322329
330+ let transactor_kind = self . transactor_kind ( ) ;
323331 let caller = self . self_account . clone ( ) ;
324332 let dest_trie_id = contract_info. and_then ( |i| i. as_alive ( ) . map ( |i| i. trie_id . clone ( ) ) ) ;
325333
@@ -328,6 +336,7 @@ where
328336 transfer (
329337 gas_meter,
330338 TransferCause :: Call ,
339+ transactor_kind,
331340 & caller,
332341 & dest,
333342 value,
@@ -372,6 +381,7 @@ where
372381 Err ( "not enough gas to pay base instantiate fee" ) ?
373382 }
374383
384+ let transactor_kind = self . transactor_kind ( ) ;
375385 let caller = self . self_account . clone ( ) ;
376386 let dest = T :: DetermineContractAddress :: contract_address_for (
377387 code_hash,
@@ -399,6 +409,7 @@ where
399409 transfer (
400410 gas_meter,
401411 TransferCause :: Instantiate ,
412+ transactor_kind,
402413 & caller,
403414 & dest,
404415 endowment,
@@ -466,6 +477,17 @@ where
466477 & self . self_account == account ||
467478 self . caller . map_or ( false , |caller| caller. is_live ( account) )
468479 }
480+
481+ fn transactor_kind ( & self ) -> TransactorKind {
482+ if self . depth == 0 {
483+ debug_assert ! ( self . self_trie_id. is_none( ) ) ;
484+ debug_assert ! ( self . caller. is_none( ) ) ;
485+ debug_assert ! ( ContractInfoOf :: <T >:: get( & self . self_account) . is_none( ) ) ;
486+ TransactorKind :: PlainAccount
487+ } else {
488+ TransactorKind :: Contract
489+ }
490+ }
469491}
470492
471493#[ cfg_attr( test, derive( Debug , PartialEq , Eq ) ) ]
@@ -519,13 +541,15 @@ enum TransferCause {
519541fn transfer < ' a , T : Trait , V : Vm < T > , L : Loader < T > > (
520542 gas_meter : & mut GasMeter < T > ,
521543 cause : TransferCause ,
544+ origin : TransactorKind ,
522545 transactor : & T :: AccountId ,
523546 dest : & T :: AccountId ,
524547 value : BalanceOf < T > ,
525548 ctx : & mut ExecutionContext < ' a , T , V , L > ,
526549) -> Result < ( ) , DispatchError > {
527550 use self :: TransferCause :: * ;
528551 use self :: TransferFeeKind :: * ;
552+ use self :: TransactorKind :: * ;
529553
530554 let token = {
531555 let kind: TransferFeeKind = match cause {
@@ -545,10 +569,19 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
545569 Err ( "not enough gas to pay transfer fee" ) ?
546570 }
547571
548- // Only ext_terminate is allowed to bring the sender below the existential deposit
549- let existence_requirement = match cause {
550- Terminate => ExistenceRequirement :: AllowDeath ,
551- _ => ExistenceRequirement :: KeepAlive ,
572+ // Only ext_terminate is allowed to bring the sender below the subsistence
573+ // threshold or even existential deposit.
574+ let existence_requirement = match ( cause, origin) {
575+ ( Terminate , _) => ExistenceRequirement :: AllowDeath ,
576+ ( _, Contract ) => {
577+ ensure ! (
578+ T :: Currency :: total_balance( transactor) . saturating_sub( value) >=
579+ ctx. config. subsistence_threshold( ) ,
580+ Error :: <T >:: InsufficientBalance ,
581+ ) ;
582+ ExistenceRequirement :: KeepAlive
583+ } ,
584+ ( _, PlainAccount ) => ExistenceRequirement :: KeepAlive ,
552585 } ;
553586 T :: Currency :: transfer ( transactor, dest, value, existence_requirement) ?;
554587
@@ -630,6 +663,7 @@ where
630663 transfer (
631664 gas_meter,
632665 TransferCause :: Call ,
666+ TransactorKind :: Contract ,
633667 & self . ctx . self_account . clone ( ) ,
634668 & to,
635669 value,
@@ -654,6 +688,7 @@ where
654688 transfer (
655689 gas_meter,
656690 TransferCause :: Terminate ,
691+ TransactorKind :: Contract ,
657692 & self_id,
658693 beneficiary,
659694 value,
0 commit comments