@@ -4,8 +4,9 @@ use alloy::{sol, sol_types::SolInterface};
44use alloy_evm:: {
55 precompiles:: { Precompile , PrecompileInput } ,
66 revm:: precompile:: { PrecompileError , PrecompileId , PrecompileResult } ,
7+ EvmInternals , EvmInternalsError ,
78} ;
8- use alloy_primitives:: { address, Address , Bytes } ;
9+ use alloy_primitives:: { address, Address , Bytes , U256 } ;
910use revm:: precompile:: PrecompileOutput ;
1011use std:: sync:: OnceLock ;
1112
@@ -20,9 +21,9 @@ pub const MINT_PRECOMPILE_ADDR: Address = address!("0x00000000000000000000000000
2021
2122/// A custom precompile that mints the native token
2223#[ derive( Clone , Debug , Default ) ]
23- pub struct MintPrecompile {
24+ pub struct MintPrecompile {
2425 admin : Address ,
25- } ;
26+ }
2627
2728impl MintPrecompile {
2829 // Use a lazily-initialized static for the ID since `custom` is not const.
@@ -31,17 +32,67 @@ impl MintPrecompile {
3132 ID . get_or_init ( || PrecompileId :: custom ( "native_mint" ) )
3233 }
3334
34- pub fn new ( admin : Address ) -> Self {
35- Self {
36- admin,
37- }
35+ pub fn new ( admin : Address ) -> Self {
36+ Self { admin }
3837 }
3938
40-
41-
4239 fn is_authorized ( & self , caller : Address ) -> bool {
4340 caller == self . admin
4441 }
42+
43+ fn map_internals_error ( err : EvmInternalsError ) -> PrecompileError {
44+ PrecompileError :: Other ( err. to_string ( ) )
45+ }
46+
47+ fn ensure_account_created (
48+ internals : & mut EvmInternals < ' _ > ,
49+ addr : Address ,
50+ ) -> Result < ( ) , PrecompileError > {
51+ let mut account = internals
52+ . load_account ( addr)
53+ . map_err ( Self :: map_internals_error) ?;
54+
55+ if account. is_loaded_as_not_existing ( ) {
56+ account. mark_created ( ) ;
57+ internals. touch_account ( addr) ;
58+ }
59+
60+ Ok ( ( ) )
61+ }
62+
63+ fn add_balance (
64+ internals : & mut EvmInternals < ' _ > ,
65+ addr : Address ,
66+ amount : U256 ,
67+ ) -> Result < ( ) , PrecompileError > {
68+ let mut account = internals
69+ . load_account ( addr)
70+ . map_err ( Self :: map_internals_error) ?;
71+ let new_balance = account
72+ . info
73+ . balance
74+ . checked_add ( amount)
75+ . ok_or_else ( || PrecompileError :: Other ( "balance overflow" . to_string ( ) ) ) ?;
76+ account. info . set_balance ( new_balance) ;
77+ Ok ( ( ) )
78+ }
79+
80+ fn sub_balance (
81+ internals : & mut EvmInternals < ' _ > ,
82+ addr : Address ,
83+ amount : U256 ,
84+ ) -> Result < ( ) , PrecompileError > {
85+ let mut account = internals
86+ . load_account ( addr)
87+ . map_err ( Self :: map_internals_error) ?;
88+ let new_balance = account
89+ . info
90+ . balance
91+ . checked_sub ( amount)
92+ . ok_or_else ( || PrecompileError :: Other ( "insufficient balance" . to_string ( ) ) ) ?;
93+ account. info . set_balance ( new_balance) ;
94+ Ok ( ( ) )
95+ }
4596}
4697
4798impl Precompile for MintPrecompile {
@@ -50,30 +101,44 @@ impl Precompile for MintPrecompile {
50101 }
51102
52103 /// Execute the precompile with the given input data, gas limit, and caller address.
53- fn call ( & self , input : PrecompileInput < ' _ > ) -> PrecompileResult {
104+ fn call ( & self , mut input : PrecompileInput < ' _ > ) -> PrecompileResult {
54105 let caller: Address = input. caller ;
55106
56107 // Enforce access control.
57108 if !self . is_authorized ( caller) {
58109 return Err ( PrecompileError :: Other ( "unauthorized caller" . to_string ( ) ) ) ;
59110 }
111+ let gas_limit = input. gas ;
112+
60113 // 1) Decode by ABI — this inspects the 4-byte selector and picks the right variant.
61114 let decoded = match INativeToken :: INativeTokenCalls :: abi_decode ( input. data ) {
62115 Ok ( v) => v,
63116 Err ( e) => return Err ( PrecompileError :: Other ( e. to_string ( ) ) ) ,
64117 } ;
65118
119+ let internals = input. internals_mut ( ) ;
120+
66121 // 2) Dispatch to the right handler.
67122 match decoded {
68123 INativeToken :: INativeTokenCalls :: mint( call) => {
69- // call.to, call.amount
70- // ... do state changes / balances / checks ...
124+ let to = call. to ;
125+ let amount = call. amount ;
126+
127+ internals. touch_account ( to) ;
128+ Self :: ensure_account_created ( internals, to) ?;
129+ Self :: add_balance ( internals, to, amount) ?;
71130
72- Ok ( PrecompileOutput :: new ( input . gas , Bytes :: new ( ) ) )
131+ Ok ( PrecompileOutput :: new ( gas_limit , Bytes :: new ( ) ) )
73132 }
74133 INativeToken :: INativeTokenCalls :: burn( call) => {
75- // call.from, call.amount
76- Ok ( PrecompileOutput :: new ( input. gas , Bytes :: new ( ) ) )
134+ let from = call. from ;
135+ let amount = call. amount ;
136+
137+ internals. touch_account ( from) ;
138+ Self :: ensure_account_created ( internals, from) ?;
139+ Self :: sub_balance ( internals, from, amount) ?;
140+
141+ Ok ( PrecompileOutput :: new ( gas_limit, Bytes :: new ( ) ) )
77142 }
78143 }
79144 }
0 commit comments