# 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
}
}
```