Skip to content

Commit

Permalink
Merge pull request #22 from rainshowerLabs/0.5.0
Browse files Browse the repository at this point in the history
0.5.0 release
  • Loading branch information
makemake-kbo authored Aug 3, 2023
2 parents 2c0e17b + 9619a02 commit 45ca5ac
Show file tree
Hide file tree
Showing 16 changed files with 896 additions and 824 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
/.venv
25 changes: 19 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
[package]
name = "sothis"
version = "0.4.0"
version = "0.5.0"
edition = "2021"
authors = ["makemake <[email protected]>"]
license = "AGPL-3.0-only"
license = "MPL-2.0"
description = "Tool for replaying historical EVM state."
readme = "README.md"
homepage = "https://github.com/makemake-kbo/sothis"
repository = "https://github.com/makemake-kbo/sothis"
homepage = "https://github.com/rainshowerLabs/sothis"
repository = "https://github.com/rainshowerLabs/sothis"
keywords = ["cli", "ethereum", "foundry", "reth", "revm"]
categories = ["command-line-utilities"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = "4.3.0"
ctrlc = "3.4.0"
ethers = {version = "2.0.7", features = ["legacy"]}
regex = "1.9.1"
reqwest = { version = "0.11.18", features = ["blocking", "json"] }
serde = { version = "1.0.163", features = ["derive"] }
serde_json = "1.0.96"
Expand Down
1,034 changes: 373 additions & 661 deletions LICENSE

Large diffs are not rendered by default.

40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Sothis is a tool for replaying historical state on a local ***anvil/hardhat*** t

Join the [Rainshower Labs discord server](https://discord.gg/Cs3h397gkz) to discuss sothis and get help.

## Using sothis as a crate

Sothis can be used as a crate in other rust projects. All tracking modes, as well as the lightweight wrapper around JSON-RPC calls can be used. For more on using sothis as a crate, check out the [wiki.](https://github.com/rainshowerLabs/sothis/wiki)

We are targeting **1.68.2 as the MSRV**(minimum supported rust version).

## Usage

Sothis has optional arguments that are not listed in their respective mode sections that might prove useful. Please study the help section below. You can view it any time by running `sothis --help`.
Expand All @@ -23,7 +29,7 @@ Options:
-r, --replay_rpc <replay_rpc>...
HTTP JSON-RPC of the node we're replaying data to
-m, --mode <mode>...
Choose between live, historic, track, or fast_track [default: historic]
Choose between live, historic, track, fast_track, or call_track [default: historic]
-b, --terminal_block <terminal_block>...
Block we're replaying until
--exit_on_tx_fail [<exit_on_tx_fail>...]
Expand All @@ -38,11 +44,17 @@ Options:
Exit the program if a transaction fails
--no_setup [<no_setup>...]
Start replaying immediately.
--decimal [<decimal>...]
Start replaying immediately.
-c, --contract_address <contract_address>...
Address of the contract we're tracking storage.
-l, --storage_slot <storage_slot>...
Storage slot for the variable we're tracking
-0, --origin_block <origin_block>...
-a, --calldata <calldata>...
Storage slot for the variable we're tracking
-o, --origin_block <origin_block>...
First block sothis will look at.
-q, --query_interval <query_interval>...
First block sothis will look at.
-p, --path <path>...
Path to file we're writing to [default: .]
Expand All @@ -52,9 +64,10 @@ Options:
Print help
-V, --version
Print version
```

Sothis currently has 4 modes. Live, historic, track, and fast track.
Sothis currently has 5 modes. Live, historic, track, fast track, and call track.

### Historic

Expand Down Expand Up @@ -139,6 +152,27 @@ Once you are done tracking the slot, terminate the process via a `SIGTERM` or a
`sothis --mode track --source_rpc http://localhost:8545 --contract_address 0x910cbd523d972eb0a6f4cae4618ad62622b39dbf --storage_slot 3 --filename siuuu.json --path ~/Desktop
`


### Call track

The fast call mode is used to track the change for a *historic* eth_call. It cannot be used to get a live view of it. The source_rpc must be an archive node for this mode to perform optimally. This can be used to get historic chainlink oracle prices, see the output of decentralzied exchange swaps over time, and more.

#### Usage

- `--mode call_track`: Used to denote we are using the tracking mode.
- `--source_rpc`: RPC of the node we are getting data from.
- `--contract_address`: Address of the contract we'll be calling.
- `--calldata`: Calldata we're using.
- `--origin_block`: The block from which we start tracking.
- `--terminal_block`(optional): Final block sothis will track. If not specified, sothis will track until terminated.
- `--filename`(optional): Name of our output file. The default filename is formatted as: `address-{}-slot-{}-timestamp-{}.json`.
- `--path`(optional): Path to our output file. The default path is the current directory.

Once you are done tracking the slot, terminate the process via a `SIGTERM` or a `SIGINT` (ctrl-c), which will terminate execution and write the file. Keep in mind that sothis will check once per new block if you tried to terminate it. If no new block are produced on the source_rpc, sothis will not terminate and nothing will be written if you force close it. The example below demonstrates tracking of the historic chainlink oracle price for ETH/USD on mainnet.

`sothis --mode call_track --source_rpc http://localhost:8545 --contract_address 0x1c479675ad559DC151F6Ec7ed3FbF8ceE79582B6 --origin_block 17799350 --calldata 0x06f13056
`

## Installation

Sothis is a rust crate. You can install it with cargo:
Expand Down
37 changes: 37 additions & 0 deletions convert_to_dec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import sys
import re

def is_valid_eth_address(hex_str):
return len(hex_str) == 42 and hex_str[:2] == "0x" and all(c in "0123456789abcdefABCDEF" for c in hex_str[2:])

def hex_to_decimal(match):
hex_str = match.group(0)
if is_valid_eth_address(hex_str):
return hex_str # Ignore valid Ethereum addresses
decimal_num = str(int(hex_str, 16))
return decimal_num

def convert_hex_to_decimal_in_file(file_path):
try:
with open(file_path, 'r') as file:
content = file.read()

# Use regular expression to find all hexadecimal numbers in the content
pattern = r'0x[0-9A-Fa-f]+'
converted_content = re.sub(pattern, hex_to_decimal, content)

with open(file_path, 'w') as file:
file.write(converted_content)

print(f"Conversion successful. Hex numbers in '{file_path}' (excluding Ethereum addresses) converted to decimal.")
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")
except Exception as e:
print(f"Error occurred: {e}")

if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python script.py <file_path>")
else:
file_path = sys.argv[1]
convert_hex_to_decimal_in_file(file_path)
111 changes: 111 additions & 0 deletions src/cli_arg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use clap::{Command, Arg};


// This is not the recommended way to set clap args but it works and its too late to change it now
pub fn create_match() -> clap::Command {
let matches = Command::new("sothis")
.version("0.5.0")
.author("makemake <[email protected]>")
.about("Tool for replaying historical transactions. Designed to be used with anvil or hardhat.")
.arg(Arg::new("source_rpc")
.long("source_rpc")
.short('s')
.num_args(1..)
.required(true)
.help("HTTP JSON-RPC of the node we're querying data from"))
.arg(Arg::new("replay_rpc")
.long("replay_rpc")
.short('r')
.num_args(1..)
.help("HTTP JSON-RPC of the node we're replaying data to"))
.arg(Arg::new("mode")
.long("mode")
.short('m')
.num_args(1..)
.default_value("historic")
.help("Choose between live, historic, track, fast_track, or call_track"))
.arg(Arg::new("terminal_block")
.long("terminal_block")
.short('b')
.num_args(1..)
.required_if_eq("mode", "historic")
.help("Block we're replaying until"))
.arg(Arg::new("exit_on_tx_fail")
.long("exit_on_tx_fail")
.num_args(0..)
.help("Exit the program if a transaction fails"))
.arg(Arg::new("block_listen_time")
.long("block_listen_time")
.short('t')
.num_args(1..)
.default_value("500")
.help("Time in ms to check for new blocks."))
.arg(Arg::new("entropy_threshold")
.long("entropy_threshold")
.num_args(1..)
.default_value("0.07")
.help("Set the percentage of failed transactions to trigger a warning"))
.arg(Arg::new("replay_delay")
.long("replay_delay")
.short('d')
.num_args(1..)
.default_value("0")
.help("Default delay for block replay in ms"))
.arg(Arg::new("send_as_unsigned")
.long("send_as_unsigned")
.num_args(0..)
.help("Exit the program if a transaction fails"))
.arg(Arg::new("no_setup")
.long("no_setup")
.num_args(0..)
.help("Start replaying immediately."))
.arg(Arg::new("decimal")
.long("decimal")
.num_args(0..)
.help("Start replaying immediately."))
.arg(Arg::new("contract_address")
.long("contract_address")
.short('c')
.num_args(1..)
.required_if_eq("mode", "track")
.required_if_eq("mode", "fast_track")
.help("Address of the contract we're tracking storage."))
.arg(Arg::new("storage_slot")
.long("storage_slot")
.short('l')
.num_args(1..)
.required_if_eq("mode", "track")
.required_if_eq("mode", "fast_track")
.help("Storage slot for the variable we're tracking"))
.arg(Arg::new("calldata")
.long("calldata")
.short('a')
.num_args(1..)
.required_if_eq("mode", "call_track")
.help("Storage slot for the variable we're tracking"))
.arg(Arg::new("origin_block")
.long("origin_block")
.short('o')
.num_args(1..)
.required_if_eq("mode", "fast_track")
.help("First block sothis will look at."))
.arg(Arg::new("query_interval")
.long("query_interval")
.short('q')
.num_args(1..)
.help("First block sothis will look at."))
.arg(Arg::new("path")
.long("path")
.short('p')
.num_args(1..)
.default_value(".")
.help("Path to file we're writing to"))
.arg(Arg::new("filename")
.long("filename")
.short('f')
.num_args(1..)
.default_value("")
.help("Name of the file."));

return matches;
}
Loading

0 comments on commit 45ca5ac

Please sign in to comment.