Ethereum Foundry Script: Balance Mismatch Error
As a developer working with Ethereum smart contracts, it’s essential to ensure that your scripts are running correctly and accurately tracking account balances. However, there’s often an oversight that can lead to issues like balance mismatches. In this article, we’ll delve into the balance mismatch
error in Foundry and provide guidance on how to reproduce and resolve it.
What is a Balance Mismatch?
A balance mismatch occurs when the actual balance of an account on the blockchain differs from its shown balance displayed by the caller of the function used to track that account’s balance. This can happen for various reasons, including:
- Incorrect transactions being broadcasted
- Conflicting balances between accounts (e.g., due to reentrancy attacks)
- Incorrect contract code or logic
Foundry Script Reproduction
The following foundry script demonstrates a balance mismatch error:
contract MyContract {
fn myFunction() -> Balance {
// Simulate some transactions
let tx1 = Tx::new(&[Address::from(0x123456789)), 100);
let tx2 = Tx::new(&[Address::from(0x234567890)], 200);
foundry_script {
vm.startBroadcast(Address::from(0x345678901));
vm.gather();
Balance::new().balance() // Should return the balance of caller
.if_eq(expectedBalance);
expectedBalance
}
}
fn myFunction2() -> Balance {
let tx1 = Tx::new(&[Address::from(0x123456789)], 100);
foundry_script {
vm.startBroadcast(Address::from(0x234567890));
vm.gather();
let balance = expectedBalance;
assert!(balance.balance().if_eq(expectedBalance));
balance
}
}
fn myFunction3() -> Balance {
let tx1 = Tx::new(&[Address::from(0x123456789)], 100);
foundry_script {
vm.startBroadcast(Address::from(0x234567890));
vm.gather();
let balance = expectedBalance;
assert!(balance.balance().if_eq(expectedBalance));
balance
}
}
fn expectedBalance() -> Balance {
// Return the expected balance based on some logic (e.g., a fixed value)
Balance::new().balance()
}
}
In this script, we have three functions myFunction
, myFunction2
, and myFunction3
that simulate transactions and track their balances. The error occurs when calling these functions from the foundry_script
. Specifically, the vm.startBroadcast()
function is called with an address as a parameter, but the caller of this function does not have the same balance.
Why Does this Error Occur?
The issue arises because the foundry_script
expects the account to be in a specific state (e.g., not being broadcasted yet). However, when calling these functions from foundry_script
, the caller’s address has already been broadcast. As a result, vm.startBroadcast()
does not return an empty address, leading to incorrect balances being tracked.
Solution: Use vm.startCall
To fix this error, we need to use vm.startCall
instead of vm.startBroadcast
. Here’s the updated script:
“`rust
contract MyContract {
fn myFunction() -> Balance {
// Simulate some transactions
let tx1 = Tx::new(&[Address::from(0x123456789)], 100);
let tx2 = Tx::new(&[Address::from(0x234567890)], 200);
vm.startCall(Address::from(0x345678901))
.withArg(tx1)
.withArg(tx2)
.build().unwrap();
}
fn myFunction2() -> Balance {
let tx1 = Tx::new(&[Address::from(0x123456789)], 100);
vm.startCall(Address::from(0x234567890))
.withArg(tx1)
.build().