Skip to content

Commit 0833ebf

Browse files
committed
create precompile for native mint
1 parent 2b14eeb commit 0833ebf

5 files changed

Lines changed: 131 additions & 4 deletions

File tree

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
resolver = "2"
33
members = [
44
"bin/ev-reth",
5-
"crates/common",
6-
"crates/node",
7-
"crates/evolve",
8-
"crates/tests",
5+
"crates/common",
6+
"crates/evolve",
7+
"crates/node",
8+
"crates/tests",
9+
"crates/ev-precompiles",
910
]
1011

1112
[workspace.package]

crates/ev-precompiles/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
[package]
3+
name = "ev-precompiles"
4+
version = "0.1.0"
5+
edition = "2021"
6+
license = "MIT"
7+
8+
[dependencies]
9+
# Reth
10+
reth-primitives = { workspace = true }
11+
12+
# Revm
13+
revm = { workspace = true, features = ["ethersdb", "optimism"] }
14+
15+
# EVM
16+
alloy-primitives = { workspace = true }
17+
bytes = "1.5.0"
18+
19+
# Tooling
20+
eyre = "0.6.11"
21+
thiserror = "1.0.58"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
use crate::mint_precompile::NATIVE_MINT_PRECOMPILE_ADDRESS;
3+
use alloy_primitives::{Address, U256};
4+
use revm::db::Database;
5+
use revm::precompile::Precompile;
6+
use revm::primitives::{ExecutionResult, Output, TransactTo};
7+
use revm::{Evm, Inspector};
8+
use std::collections::HashSet;
9+
10+
/// An inspector that gates calls to the native mint precompile.
11+
///
12+
/// This inspector enforces an allowlist and various limits on the minting of the native token.
13+
/// It also performs the state mutation (crediting the balance) when the precompile call is
14+
/// successful.
15+
pub struct MintInspector {
16+
/// The address of the native mint precompile.
17+
precompile: Address,
18+
/// A static list of addresses that are allowed to call the precompile.
19+
allow: HashSet<Address>,
20+
/// An optional on-chain registry for validating callers.
21+
registry: Option<Address>,
22+
/// The maximum amount that can be minted in a single call.
23+
per_call_cap: U256,
24+
/// The maximum amount that can be minted in a single block.
25+
per_block_cap: Option<U256>,
26+
/// The total amount minted in the current block.
27+
minted_this_block: U256,
28+
}
29+
30+
impl<DB: Database> Inspector<DB> for MintInspector {
31+
/// Called before the execution of a transaction.
32+
fn transact_inspect(
33+
&mut self,
34+
context: &mut revm::InnerEvmContext<DB>,
35+
transact_to: TransactTo,
36+
caller: Address,
37+
value: U256,
38+
) -> Option<Vec<u8>> {
39+
// Reset the per-block mint counter at the beginning of each transaction.
40+
self.minted_this_block = U256::ZERO;
41+
None
42+
}
43+
44+
/// Called after the execution of a transaction.
45+
fn transact_end(&mut self, context: &mut revm::InnerEvmContext<DB>, result: ExecutionResult) {
46+
// No-op
47+
}
48+
}

crates/ev-precompiles/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub mod builder_ext;
2+
pub mod config;
3+
pub mod inspector_gate;
4+
pub mod mint_precompile;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
use alloy_primitives::{Address, U256};
3+
use bytes::Bytes;
4+
use revm::precompile::{Precompile, PrecompileError, PrecompileOutput};
5+
use std::str;
6+
7+
/// The address of the native mint precompile.
8+
pub const NATIVE_MINT_PRECOMPILE_ADDRESS: Address = Address::new([
9+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10+
0x00, 0x00, 0x00, 0xEF,
11+
]);
12+
13+
/// The ABI for the native mint precompile.
14+
///
15+
/// The precompile accepts a 52-byte buffer, structured as follows:
16+
/// - The first 20 bytes represent the recipient's address (`to`).
17+
/// - The next 32 bytes represent the amount to mint (`amount`).
18+
///
19+
/// ```solidity
20+
/// interface INativeMint {
21+
/// function mint(address to, uint256 amount) external;
22+
/// }
23+
/// ```
24+
pub const NATIVE_MINT_PRECOMPILE_ABI: &str = r#"[{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"}]"#;
25+
26+
/// The gas cost for the native mint precompile.
27+
pub const NATIVE_MINT_PRECOMPILE_GAS_COST: u64 = 5_000;
28+
29+
/// A stateless precompile for minting the native token.
30+
///
31+
/// This precompile is responsible for parsing the input, validating the gas, and returning a
32+
/// success or error. The actual state mutation (crediting the balance) is handled by the
33+
/// `MintInspector`.
34+
pub fn native_mint(input: &Bytes, gas_limit: u64) -> Result<PrecompileOutput, PrecompileError> {
35+
// Check if the gas limit is sufficient.
36+
if gas_limit < NATIVE_MINT_PRECOMPILE_GAS_COST {
37+
return Err(PrecompileError::OutOfGas);
38+
}
39+
40+
// The input should be exactly 52 bytes long: 20 bytes for the address and 32 bytes for the
41+
// amount.
42+
if input.len() != 52 {
43+
return Err(PrecompileError::Other(
44+
"invalid input length".to_string().into(),
45+
));
46+
}
47+
48+
// Return success with the gas used. The actual minting is handled by the inspector.
49+
Ok(PrecompileOutput::new(
50+
NATIVE_MINT_PRECOMPILE_GAS_COST,
51+
Bytes::new(),
52+
))
53+
}

0 commit comments

Comments
 (0)