use crate::{DynSolType, Selector, StateMutability, StorageRecord};
use crate::{
arguments::function_arguments,
control_flow_graph::basic_blocks,
control_flow_graph::{ControlFlowGraph, control_flow_graph},
evm::code_iterator::disassemble,
selectors::function_selectors,
state_mutability::function_state_mutability,
storage::contract_storage,
};
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Function {
#[cfg_attr(
feature = "serde",
serde(serialize_with = "crate::serialize::selector")
)]
pub selector: Selector,
#[cfg_attr(feature = "serde", serde(rename = "bytecodeOffset"))]
pub bytecode_offset: usize,
#[cfg_attr(
feature = "serde",
serde(serialize_with = "crate::serialize::arguments")
)]
pub arguments: Option<Vec<DynSolType>>,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "crate::serialize::state_mutability",
rename = "stateMutability"
)
)]
pub state_mutability: Option<StateMutability>,
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Contract {
pub functions: Option<Vec<Function>>,
pub storage: Option<Vec<StorageRecord>>,
pub disassembled: Option<Vec<(usize, String)>>,
#[cfg_attr(feature = "serde", serde(rename = "basicBlocks"))]
pub basic_blocks: Option<Vec<(usize, usize)>>,
#[cfg_attr(feature = "serde", serde(rename = "controlFlowGraph"))]
pub control_flow_graph: Option<ControlFlowGraph>,
}
#[derive(Default)]
pub struct ContractInfoArgs<'a> {
code: &'a [u8],
need_selectors: bool,
need_arguments: bool,
need_state_mutability: bool,
need_storage: bool,
need_disassemble: bool,
need_basic_blocks: bool,
need_control_flow_graph: bool,
}
impl<'a> ContractInfoArgs<'a> {
pub fn new(code: &'a [u8]) -> Self {
ContractInfoArgs {
code,
..Default::default()
}
}
pub fn with_selectors(mut self) -> Self {
self.need_selectors = true;
self
}
pub fn with_arguments(mut self) -> Self {
self.need_selectors = true;
self.need_arguments = true;
self
}
pub fn with_state_mutability(mut self) -> Self {
self.need_selectors = true;
self.need_state_mutability = true;
self
}
pub fn with_storage(mut self) -> Self {
self.need_selectors = true;
self.need_arguments = true;
self.need_storage = true;
self
}
pub fn with_disassemble(mut self) -> Self {
self.need_disassemble = true;
self
}
pub fn with_basic_blocks(mut self) -> Self {
self.need_basic_blocks = true;
self
}
pub fn with_control_flow_graph(mut self) -> Self {
self.need_basic_blocks = true;
self.need_control_flow_graph = true;
self
}
}
pub fn contract_info(args: ContractInfoArgs) -> Contract {
const GAS_LIMIT: u32 = 0;
let (basic_blocks, control_flow_graph): (Option<Vec<_>>, _) = if args.need_basic_blocks {
let bb = basic_blocks(args.code);
let blocks = Some(bb.values().map(|bl| (bl.start, bl.end)).collect());
let cfg = if args.need_control_flow_graph {
Some(control_flow_graph(args.code, bb))
} else {
None
};
(blocks, cfg)
} else {
(None, None)
};
let functions = if args.need_selectors {
let (selectors, _selectors_gas_used) = function_selectors(args.code, GAS_LIMIT);
Some(
selectors
.into_iter()
.map(|(selector, bytecode_offset)| Function {
selector,
arguments: if args.need_arguments {
Some(function_arguments(args.code, &selector, GAS_LIMIT))
} else {
None
},
state_mutability: if args.need_state_mutability {
Some(function_state_mutability(args.code, &selector, GAS_LIMIT))
} else {
None
},
bytecode_offset,
})
.collect::<Vec<_>>(),
)
} else {
None
};
let storage = if args.need_storage {
let fns = functions
.as_ref()
.expect("enabled on with_storage()")
.iter()
.map(|f| (f.selector, f.bytecode_offset, f.arguments.as_ref().unwrap()));
Some(contract_storage(args.code, fns, GAS_LIMIT))
} else {
None
};
let disassembled = if args.need_disassemble {
Some(disassemble(args.code))
} else {
None
};
Contract {
functions,
storage,
disassembled,
basic_blocks,
control_flow_graph,
}
}