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")
}