diff --git a/crates/integration/contracts/Value.sol b/crates/integration/contracts/Value.sol index 99c195b3..5fa6bd81 100644 --- a/crates/integration/contracts/Value.sol +++ b/crates/integration/contracts/Value.sol @@ -6,4 +6,8 @@ contract Value { function value() public payable returns (uint ret) { ret = msg.value; } + + function balance_of(address _address) public view returns (uint ret) { + ret = _address.balance; + } } diff --git a/crates/integration/src/cases.rs b/crates/integration/src/cases.rs index e7b7f51b..282d83ec 100644 --- a/crates/integration/src/cases.rs +++ b/crates/integration/src/cases.rs @@ -148,6 +148,12 @@ sol!( } ); +sol!( + contract Value { + function balance_of(address _address) public view returns (uint ret); + } +); + impl Contract { /// Execute the contract. /// @@ -490,6 +496,18 @@ impl Contract { calldata: Default::default(), } } + + pub fn value_balance_of(address: Address) -> Self { + let code = include_str!("../contracts/Value.sol"); + let name = "Value"; + + Self { + name, + evm_runtime: crate::compile_evm_bin_runtime(name, code), + pvm_runtime: crate::compile_blob(name, code), + calldata: Value::balance_ofCall::new((address,)).abi_encode(), + } + } } #[cfg(test)] diff --git a/crates/integration/src/mock_runtime.rs b/crates/integration/src/mock_runtime.rs index 17e27f47..fb05f198 100644 --- a/crates/integration/src/mock_runtime.rs +++ b/crates/integration/src/mock_runtime.rs @@ -350,6 +350,10 @@ impl State { pub fn accounts(&self) -> &HashMap { &self.accounts } + + pub fn accounts_mut(&mut self) -> &mut HashMap { + &mut self.accounts + } } fn link_host_functions(engine: &Engine) -> Linker { @@ -902,6 +906,31 @@ fn link_host_functions(engine: &Engine) -> Linker { ) .unwrap(); + linker + .func_wrap( + runtime_api::imports::BALANCE, + |caller: Caller, address_ptr: u32, balance_ptr: u32| -> Result<(), Trap> { + let (mut caller, transaction) = caller.split(); + + let bytes = caller.read_memory_into_vec(address_ptr, 32)?; + let word = U256::from_le_slice(&bytes); + let address = Address::from_word(word.into()); + let balance = transaction + .state + .accounts() + .get(&address) + .map(|account| account.value) + .unwrap_or(U256::default()); + + caller.write_memory(balance_ptr, &balance.to_le_bytes::<32>())?; + + log::info!("account {address} balance {balance}"); + + Ok(()) + }, + ) + .unwrap(); + linker } diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index 8f9390fd..0e359db6 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -565,3 +565,28 @@ fn mcopy() { assert_eq!(expected, received); } + +#[test] +fn balance() { + let (_, output) = assert_success(&Contract::value_balance_of(Default::default()), false); + + let expected = U256::ZERO; + let received = U256::from_be_slice(&output.data); + assert_eq!(expected, received); + + let expected = U256::from(54589); + let (mut state, address) = State::new_deployed(Contract::value_balance_of(Default::default())); + state.accounts_mut().get_mut(&address).unwrap().value = expected; + + let contract = Contract::value_balance_of(address); + let (_, output) = state + .transaction() + .with_default_account(&contract.pvm_runtime) + .calldata(contract.calldata) + .call(); + + assert_eq!(ReturnFlags::Success, output.flags); + + let received = U256::from_be_slice(&output.data); + assert_eq!(expected, received) +} diff --git a/crates/llvm-context/src/polkavm/const/runtime_api.rs b/crates/llvm-context/src/polkavm/const/runtime_api.rs index 4b5ae89c..01b65acc 100644 --- a/crates/llvm-context/src/polkavm/const/runtime_api.rs +++ b/crates/llvm-context/src/polkavm/const/runtime_api.rs @@ -15,6 +15,8 @@ pub mod exports { pub mod imports { pub static ADDRESS: &str = "address"; + pub static BALANCE: &str = "balance"; + pub static BLOCK_NUMBER: &str = "block_number"; pub static CALL: &str = "seal_call"; @@ -45,8 +47,9 @@ pub mod imports { /// All imported runtime API symbols. /// Useful for configuring common attributes and linkage. - pub static IMPORTS: [&str; 15] = [ + pub static IMPORTS: [&str; 16] = [ ADDRESS, + BALANCE, BLOCK_NUMBER, CALL, CALLER, diff --git a/crates/llvm-context/src/polkavm/evm/ether_gas.rs b/crates/llvm-context/src/polkavm/evm/ether_gas.rs index 6949cbf7..5d2ac518 100644 --- a/crates/llvm-context/src/polkavm/evm/ether_gas.rs +++ b/crates/llvm-context/src/polkavm/evm/ether_gas.rs @@ -41,11 +41,31 @@ where /// Translates the `balance` instructions. pub fn balance<'ctx, D>( - _context: &mut Context<'ctx, D>, - _address: inkwell::values::IntValue<'ctx>, + context: &mut Context<'ctx, D>, + address: inkwell::values::IntValue<'ctx>, ) -> anyhow::Result> where D: Dependency + Clone, { - todo!() + let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer"); + let address_pointer = context.build_alloca(context.word_type(), "address_pointer"); + context.build_store(address_pointer, address)?; + + let balance = context.builder().build_ptr_to_int( + balance_pointer.value, + context.xlen_type(), + "balance", + )?; + let address = context.builder().build_ptr_to_int( + address_pointer.value, + context.xlen_type(), + "address", + )?; + + context.build_runtime_call( + runtime_api::imports::BALANCE, + &[address.into(), balance.into()], + ); + + context.build_load(balance_pointer, "balance") }