use alloy_primitives::{hex, B256};
use alloy_sol_types::{ContractError, GenericRevertReason};
use cfx_types::{Address, H160, H256, U256};
use cfx_vm_interpreter::instructions::{INSTRUCTIONS, INSTRUCTIONS_CANCUN};
use revm::primitives::{Address as RAddress, U256 as RU256};
#[inline]
pub(crate) fn convert_memory(data: &[u8]) -> Vec<String> {
let mut memory = Vec::with_capacity((data.len() + 31) / 32);
for idx in (0..data.len()).step_by(32) {
let len = std::cmp::min(idx + 32, data.len());
memory.push(hex::encode(&data[idx..len]));
}
memory
}
#[inline]
pub(crate) fn gas_used(spent: u64, refunded: u64) -> u64 {
let refund_quotient = 5;
spent - (refunded).min(spent / refund_quotient)
}
#[inline]
pub(crate) fn maybe_revert_reason(output: &[u8]) -> Option<String> {
let reason = match GenericRevertReason::decode(output)? {
GenericRevertReason::ContractError(err) => {
match err {
ContractError::Revert(revert) => revert.reason,
err => err.to_string(),
}
}
GenericRevertReason::RawString(err) => err,
};
if reason.is_empty() {
None
} else {
Some(reason)
}
}
pub(crate) fn stack_push_count(step_op: u8, cancun_enabled: bool) -> usize {
match cancun_enabled {
true => match INSTRUCTIONS_CANCUN.get(step_op as usize) {
Some(Some(instruct)) => instruct.ret,
_ => 0,
},
false => match INSTRUCTIONS.get(step_op as usize) {
Some(Some(instruct)) => instruct.ret,
_ => 0,
},
}
}
pub fn to_alloy_u256(u: U256) -> RU256 {
let mut be_bytes: [u8; 32] = [0; 32];
u.to_big_endian(&mut be_bytes);
RU256::from_be_bytes(be_bytes)
}
pub fn to_alloy_address(h: H160) -> RAddress {
RAddress::from_slice(h.as_bytes())
}
pub fn to_alloy_h256(h: H256) -> B256 { B256::from(h.0) }
pub fn from_alloy_address(address: RAddress) -> Address {
Address::from_slice(address.as_slice())
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_sol_types::{GenericContractError, SolInterface};
#[test]
fn decode_revert_reason() {
let err = GenericContractError::Revert("my revert".into());
let encoded = err.abi_encode();
let reason = maybe_revert_reason(&encoded).unwrap();
assert_eq!(reason, "my revert");
}
#[test]
fn decode_revert_reason_with_error() {
let err = hex!("08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e5400000000000000000000000000000000000000000000000000000080");
let reason = maybe_revert_reason(&err[..]).unwrap();
assert_eq!(reason, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
}
}