@@ -292,6 +292,11 @@ enum ContractRuntimePrecompile {
292292 } ,
293293 /// Calling `validation_round` of `ContractRuntime`
294294 ValidationRound ,
295+ /// Calling `transfer` of `ContractRuntime`
296+ Transfer {
297+ account : Account ,
298+ amount : Amount ,
299+ } ,
295300}
296301
297302/// Some functionalities from the ServiceRuntime not in BaseRuntime
@@ -485,28 +490,62 @@ impl<'a, Runtime: ContractRuntime> PrecompileProvider<Ctx<'a, Runtime>> for Cont
485490 }
486491}
487492
493+ fn get_evm_destination < Runtime : ContractRuntime > (
494+ context : & mut Ctx < ' _ , Runtime > ,
495+ account : Account ,
496+ ) -> Result < Option < Address > , ExecutionError > {
497+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
498+ let chain_id = runtime. chain_id ( ) ?;
499+ if chain_id == account. chain_id {
500+ return Ok ( None ) ;
501+ }
502+ Ok ( account. owner . to_evm_address ( ) )
503+ }
504+
505+
506+ /// If we are using the `None` case of `fn call` and `fn create` then the transfer
507+ /// of ethers is done by REVM. On the other hand, if we are managing the call/create
508+ /// by hand, then we need to do the transfers ourselves.
509+ fn revm_transfer < Runtime : ContractRuntime > (
510+ context : & mut Ctx < ' _ , Runtime > ,
511+ source : Address ,
512+ destination : Address ,
513+ value : U256 ,
514+ ) -> Result < ( ) , ExecutionError > {
515+ // In Ethereum, all transfers matter
516+ if let Some ( error) = context. journal ( ) . transfer ( source, destination, value) ? {
517+ let error = format ! ( "{error:?}" ) ;
518+ let error = EvmExecutionError :: TransactError ( error) ;
519+ return Err ( error. into ( ) ) ;
520+ }
521+ Ok ( ( ) )
522+ }
523+
488524impl < ' a > ContractPrecompile {
489525 fn contract_runtime_call < Runtime : ContractRuntime > (
490526 request : ContractRuntimePrecompile ,
491527 context : & mut Ctx < ' a , Runtime > ,
492528 ) -> Result < Vec < u8 > , ExecutionError > {
493- let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
494529 match request {
495530 ContractRuntimePrecompile :: AuthenticatedOwner => {
531+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
496532 let account_owner = runtime. authenticated_owner ( ) ?;
497533 Ok ( bcs:: to_bytes ( & account_owner) ?)
498534 }
499535
500536 ContractRuntimePrecompile :: MessageOriginChainId => {
537+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
501538 let origin_chain_id = runtime. message_origin_chain_id ( ) ?;
502539 Ok ( bcs:: to_bytes ( & origin_chain_id) ?)
503540 }
504541
505542 ContractRuntimePrecompile :: MessageIsBouncing => {
543+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
506544 let result = runtime. message_is_bouncing ( ) ?;
507545 Ok ( bcs:: to_bytes ( & result) ?)
508546 }
509547 ContractRuntimePrecompile :: AuthenticatedCallerId => {
548+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
510549 let application_id = runtime. authenticated_caller_id ( ) ?;
511550 Ok ( bcs:: to_bytes ( & application_id) ?)
512551 }
@@ -524,27 +563,34 @@ impl<'a> ContractPrecompile {
524563 grant,
525564 message,
526565 } ;
566+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
527567 runtime. send_message ( send_message_request) ?;
528568 Ok ( vec ! [ ] )
529569 }
530570 ContractRuntimePrecompile :: TryCallApplication { target, argument } => {
531571 let authenticated = true ;
572+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
532573 runtime. try_call_application ( authenticated, target, argument)
533574 }
534575 ContractRuntimePrecompile :: Emit { stream_name, value } => {
576+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
535577 let result = runtime. emit ( stream_name, value) ?;
536578 Ok ( bcs:: to_bytes ( & result) ?)
537579 }
538580 ContractRuntimePrecompile :: ReadEvent {
539581 chain_id,
540582 stream_name,
541583 index,
542- } => runtime. read_event ( chain_id, stream_name, index) ,
584+ } => {
585+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
586+ runtime. read_event ( chain_id, stream_name, index)
587+ }
543588 ContractRuntimePrecompile :: SubscribeToEvents {
544589 chain_id,
545590 application_id,
546591 stream_name,
547592 } => {
593+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
548594 runtime. subscribe_to_events ( chain_id, application_id, stream_name) ?;
549595 Ok ( vec ! [ ] )
550596 }
@@ -553,17 +599,40 @@ impl<'a> ContractPrecompile {
553599 application_id,
554600 stream_name,
555601 } => {
602+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
556603 runtime. unsubscribe_from_events ( chain_id, application_id, stream_name) ?;
557604 Ok ( vec ! [ ] )
558605 }
559606 ContractRuntimePrecompile :: QueryService {
560607 application_id,
561608 query,
562- } => runtime. query_service ( application_id, query) ,
609+ } => {
610+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
611+ runtime. query_service ( application_id, query)
612+ }
563613 ContractRuntimePrecompile :: ValidationRound => {
614+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
564615 let value = runtime. validation_round ( ) ?;
565616 Ok ( bcs:: to_bytes ( & value) ?)
566617 }
618+ ContractRuntimePrecompile :: Transfer { account, amount } => {
619+ if amount != Amount :: ZERO {
620+ let destination = {
621+ let destination = get_evm_destination ( context, account) ?;
622+ destination. unwrap_or ( FAUCET_ADDRESS )
623+ } ;
624+ let application_id = {
625+ let mut runtime = context. db ( ) . 0 . runtime . lock ( ) . unwrap ( ) ;
626+ let application_id = runtime. application_id ( ) ?;
627+ let source = application_id. into ( ) ;
628+ runtime. transfer ( source, account, amount) ?;
629+ application_id
630+ } ;
631+ let source: Address = application_id. evm_address ( ) ;
632+ revm_transfer ( context, source, destination, amount. into ( ) ) ?;
633+ }
634+ Ok ( vec ! [ ] )
635+ }
567636 }
568637 }
569638
@@ -892,7 +961,7 @@ impl<Runtime: ContractRuntime> CallInterceptorContract<Runtime> {
892961 // a contract that do not yet exist.
893962 // It is a common construction. We can see that in ERC20 contract code
894963 // for example for burning and minting.
895- Self :: revm_transfer (
964+ revm_transfer (
896965 context,
897966 self . db . contract_address ,
898967 FAUCET_ADDRESS ,
@@ -994,7 +1063,7 @@ impl<Runtime: ContractRuntime> CallInterceptorContract<Runtime> {
9941063 let destination = Account { chain_id, owner } ;
9951064 runtime. transfer ( source, destination, amount) ?;
9961065 }
997- Self :: revm_transfer ( context, inputs. caller , inputs. target_address , value) ?;
1066+ revm_transfer ( context, inputs. caller , inputs. target_address , value) ?;
9981067 }
9991068 }
10001069 // Other smart contracts calls are handled by the runtime
@@ -1032,24 +1101,6 @@ impl<Runtime: ContractRuntime> CallInterceptorContract<Runtime> {
10321101 } ;
10331102 Ok ( Some ( call_outcome) )
10341103 }
1035-
1036- /// If we are using the `None` case of `fn call` and `fn create` then the transfer
1037- /// of ethers is done by REVM. On the other hand, if we are managing the call/create
1038- /// by hand, then we need to do the transfers ourselves.
1039- fn revm_transfer (
1040- context : & mut Ctx < ' _ , Runtime > ,
1041- source : Address ,
1042- destination : Address ,
1043- value : U256 ,
1044- ) -> Result < ( ) , ExecutionError > {
1045- // In Ethereum, all transfers matter
1046- if let Some ( error) = context. journal ( ) . transfer ( source, destination, value) ? {
1047- let error = format ! ( "{error:?}" ) ;
1048- let error = EvmExecutionError :: TransactError ( error) ;
1049- return Err ( error. into ( ) ) ;
1050- }
1051- Ok ( ( ) )
1052- }
10531104}
10541105
10551106struct CallInterceptorService < Runtime > {
0 commit comments