Skip to content

Commit 59fdecd

Browse files
mattsseclaude
andauthored
feat: add call-util feature with caller_gas_allowance (#111)
* feat: add call-util feature with caller_gas_allowance Adds a new `call-util` feature that provides utilities for eth_call and adjacent RPC endpoints. The initial implementation includes the `caller_gas_allowance` function ported from reth, which calculates the maximum gas a caller can afford based on their balance and the transaction's gas price. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: resolve clippy warning and From trait conflict * chore: apply rustfmt --------- Co-authored-by: Claude <[email protected]>
1 parent 526bf1d commit 59fdecd

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

crates/evm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ std = [
5353
]
5454
op = ["op-revm", "op-alloy-consensus"]
5555
overrides = ["dep:alloy-rpc-types-eth"]
56+
call-util = ["overrides"]

crates/evm/src/call.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! Utilities for dealing with eth_call and adjacent RPC endpoints.
2+
3+
use alloy_primitives::U256;
4+
use revm::Database;
5+
6+
/// Insufficient funds error
7+
#[derive(Debug, thiserror::Error)]
8+
#[error("insufficient funds: cost {cost} > balance {balance}")]
9+
pub struct InsufficientFundsError {
10+
/// Transaction cost
11+
pub cost: U256,
12+
/// Account balance
13+
pub balance: U256,
14+
}
15+
16+
/// Error type for call utilities
17+
#[derive(Debug, thiserror::Error)]
18+
pub enum CallError<E> {
19+
/// Database error
20+
#[error(transparent)]
21+
Database(E),
22+
/// Insufficient funds error
23+
#[error(transparent)]
24+
InsufficientFunds(#[from] InsufficientFundsError),
25+
}
26+
27+
/// Calculates the caller gas allowance.
28+
///
29+
/// `allowance = (account.balance - tx.value) / tx.gas_price`
30+
///
31+
/// Returns an error if the caller has insufficient funds.
32+
/// Caution: This assumes non-zero `env.gas_price`. Otherwise, zero allowance will be returned.
33+
///
34+
/// Note: this takes the mut [Database] trait because the loaded sender can be reused for the
35+
/// following operation like `eth_call`.
36+
pub fn caller_gas_allowance<DB, T>(db: &mut DB, env: &T) -> Result<u64, CallError<DB::Error>>
37+
where
38+
DB: Database,
39+
T: revm::context_interface::Transaction,
40+
{
41+
// Get the caller account.
42+
let caller = db.basic(env.caller()).map_err(CallError::Database)?;
43+
// Get the caller balance.
44+
let balance = caller.map(|acc| acc.balance).unwrap_or_default();
45+
// Get transaction value.
46+
let value = env.value();
47+
// Subtract transferred value from the caller balance. Return error if the caller has
48+
// insufficient funds.
49+
let balance =
50+
balance.checked_sub(env.value()).ok_or(InsufficientFundsError { cost: value, balance })?;
51+
52+
Ok(balance
53+
// Calculate the amount of gas the caller can afford with the specified gas price.
54+
.checked_div(U256::from(env.gas_price()))
55+
// This will be 0 if gas price is 0. It is fine, because we check it before.
56+
.unwrap_or_default()
57+
.saturating_to())
58+
}

crates/evm/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub mod error;
2020
pub use error::*;
2121
pub mod tx;
2222
pub use tx::*;
23+
#[cfg(feature = "call-util")]
24+
pub mod call;
2325
#[cfg(feature = "overrides")]
2426
pub mod overrides;
2527
pub mod precompiles;

0 commit comments

Comments
 (0)