foundry_evm_networks/celo/
transfer.rs1use std::borrow::Cow;
15
16use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
17use alloy_primitives::{Address, U256, address};
18use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
19
20pub const CELO_TRANSFER_LABEL: &str = "CELO_TRANSFER_PRECOMPILE";
22
23pub const CELO_TRANSFER_ADDRESS: Address = address!("0x00000000000000000000000000000000000000fd");
25
26pub static PRECOMPILE_ID_CELO_TRANSFER: PrecompileId =
28 PrecompileId::Custom(Cow::Borrowed("celo transfer"));
29
30const CELO_TRANSFER_GAS_COST: u64 = 9000;
32
33pub fn precompile() -> DynPrecompile {
35 DynPrecompile::new_stateful(PRECOMPILE_ID_CELO_TRANSFER.clone(), celo_transfer_precompile)
36}
37
38pub fn celo_transfer_precompile(mut input: PrecompileInput<'_>) -> PrecompileResult {
42 if input.gas < CELO_TRANSFER_GAS_COST {
44 return Err(PrecompileError::OutOfGas);
45 }
46
47 if input.data.len() != 96 {
49 return Err(PrecompileError::Other(
50 format!(
51 "Invalid input length for Celo transfer precompile: expected 96 bytes, got {}",
52 input.data.len()
53 )
54 .into(),
55 ));
56 }
57
58 let from_bytes = &input.data[12..32];
60 let to_bytes = &input.data[44..64];
61 let value_bytes = &input.data[64..96];
62
63 let from_address = Address::from_slice(from_bytes);
64 let to_address = Address::from_slice(to_bytes);
65 let value = U256::from_be_slice(value_bytes);
66
67 let internals = input.internals_mut();
69
70 let from_account = match internals.load_account(from_address) {
73 Ok(account) => account,
74 Err(e) => {
75 return Err(PrecompileError::Other(
76 format!("Failed to load from account: {e:?}").into(),
77 ));
78 }
79 };
80
81 if from_account.data.info.balance < value {
83 return Err(PrecompileError::Other("Insufficient balance".into()));
84 }
85
86 let to_account = match internals.load_account(to_address) {
87 Ok(account) => account,
88 Err(e) => {
89 return Err(PrecompileError::Other(format!("Failed to load to account: {e:?}").into()));
90 }
91 };
92
93 if to_account.data.info.balance.checked_add(value).is_none() {
95 return Err(PrecompileError::Other("Balance overflow in to account".into()));
96 }
97
98 internals
100 .transfer(from_address, to_address, value)
101 .map_err(|e| PrecompileError::Other(format!("Failed to perform transfer: {e:?}").into()))?;
102
103 Ok(PrecompileOutput::new(CELO_TRANSFER_GAS_COST, alloy_primitives::Bytes::new()))
105}