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::{
19 PrecompileError, PrecompileHalt, PrecompileId, PrecompileOutput, PrecompileResult,
20};
21
22pub const CELO_TRANSFER_LABEL: &str = "CELO_TRANSFER_PRECOMPILE";
24
25pub const CELO_TRANSFER_ADDRESS: Address = address!("0x00000000000000000000000000000000000000fd");
27
28pub static PRECOMPILE_ID_CELO_TRANSFER: PrecompileId =
30 PrecompileId::Custom(Cow::Borrowed("celo transfer"));
31
32const CELO_TRANSFER_GAS_COST: u64 = 9000;
34
35pub fn precompile() -> DynPrecompile {
37 DynPrecompile::new_stateful(PRECOMPILE_ID_CELO_TRANSFER.clone(), celo_transfer_precompile)
38}
39
40pub fn celo_transfer_precompile(mut input: PrecompileInput<'_>) -> PrecompileResult {
44 if input.gas < CELO_TRANSFER_GAS_COST {
46 return Ok(PrecompileOutput::halt(PrecompileHalt::OutOfGas, input.reservoir));
47 }
48
49 if input.data.len() != 96 {
51 return Ok(PrecompileOutput::halt(
52 PrecompileHalt::Other(
53 format!(
54 "Invalid input length for Celo transfer precompile: expected 96 bytes, got {}",
55 input.data.len()
56 )
57 .into(),
58 ),
59 input.reservoir,
60 ));
61 }
62
63 let from_bytes = &input.data[12..32];
65 let to_bytes = &input.data[44..64];
66 let value_bytes = &input.data[64..96];
67
68 let from_address = Address::from_slice(from_bytes);
69 let to_address = Address::from_slice(to_bytes);
70 let value = U256::from_be_slice(value_bytes);
71
72 let internals = input.internals_mut();
74
75 let from_account = match internals.load_account(from_address) {
78 Ok(account) => account,
79 Err(e) => {
80 return Ok(PrecompileOutput::halt(
81 PrecompileHalt::Other(format!("Failed to load sender account: {e:?}").into()),
82 input.reservoir,
83 ));
84 }
85 };
86
87 if from_account.data.info.balance < value {
89 return Ok(PrecompileOutput::halt(
90 PrecompileHalt::Other("Insufficient balance".into()),
91 input.reservoir,
92 ));
93 }
94
95 let to_account = match internals.load_account(to_address) {
96 Ok(account) => account,
97 Err(e) => {
98 return Ok(PrecompileOutput::halt(
99 PrecompileHalt::Other(format!("Failed to load recipient account: {e:?}").into()),
100 input.reservoir,
101 ));
102 }
103 };
104
105 if to_account.data.info.balance.checked_add(value).is_none() {
107 return Ok(PrecompileOutput::halt(
108 PrecompileHalt::Other("Balance overflow in to account".into()),
109 input.reservoir,
110 ));
111 }
112
113 internals
115 .transfer(from_address, to_address, value)
116 .map_err(|e| PrecompileError::Fatal(format!("Failed to perform transfer: {e:?}")))?;
117
118 Ok(PrecompileOutput::new(
120 CELO_TRANSFER_GAS_COST,
121 alloy_primitives::Bytes::new(),
122 input.reservoir,
123 ))
124}