-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ✨ feat: add snapshot information to `command_palette` TUI view * ✨ feat: add help TUI view back
- Loading branch information
1 parent
4322bfb
commit 990f84d
Showing
4 changed files
with
284 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,22 +11,18 @@ lazy_static! { | |
pub static ref ABOUT_TEXT: Vec<String> = vec![ | ||
format!("heimdall-rs v{}", env!("CARGO_PKG_VERSION")), | ||
"By Jonathan Becker <[email protected]>".to_string(), | ||
"The storage dump module will fetch all storage slots and values accessed by any EVM contract.".to_string(), | ||
"The snapshot module allows users to quickly generate an overview of a contract's bytecode, without the need for the contract's source code.".to_string(), | ||
]; | ||
|
||
pub static ref HELP_MENU_COMMANDS: Vec<String> = vec![ | ||
":q, :quit exit the program".to_string(), | ||
":h, :help display this help menu".to_string(), | ||
":f, :find <VALUE> search for a storage slot by slot or value".to_string(), | ||
":e, :export <FILENAME> export the current storage dump to a file, preserving decoded values".to_string(), | ||
":s, :seek <DIRECTION> <AMOUNT> move the cusor up or down by a specified amount".to_string(), | ||
]; | ||
|
||
pub static ref HELP_MENU_CONTROLS: Vec<String> = vec![ | ||
"↑, Scroll Up move the cursor up one slot".to_string(), | ||
"↓, Scroll Down move the cursor down one slot".to_string(), | ||
"←, → change the decoding type of the selected slot".to_string(), | ||
"CTRL + ↑, CTRL + ↓ move the cursor up or down by 10 slots".to_string(), | ||
"↑, Scroll Up move the cursor up".to_string(), | ||
"↓, Scroll Down move the cursor down".to_string(), | ||
"←, → switch scrolling context between selector list and snapshot information".to_string(), | ||
"ESC clear the search filter".to_string(), | ||
]; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,292 @@ | ||
use heimdall_common::utils::strings::encode_hex_reduced; | ||
use tui::{ | ||
backend::Backend, | ||
layout::{Constraint, Direction, Layout}, | ||
style::{Color, Style}, | ||
widgets::{Block, Borders, Paragraph}, | ||
layout::{Alignment, Constraint, Direction, Layout}, | ||
style::{Color, Modifier, Style}, | ||
text::{Span, Spans}, | ||
widgets::{Block, Borders, Paragraph, Table}, | ||
Frame, | ||
}; | ||
|
||
use crate::snapshot::structures::state::State; | ||
use crate::snapshot::{structures::state::State, util::table::build_rows}; | ||
|
||
pub fn render_tui_command_palette<B: Backend>(f: &mut Frame<B>, state: &mut State) { | ||
// creates a new block with the given title | ||
// https://github.com/fdehau/tui-rs/blob/master/examples/paragraph.rs | ||
let create_block = |title, borders| { | ||
Block::default() | ||
.borders(borders) | ||
.style(Style::default().fg(Color::White)) | ||
.title(Span::styled(title, Style::default().add_modifier(Modifier::BOLD))) | ||
}; | ||
|
||
// build main layout | ||
let main_layout = Layout::default() | ||
.direction(Direction::Vertical) | ||
.margin(1) | ||
.constraints([Constraint::Length(3), Constraint::Percentage(100)].as_ref()) | ||
.split(f.size()); | ||
|
||
let sub_layout = Layout::default() | ||
.direction(Direction::Horizontal) | ||
.constraints([Constraint::Length(14), Constraint::Percentage(100)].as_ref()) | ||
.split(main_layout[1]); | ||
|
||
let detail_layout = Layout::default() | ||
.direction(Direction::Vertical) | ||
.constraints([Constraint::Percentage(100)].as_ref()) | ||
.split(sub_layout[1]); | ||
|
||
// add command paragraph input | ||
let input_buffer = state.input_buffer.clone(); | ||
let command_input = Paragraph::new(input_buffer) | ||
.style(Style::default().fg(Color::White)) | ||
.block(Block::default().title(" Command ").borders(Borders::ALL)); | ||
|
||
// build rows | ||
let rows = build_rows(state, main_layout[1].height as usize - 4); | ||
|
||
// build table | ||
let table = Table::new(rows) | ||
.block( | ||
Block::default() | ||
.title(" Selectors ") | ||
.style(Style::default().fg(Color::White).add_modifier(Modifier::BOLD)) | ||
.borders(Borders::ALL), | ||
) | ||
.widths(&[Constraint::Length(12), Constraint::Length(14), Constraint::Percentage(100)]); | ||
|
||
// build function info | ||
let snapshot = state.snapshots.get(state.function_index).unwrap(); | ||
|
||
// build modifiers | ||
let modifiers = vec![ | ||
if snapshot.payable { "payable" } else { "" }, | ||
if snapshot.pure { "pure" } else { "" }, | ||
if snapshot.view && !snapshot.pure { "view" } else { "" }, | ||
] | ||
.iter() | ||
.filter(|x| !x.is_empty()) | ||
.map(|x| x.to_string()) | ||
.collect::<Vec<_>>(); | ||
|
||
// build argument list | ||
let mut arg_strings: Vec<String> = Vec::new(); | ||
match &snapshot.resolved_function { | ||
Some(function) => { | ||
for (index, input) in function.inputs.iter().enumerate() { | ||
arg_strings.push(format!("arg{} {}", index, input)); | ||
} | ||
} | ||
None => { | ||
let mut sorted_arguments: Vec<_> = snapshot.arguments.clone().into_iter().collect(); | ||
sorted_arguments.sort_by(|x, y| x.0.cmp(&y.0)); | ||
for (index, (_, solidity_type)) in sorted_arguments { | ||
arg_strings.push(format!("arg{} {}", index, solidity_type.first().unwrap())); | ||
} | ||
} | ||
}; | ||
|
||
// add function resolved name | ||
let mut text = vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Function ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
Spans::from(match &snapshot.resolved_function { | ||
Some(function) => format!(" {}({})", function.name, arg_strings.join(", ")), | ||
None => format!(" Unresolved_{}()", snapshot.selector), | ||
}), | ||
]; | ||
|
||
// build function snapshot | ||
text.append(&mut vec![ | ||
// add modifiers and arguments | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Modifiers Returns Entry Point Branch Count", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
Spans::from(format!( | ||
" {:<16}{:<15}{:<17}{}", | ||
modifiers.join(" "), | ||
snapshot.returns.clone().unwrap_or("None".to_owned()), | ||
snapshot.entry_point, | ||
snapshot.branch_count | ||
)), | ||
]); | ||
|
||
// add gas consumptions | ||
text.append(&mut vec![ | ||
// add modifiers and arguments | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Minimum Gas Consumed Maximum Gas Consumed Average Gas Consumed", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
Spans::from(format!( | ||
" {:<24}{:<25}{}", | ||
snapshot.gas_used.min, snapshot.gas_used.max, snapshot.gas_used.avg | ||
)), | ||
]); | ||
|
||
// add events | ||
if !snapshot.events.is_empty() { | ||
text.append(&mut vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Events ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
]); | ||
text.append( | ||
&mut snapshot | ||
.events | ||
.iter() | ||
.map(|x| { | ||
let key = encode_hex_reduced(*x.0).replacen("0x", "", 1); | ||
match state.resolved_events.get(&key) { | ||
Some(event) => { | ||
Spans::from(format!(" {}({})", event.name, event.inputs.join(","))) | ||
} | ||
None => Spans::from(format!(" Event_{}()", key[0..8].to_owned())), | ||
} | ||
}) | ||
.collect::<Vec<_>>(), | ||
); | ||
} | ||
|
||
// add errors | ||
if !snapshot.errors.is_empty() { | ||
text.append(&mut vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Errors ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
]); | ||
text.append( | ||
&mut snapshot | ||
.errors | ||
.iter() | ||
.map(|x| { | ||
let key = encode_hex_reduced(*x.0).replacen("0x", "", 1); | ||
match state.resolved_errors.get(&key) { | ||
Some(error) => { | ||
Spans::from(format!(" {}({})", error.name, error.inputs.join(","))) | ||
} | ||
None => Spans::from(format!(" Error_{}()", key[0..8].to_owned())), | ||
} | ||
}) | ||
.collect::<Vec<_>>(), | ||
); | ||
} | ||
|
||
// add external calls | ||
if !snapshot.external_calls.is_empty() { | ||
text.append(&mut vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" External Calls ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
]); | ||
text.append( | ||
&mut snapshot | ||
.external_calls | ||
.iter() | ||
.map(|x| Spans::from(format!(" {}", x))) | ||
.collect::<Vec<_>>(), | ||
); | ||
} | ||
|
||
// add strings | ||
if !snapshot.strings.is_empty() { | ||
text.append(&mut vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Strings ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
]); | ||
text.append( | ||
&mut snapshot | ||
.strings | ||
.iter() | ||
.map(|x| Spans::from(format!(" {}", x))) | ||
.collect::<Vec<_>>(), | ||
); | ||
} | ||
|
||
// add addresses | ||
if !snapshot.addresses.is_empty() { | ||
text.append(&mut vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Hardcoded Addresses ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
]); | ||
text.append( | ||
&mut snapshot | ||
.addresses | ||
.iter() | ||
.map(|x| Spans::from(format!(" {}", x))) | ||
.collect::<Vec<_>>(), | ||
); | ||
} | ||
|
||
// add storage | ||
if !snapshot.storage.is_empty() { | ||
text.append(&mut vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Storage ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
]); | ||
text.append( | ||
&mut snapshot | ||
.storage | ||
.iter() | ||
.map(|x| Spans::from(format!(" {}", x))) | ||
.collect::<Vec<_>>(), | ||
); | ||
} | ||
|
||
// add control statements | ||
if !snapshot.control_statements.is_empty() { | ||
text.append(&mut vec![ | ||
Spans::from(""), // buffer | ||
Spans::from(Span::styled( | ||
" Control Statements ", | ||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD), | ||
)), | ||
]); | ||
text.append( | ||
&mut snapshot | ||
.control_statements | ||
.iter() | ||
.map(|x| Spans::from(format!(" {}", x))) | ||
.collect::<Vec<_>>(), | ||
); | ||
} | ||
|
||
// about text | ||
let snapshot_header = format!( | ||
" {}Snapshot of 0x{}{} ", | ||
if state.scroll { "> " } else { "" }, | ||
snapshot.selector, | ||
if state.scroll { " <" } else { "" }, | ||
); | ||
let function_snapshot = Paragraph::new(text) | ||
.style(Style::default().fg(Color::White)) | ||
.block(create_block(snapshot_header, Borders::ALL)) | ||
.alignment(Alignment::Left) | ||
.scroll((state.scroll_index as u16, 0)); | ||
|
||
f.render_widget(command_input, main_layout[0]); | ||
f.render_widget(table, sub_layout[0]); | ||
f.render_widget(function_snapshot, detail_layout[0]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters