1use crate::config::TraceStyle;
3use alloy_primitives::{hex, B256};
4use alloy_sol_types::{ContractError, GenericRevertReason};
5use cfx_types::{Address, H160, H256, U256};
6use cfx_vm_interpreter::instructions::{
7 INSTRUCTIONS, INSTRUCTIONS_CANCUN, INSTRUCTIONS_CIP645,
8};
9use revm::interpreter::InstructionResult;
10use revm_primitives::{Address as RAddress, U256 as RU256};
11
12pub(crate) fn fmt_error_msg(
18 res: InstructionResult, kind: TraceStyle,
19) -> Option<String> {
20 if res.is_ok() {
21 return None;
22 }
23 let msg = match res {
24 InstructionResult::Revert => if kind.is_parity() {
25 "Reverted"
26 } else {
27 "execution reverted"
28 }
29 .to_string(),
30 InstructionResult::OutOfGas | InstructionResult::PrecompileOOG => {
31 if kind.is_parity() {
32 "Out of gas"
33 } else {
34 "out of gas"
35 }
36 .to_string()
37 }
38 InstructionResult::OutOfFunds => if kind.is_parity() {
39 "Insufficient balance for transfer"
40 } else {
41 "insufficient balance for transfer"
42 }
43 .to_string(),
44 InstructionResult::MemoryOOG => if kind.is_parity() {
45 "Out of gas"
46 } else {
47 "out of gas: out of memory"
48 }
49 .to_string(),
50 InstructionResult::MemoryLimitOOG => if kind.is_parity() {
51 "Out of gas"
52 } else {
53 "out of gas: reach memory limit"
54 }
55 .to_string(),
56 InstructionResult::InvalidOperandOOG => if kind.is_parity() {
57 "Out of gas"
58 } else {
59 "out of gas: invalid operand"
60 }
61 .to_string(),
62 InstructionResult::OpcodeNotFound => if kind.is_parity() {
63 "Bad instruction"
64 } else {
65 "invalid opcode"
66 }
67 .to_string(),
68 InstructionResult::StackOverflow => "Out of stack".to_string(),
69 InstructionResult::InvalidJump => if kind.is_parity() {
70 "Bad jump destination"
71 } else {
72 "invalid jump destination"
73 }
74 .to_string(),
75 InstructionResult::PrecompileError => if kind.is_parity() {
76 "Built-in failed"
77 } else {
78 "precompiled failed"
79 }
80 .to_string(),
81 InstructionResult::InvalidFEOpcode => if kind.is_parity() {
82 "Bad instruction"
83 } else {
84 "invalid opcode: INVALID"
85 }
86 .to_string(),
87 InstructionResult::ReentrancySentryOOG => if kind.is_parity() {
88 "Out of gas"
89 } else {
90 "out of gas: not enough gas for reentrancy sentry"
91 }
92 .to_string(),
93 status => format!("{status:?}"),
94 };
95
96 Some(msg)
97}
98
99#[inline]
102pub(crate) fn convert_memory(data: &[u8]) -> Vec<String> {
103 let mut memory = Vec::with_capacity((data.len() + 31) / 32);
104 for idx in (0..data.len()).step_by(32) {
105 let len = std::cmp::min(idx + 32, data.len());
106 memory.push(hex::encode(&data[idx..len]));
107 }
108 memory
109}
110
111#[inline]
113pub(crate) fn gas_used(spent: u64, refunded: u64) -> u64 {
114 let refund_quotient = 5;
115 spent - (refunded).min(spent / refund_quotient)
116}
117
118#[inline]
120pub(crate) fn maybe_revert_reason(output: &[u8]) -> Option<String> {
121 let reason = match GenericRevertReason::decode(output)? {
122 GenericRevertReason::ContractError(err) => {
123 match err {
124 ContractError::Revert(revert) => revert.reason,
127 err => err.to_string(),
128 }
129 }
130 GenericRevertReason::RawString(err) => err,
131 };
132 if reason.is_empty() {
133 None
134 } else {
135 Some(reason)
136 }
137}
138
139pub(crate) fn stack_push_count(
146 step_op: u8, cancun_enabled: bool, cip645: bool,
147) -> usize {
148 let instuction_set = if !cancun_enabled {
149 &*INSTRUCTIONS
150 } else if cip645 {
151 &*INSTRUCTIONS_CANCUN
152 } else {
153 &*INSTRUCTIONS_CIP645
154 };
155 match instuction_set.get(step_op as usize) {
156 Some(Some(instruct)) => instruct.ret,
157 _ => 0,
158 }
159}
160
161pub fn to_alloy_u256(u: U256) -> RU256 {
163 let mut be_bytes: [u8; 32] = [0; 32];
164 u.to_big_endian(&mut be_bytes);
165 RU256::from_be_bytes(be_bytes)
166}
167
168pub fn to_alloy_address(h: H160) -> RAddress {
169 RAddress::from_slice(h.as_bytes())
170}
171
172pub fn to_alloy_h256(h: H256) -> B256 { B256::from(h.0) }
173
174pub fn from_alloy_address(address: RAddress) -> Address {
175 Address::from_slice(address.as_slice())
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use alloy_sol_types::{GenericContractError, SolInterface};
182
183 #[test]
184 fn decode_revert_reason() {
185 let err = GenericContractError::Revert("my revert".into());
186 let encoded = err.abi_encode();
187 let reason = maybe_revert_reason(&encoded).unwrap();
188 assert_eq!(reason, "my revert");
189 }
190
191 #[test]
193 fn decode_revert_reason_with_error() {
194 let err = hex!("08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e5400000000000000000000000000000000000000000000000000000080");
195 let reason = maybe_revert_reason(&err[..]).unwrap();
196 assert_eq!(reason, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
197 }
198}