# Anti-Venom A solution toward the social engineering elements of cryptocurrency address-impersonation attacks. ```sh #!/usr/bin/env nu # Enhanced EVM transaction fetcher with translation support and address book def main [--translate(-t), --config(-c), --labels(-l)] { # Configuration check mode if $config { print "Checking environment configuration..." let evm_set = not ($env.EVM_ADDR? | is-empty) let noves_set = not ($env.NOVES_API_KEY? | is-empty) let dune_set = not ($env.SIM_API_KEY? | is-empty) print $"EVM_ADDR: (if $evm_set { '✓ Set' } else { '✗ Not set' })" print $"NOVES_API_KEY: (if $noves_set { '✓ Set' } else { '✗ Not set' })" print $"SIM_API_KEY: (if $dune_set { '✓ Set' } else { '✗ Not set' })" let required_set = $evm_set or $dune_set print $"\nRequired configuration status: (if $required_set { '✓ Valid (EVM_ADDR or SIM_API_KEY is set)' } else { '✗ Invalid (Either EVM_ADDR or SIM_API_KEY must be set)' })" # Check for address book if ("address_book.csv" | path exists) { try { let address_book = (open address_book.csv) let headers = ($address_book | columns) if ($headers | any { |h| $h == "address" }) and ($headers | any { |h| $h == "chain" }) and ($headers | any { |h| $h == "label" }) { print $"Address book: ✓ Found with (($address_book | length)) entries" } else { print "Address book: ✗ Invalid format (missing required headers: address, chain, label)" } } catch { print "Address book: ✗ Error reading file" } } else { print "Address book: - Not found (optional)" } return } # Check required environment variables if ($env.EVM_ADDR? | is-empty) and ($env.SIM_API_KEY? | is-empty) { error make {msg: "Either EVM_ADDR or SIM_API_KEY environment variable must be set. Use --config to check your configuration."} } # Check SIM_API_KEY when needed if ($env.SIM_API_KEY? | is-empty) { error make {msg: "SIM_API_KEY environment variable is not set. This is required for fetching transaction data."} } # Get EVM address let evm_addr = if ($env.EVM_ADDR? | is-empty) { print "EVM_ADDR environment variable is not set." let user_addr = (input "Please enter an EVM address: ") if ($user_addr | str trim | is-empty) { error make {msg: "EVM address cannot be empty"} } $env.EVM_ADDR = $user_addr $user_addr } else { $env.EVM_ADDR } print $"Using EVM address: ($evm_addr)" # Load address book if available let address_book = if ("address_book.csv" | path exists) { try { let book = (open address_book.csv) let headers = ($book | columns | each { |h| $h | str trim }) let has_address = ($headers | any { |h| $h == "address" }) let has_chain = ($headers | any { |h| $h == "chain" }) let has_label = ($headers | any { |h| $h == "label" }) if $has_address and $has_chain and $has_label { let clean_book = ($book | rename ...(($book | columns) | each { |col| $col | str trim })) print $"Loaded address book with (($clean_book | length)) entries" $clean_book } else { let missing = (["address", "chain", "label"] | where { |required| not ($headers | any { |h| $h == $required }) }) print $"Warning: address_book.csv missing required headers: (($missing | str join ', ')). Found headers: (($headers | str join ', ')). Ignoring address book." [] } } catch { print "Warning: Could not read address_book.csv. Ignoring address book." [] } } else { [] } # Function to apply address labels (chain-agnostic by default) def apply-address-label [address: string, chain: string, address_book: list, use_labels: bool] { if not $use_labels or ($address_book | is-empty) { return $address } let normalized_address = ($address | str downcase) let normalized_book = ($address_book | update address { |row| $row.address | str downcase }) let address_matches = ($normalized_book | where address == $normalized_address) if ($address_matches | is-empty) { $address } else if ($address_matches | length) == 1 { # Only one match, use it regardless of chain let match = ($address_matches | first) $match.label } else { # Multiple matches, try to find chain-specific match let chain_matches = ($address_matches | where chain == $chain) if not ($chain_matches | is-empty) { let match = ($chain_matches | first) $match.label } else { # No exact chain match, use first available label let match = ($address_matches | first) $match.label } } } # Defining custom command to format addresses def format-evm-address [address_string: string, current_evm_addr: string, chain: string, address_book: list, use_labels: bool] { let display_text = if $use_labels { apply-address-label $address_string $chain $address_book $use_labels } else { $address_string } if $address_string == $current_evm_addr { $"\e[1;32m($display_text)\e[0m" # Green for current address (whether label or address) } else { if $use_labels and $display_text != $address_string { $display_text } else { let addr_no_prefix = ($address_string | str replace --all "0x" "") let bytes = ($addr_no_prefix | split chars) let chunks = ($bytes | chunks 8) let colorized = (0..(($chunks | length) - 1) | each { |i| let color_idx = ($i mod 2) + 33 # Start from 33 (yellow) for alternating colors $"\e[1;($color_idx)m(($chunks | get $i) | str join)\e[0m" }) $"\e[1;34m0x\e[0m(($colorized | str join))" # Blue for "0x" prefix } } } # Function to get transaction translation (simplified) def get-translation [hash: string] { # Check if NOVES_API_KEY is set when translation is requested if ($env.NOVES_API_KEY? | is-empty) { error make {msg: "NOVES_API_KEY environment variable is not set. This is required for transaction translations."} } try { let response = (http get --headers [accept "application/json" apiKey $env.NOVES_API_KEY] $"https://translate.noves.fi/evm/eth/describeTx/($hash)") if ($response | get -i description) != null { $response.description } else { "No translation available" } } catch { "translation unavailable" } } # Fetch and process transactions let transactions = ( http get --headers [X-Sim-Api-Key $env.SIM_API_KEY] $"https://api.sim.dune.com/v1/evm/transactions/($evm_addr)" | get transactions | first 25 | update gas_used { |it| $it.gas_used | into int --radix 10 } | update gas_price { |it| $it.gas_price | into int --radix 10 } | update effective_gas_price { |it| $it.effective_gas_price | into int --radix 10 } | update value { |it| $it.value | into int --radix 10 } | update nonce { |it| $it.nonce | into int --radix 10 } | update block_time { |it| $it.block_time | format date "%Y-%m-%d %H:%M" } | select block_time chain transaction_type hash from to value success | update from { |row| format-evm-address $row.from $evm_addr $row.chain $address_book $labels } | update to { |row| format-evm-address $row.to $evm_addr $row.chain $address_book $labels } ) # Add optional translation column if requested if $translate { print "Fetching transaction translations..." $transactions | insert translation { |row| get-translation $row.hash } } else { $transactions } } ```