Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix erc20 example
Browse files Browse the repository at this point in the history
rakita committed Jan 18, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent d7fcbe0 commit f6fed06
Showing 2 changed files with 53 additions and 76 deletions.
24 changes: 17 additions & 7 deletions examples/erc20_gas/src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use alloy_sol_types::SolValue;
use revm::{
context::Cfg,
context_interface::{
@@ -12,12 +11,12 @@ use revm::{
Host,
},
precompile::PrecompileErrors,
primitives::{keccak256, U256},
primitives::U256,
specification::hardfork::SpecId,
};
use std::cmp::Ordering;

use crate::{token_operation, TOKEN, TREASURY};
use crate::{erc_address_storage, token_operation, TOKEN, TREASURY};

pub struct Erc20MainetHandler<CTX: CfgGetter + Host, ERROR: From<PrecompileErrors>> {
frame_context: FrameContext<
@@ -37,6 +36,14 @@ impl<CTX: CfgGetter + Host, ERROR: From<PrecompileErrors>> Erc20MainetHandler<CT
}
}

impl<CTX: CfgGetter + Host, ERROR: From<PrecompileErrors>> Default
for Erc20MainetHandler<CTX, ERROR>
{
fn default() -> Self {
Self::new()
}
}

impl<CTX, ERROR> EthHandler for Erc20MainetHandler<CTX, ERROR>
where
CTX: EthContext,
@@ -61,11 +68,9 @@ where
}

fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> {
let caller_u256: U256 = context.tx().caller().into_word().into();
println!("Validate TX: {:?}", caller_u256);
let caller = context.tx().caller();
let caller_nonce = context.journal().load_account(caller)?.data.info.nonce;
let token_account = context.journal().load_account(TOKEN)?.data.clone();
let _ = context.journal().load_account(TOKEN)?.data.clone();

if !context.cfg().is_nonce_check_disabled() {
let tx_nonce = context.tx().nonce();
@@ -100,7 +105,7 @@ where
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
}

let account_balance_slot = keccak256((caller, U256::from(3)).abi_encode()).into();
let account_balance_slot = erc_address_storage(caller);
let account_balance = context
.journal()
.sload(TOKEN, account_balance_slot)
@@ -119,6 +124,10 @@ where
}

fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> {
// load and touch token account
let _ = context.journal().load_account(TOKEN)?.data;
context.journal().touch_account(TOKEN);

let basefee = context.block().basefee() as u128;
let blob_price = context.block().blob_gasprice().unwrap_or_default();
let effective_gas_price = context.tx().effective_gas_price(basefee);
@@ -131,6 +140,7 @@ where
}

let caller = context.tx().caller();
println!("Deduct caller: {:?} for amount: {gas_cost:?}", caller);
token_operation::<CTX, ERROR>(context, caller, TREASURY, U256::from(gas_cost))?;

Ok(())
105 changes: 36 additions & 69 deletions examples/erc20_gas/src/main.rs
Original file line number Diff line number Diff line change
@@ -5,22 +5,23 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]

use alloy_provider::{network::Ethereum, ProviderBuilder, RootProvider};
use alloy_sol_types::{sol, SolCall, SolValue};
use alloy_sol_types::SolValue;
use alloy_transport_http::Http;
use anyhow::{anyhow, Result};
use anyhow::Result;
use database::{AlloyDB, BlockId, CacheDB};
use exec::{transact_erc20evm, transact_erc20evm_commit};
use exec::transact_erc20evm_commit;
use reqwest::{Client, Url};
use revm::{
context_interface::{
result::{ExecutionResult, InvalidHeader, InvalidTransaction, Output},
result::{InvalidHeader, InvalidTransaction},
Journal, JournalDBError, JournalGetter,
},
database_interface::WrapDatabaseAsync,
precompile::PrecompileErrors,
primitives::{address, keccak256, Address, Bytes, TxKind, U256},
state::{AccountInfo, EvmStorageSlot},
Context,
specification::hardfork::SpecId,
state::AccountInfo,
Context, Database,
};

pub mod exec;
@@ -48,15 +49,13 @@ async fn main() -> Result<()> {
// Random empty account: To
let account_to = address!("21a4B6F62E51e59274b6Be1705c7c68781B87C77");

let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48");

// USDC has 6 decimals
let hundred_tokens = U256::from(100_000_000_000_000_000u128);

let balance_slot = keccak256((account, U256::from(3)).abi_encode()).into();
let balance_slot = erc_address_storage(account);
println!("Balance slot: {balance_slot}");
cache_db
.insert_account_storage(usdc, balance_slot, hundred_tokens * U256::from(2))
.insert_account_storage(TOKEN, balance_slot, hundred_tokens * U256::from(2))
.unwrap();
cache_db.insert_account_info(
account,
@@ -68,15 +67,14 @@ async fn main() -> Result<()> {
},
);

let balance_before = balance_of(usdc, account, &mut cache_db).unwrap();
let balance_before = balance_of(account, &mut cache_db).unwrap();
println!("Balance before: {balance_before}");

// Transfer 100 tokens from account to account_to
// Magic happens here with custom handlers
transfer(account, account_to, hundred_tokens, &mut cache_db)?;

let balance_after = balance_of(usdc, account, &mut cache_db)?;

let balance_after = balance_of(account, &mut cache_db)?;
println!("Balance after: {balance_after}");

Ok(())
@@ -96,14 +94,8 @@ where
+ From<JournalDBError<CTX>>
+ From<PrecompileErrors>,
{
let token_account = context.journal().load_account(TOKEN)?.data;

let sender_balance_slot: U256 = keccak256((sender, U256::from(3)).abi_encode()).into();
let sender_balance = token_account
.storage
.get(&sender_balance_slot)
.map(|slot| slot.present_value())
.unwrap_or_default();
let sender_balance_slot = erc_address_storage(sender);
let sender_balance = context.journal().sload(TOKEN, sender_balance_slot)?.data;

if sender_balance < amount {
return Err(ERROR::from(
@@ -112,72 +104,47 @@ where
}
// Subtract the amount from the sender's balance
let sender_new_balance = sender_balance.saturating_sub(amount);
token_account.storage.insert(
sender_balance_slot,
EvmStorageSlot::new_changed(sender_balance, sender_new_balance),
);
context
.journal()
.sstore(TOKEN, sender_balance_slot, sender_new_balance)?;

// Add the amount to the recipient's balance
let recipient_balance_slot: U256 = keccak256((recipient, U256::from(3)).abi_encode()).into();
let recipient_balance = token_account
.storage
.get(&recipient_balance_slot)
.map(|slot| slot.present_value())
.unwrap_or_default();
let recipient_balance_slot = erc_address_storage(recipient);
let recipient_balance = context.journal().sload(TOKEN, recipient_balance_slot)?.data;

let recipient_new_balance = recipient_balance.saturating_add(amount);
token_account.storage.insert(
recipient_balance_slot,
EvmStorageSlot::new_changed(recipient_balance, recipient_new_balance),
);
context
.journal()
.sstore(TOKEN, recipient_balance_slot, recipient_new_balance)?;

Ok(())
}

fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result<U256> {
sol! {
function balanceOf(address account) public returns (uint256);
}

let encoded = balanceOfCall { account: address }.abi_encode();

let mut ctx = Context::builder()
.with_db(alloy_db)
.modify_tx_chained(|tx| {
// 0x1 because calling USDC proxy from zero address fails
tx.caller = TREASURY;
tx.kind = TxKind::Call(token);
tx.data = encoded.into();
tx.value = U256::from(0);
});

let ref_tx = transact_erc20evm(&mut ctx).unwrap();
let value = match ref_tx.result {
ExecutionResult::Success {
output: Output::Call(value),
..
} => value,
result => return Err(anyhow!("'balanceOf' execution failed: {result:?}")),
};

let balance = <U256>::abi_decode(&value, false)?;

Ok(balance)
fn balance_of(address: Address, alloy_db: &mut AlloyCacheDB) -> Result<U256> {
let slot = erc_address_storage(address);
alloy_db.storage(TOKEN, slot).map_err(From::from)
}

fn transfer(from: Address, to: Address, amount: U256, cache_db: &mut AlloyCacheDB) -> Result<()> {
let mut ctx = Context::builder()
.with_db(cache_db)
.modify_cfg_chained(|cfg| {
cfg.spec = SpecId::CANCUN;
})
.modify_tx_chained(|tx| {
tx.caller = from;
tx.kind = TxKind::Call(to);
tx.value = amount;
tx.gas_price = 2;
})
.modify_block_chained(|b| {
b.basefee = 1;
});
let ref_tx = transact_erc20evm_commit(&mut ctx).unwrap();
let success: bool = match ref_tx {
ExecutionResult::Success { .. } => true, //<bool>::abi_decode(&value, false)?,
result => return Err(anyhow!("'transfer' execution failed: {result:?}")),
};
transact_erc20evm_commit(&mut ctx).unwrap();

Ok(())
}

pub fn erc_address_storage(address: Address) -> U256 {
keccak256((address, U256::from(4)).abi_encode()).into()
}

0 comments on commit f6fed06

Please sign in to comment.