@@ -56,7 +56,7 @@ use alloy_rpc_types::{
5656 } ,
5757 request:: TransactionRequest ,
5858 simulate:: { SimulatePayload , SimulatedBlock } ,
59- state:: EvmOverrides ,
59+ state:: { AccountOverride , EvmOverrides , StateOverridesBuilder } ,
6060 trace:: {
6161 filter:: TraceFilter ,
6262 geth:: { GethDebugTracingCallOptions , GethDebugTracingOptions , GethTrace } ,
@@ -67,6 +67,7 @@ use alloy_rpc_types::{
6767 EIP1186AccountProofResponse , FeeHistory , Filter , FilteredParams , Index , Log , Work ,
6868} ;
6969use alloy_serde:: WithOtherFields ;
70+ use alloy_sol_types:: { sol, SolCall , SolValue } ;
7071use alloy_transport:: TransportErrorKind ;
7172use anvil_core:: {
7273 eth:: {
@@ -355,6 +356,9 @@ impl EthApi {
355356 EthRequest :: SetBalance ( addr, val) => {
356357 self . anvil_set_balance ( addr, val) . await . to_rpc_result ( )
357358 }
359+ EthRequest :: DealERC20 ( addr, token_addr, val) => {
360+ self . anvil_deal_erc20 ( addr, token_addr, val) . await . to_rpc_result ( )
361+ }
358362 EthRequest :: SetCode ( addr, code) => {
359363 self . anvil_set_code ( addr, code) . await . to_rpc_result ( )
360364 }
@@ -1852,6 +1856,76 @@ impl EthApi {
18521856 Ok ( ( ) )
18531857 }
18541858
1859+ /// Deals ERC20 tokens to a address
1860+ ///
1861+ /// Handler for RPC call: `anvil_dealERC20`
1862+ pub async fn anvil_deal_erc20 (
1863+ & self ,
1864+ address : Address ,
1865+ token_address : Address ,
1866+ balance : U256 ,
1867+ ) -> Result < ( ) > {
1868+ node_info ! ( "anvil_dealERC20" ) ;
1869+
1870+ sol ! {
1871+ #[ sol( rpc) ]
1872+ contract IERC20 {
1873+ function balanceOf( address target) external view returns ( uint256) ;
1874+ }
1875+ }
1876+
1877+ let calldata = IERC20 :: balanceOfCall { target : address } . abi_encode ( ) ;
1878+ let tx = TransactionRequest :: default ( ) . with_to ( token_address) . with_input ( calldata. clone ( ) ) ;
1879+
1880+ // first collect all the slots that are used by the balanceOf call
1881+ let access_list_result =
1882+ self . create_access_list ( WithOtherFields :: new ( tx. clone ( ) ) , None ) . await ?;
1883+ let access_list = access_list_result. access_list ;
1884+
1885+ // now we can iterate over all the accessed slots and try to find the one that contains the
1886+ // balance by overriding the slot and checking the `balanceOfCall` of
1887+ for item in access_list. 0 {
1888+ if item. address != token_address {
1889+ continue ;
1890+ } ;
1891+ for slot in & item. storage_keys {
1892+ let account_override = AccountOverride :: default ( )
1893+ . with_state_diff ( std:: iter:: once ( ( * slot, B256 :: from ( balance. to_be_bytes ( ) ) ) ) ) ;
1894+
1895+ let state_override = StateOverridesBuilder :: default ( )
1896+ . append ( token_address, account_override)
1897+ . build ( ) ;
1898+
1899+ let evm_override = EvmOverrides :: state ( Some ( state_override) ) ;
1900+
1901+ let Ok ( result) =
1902+ self . call ( WithOtherFields :: new ( tx. clone ( ) ) , None , evm_override) . await
1903+ else {
1904+ // overriding this slot failed
1905+ continue ;
1906+ } ;
1907+
1908+ let Ok ( result_balance) = U256 :: abi_decode ( & result) else {
1909+ // response returned something other than a U256
1910+ continue ;
1911+ } ;
1912+
1913+ if result_balance == balance {
1914+ self . anvil_set_storage_at (
1915+ token_address,
1916+ U256 :: from_be_bytes ( slot. 0 ) ,
1917+ B256 :: from ( balance. to_be_bytes ( ) ) ,
1918+ )
1919+ . await ?;
1920+ return Ok ( ( ) ) ;
1921+ }
1922+ }
1923+ }
1924+
1925+ // unable to set the balance
1926+ Err ( BlockchainError :: Message ( "Unable to set ERC20 balance, no slot found" . to_string ( ) ) )
1927+ }
1928+
18551929 /// Sets the code of a contract.
18561930 ///
18571931 /// Handler for RPC call: `anvil_setCode`
0 commit comments