Ethereum helper methods
May 11, 2024 · View on GitHub
source this in your .bashrc or .zshrc file with . ~/.ethrc
--- Solidity sandbox ---
https://github.com/maurelian/solidity-sandbox
scratch() { dir=(pwd) cd ~/Documents/projects/solidity-sandbox || exit bash newTest.sh \1 cd "$dir" || exit }
--- Forge helers ---
alias f="forge"
Aliases to local foundry binaries .
alias forgel="/prj/foundry-rs/foundry/target/debug/forge"
alias castl="/prj/foundry-rs/foundry/target/debug/cast"
alias anvill="/prj/foundry-rs/foundry/target/debug/anvil"
alias chisell="/prj/foundry-rs/foundry/target/debug/chisel"
alias scopelintl="/prj/scopelint/target/debug/scopelint"
alias bulloakl="/prj/bulloak/target/debug/bulloak"
Update foundry and install shell completions:
- https://book.getfoundry.sh/config/shell-autocompletion
- https://twitter.com/0xyyy_/status/1562091599731228672
We use a function then alias gm to it so we can override the
alias gm="git merge" that exists in the ohmyzsh git plugin.
FOUNDRY_PLUGIN_DIR={ZSH_CUSTOM:-{ZSH:-~/.oh-my-zsh}/custom}/plugins/foundry fpath+=FOUNDRY_PLUGIN_DIR updateFoundry() { foundryup "@" mkdir -p FOUNDRY_PLUGIN_DIR/_anvil cast completions zsh > FOUNDRY_PLUGIN_DIR/_forge } alias gm="updateFoundry"
--- Token addresses ---
aave=0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9 comp=0xc00e94Cb662C3520282E6f5717214004A7f26888 crv=0xD533a949740bb3306d119CC777fa900bA034cd52 dai=0x6B175474E89094C44Da98b954EedeAC495271d0F gtc=0xDe30da39c46104798bB5aA3fe8B9e0e1F348163F mkr=0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2 snx=0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F tbtc=0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa uni=0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 usdc=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 usdn=0x674C6Ad92Fd080e4004b2312b45f796a192D27a0 usdt=0xdAC17F958D2ee523a2206206994597C13D831ec7 wbtc=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 weth=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 yfi=0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e zero=0x0000000000000000000000000000000000000000
--- Contracts ---
umbra=0xFb2dc580Eed955B528407b4d36FfaFe3da685401 stealthKeyRegistry=0x31fe56609C65Cd0C510E7125f051D440424D38f3 multicall=0xcA11bde05977b3631167028862bE2a173976CA11
--- Prices / gas ---
Returns the price in USD of the specified token
https://www.coingecko.com/en/api/documentation
example: price eth
example: price $gtc
example: price <token>
price() { if [[ $1 = 'eth' ]]; then echo (curl -s https://api.coingecko.com/api/v3/coins/ethereum/contract/\$1 | jq '.market_data.current_price.usd') fi }
prints the basefee in gwei, and the cost in USD of sending transactions based on current basefee and no priority fee
gas() { wad=1000000000000000000 # 1e18 price_eth=(cast base-fee) price_gas_gwei=price_gas")
Calculating costs in USD
cost_transfer_usd=price_eth*wad" | bc) # ETH transfer cost in USD cost_swap_usd=price_eth*wad" | bc) # Uniswap V3 swap cost in USD cost_deploy_usd=price_eth*wad" | bc) # Contract deployment cost in USD
Calculating costs in ETH
cost_transfer_eth=price_gas21000/wad" | bc) # ETH transfer cost in ETH cost_swap_eth=(echo "scale=10;wad" | bc) # Uniswap V3 swap cost in ETH cost_deploy_eth=price_gas1000000/$wad" | bc) # Contract deployment cost in ETH
printf 'ETH price \t%1.3f USD\n' "price_gas_gwei" echo "" printf 'Transfer \t21k gas \t cost_transfer_usd" "%1.3f \t%1.5f ETH\n' "cost_swap_eth" printf 'Deploy \t1M gas \t cost_deploy_usd" "$cost_deploy_eth" }
--- ERC-20 Calls ---
example: name <token>
example: name $dai
name() { cast call "$1" "name()(string)" }
example: symbol <token>
symbol() { cast call "$1" "symbol()(string)" }
example: decimals <token>
decimals() { cast call "$1" "decimals()(uint8)" }
example: balanceOf <token> <address>
balanceOf() { cast call "$1" "balanceOf(address)(uint256)" "$2" }
example: allowance <token> <holder> <spender>
allowance() { cast call "$1" "allowance(address,address)(uint256)" "$2" "$3" }
example: totalSupply <token>
totalSupply() { cast call "$1" "totalSupply()(uint256)" }
interface() { if [[ $1 == 0x* ]]; then cast interface "$1" -c "{3:-ETHERSCAN_API_KEY}" else cast interface <(forge inspect "\1" abi) fi }
--- Seth/Cast helpers ---
checksums the address
checksum() { cast --to-checksum-address "$1" }
checksums the address and copies it to the clipboard (after deleting any new lines characters)
checksumc() { cast --to-checksum-address "$1" | tr -d '\n' | pbcopy }
returns the basefee in gwei
alias basefee='cast --from-wei $(cast basefee) gwei'
example: tracetx <txhash> (trace is a builtin command)
tracetx() { seth run-tx $1 --trace }
example: debug <txhash>
debug() {
seth bundle-source (seth tx \1 to) > tmp.json && \ # often might need to bundle other sources
seth run-tx $1 --source=tmp.json --debug &&
rm tmp.json #
Returns the slot used to store the value of a Solidity mapping for a specified address
example: getSolidityAddressSlot <address> <mappingSlot>
getSolidityAddressSlot() { echo (abiEncode "x(address,uint256)" $1 $2)) }
Returns the slot used to store the value of a Vyper mapping for a specified address
note: not guaranteed to work for all Vyper versions since storage layout is not yet stable. More info: https://twitter.com/big_tech_sux/status/1420159854170152963
example: getVyperAddressSlot <address> <mappingSlot>
getVyperAddressSlot() { echo (abiEncode "x(uint256,address)" $2 $1)) }
Usage getSafeDetails <safe-address> [cast call options]
All cast call options are passed to cast call commands. The one
exception is when we do ENS lookups, in which case we ignore cast flags
and always use the latest block with a mainnet RPC URL. This is done by
assuming a MAINNET_RPC_URL env var is set.
getSafeDetails() { local safe=$1 shift # Shift the first argument off so "$@" contains any additional arguments
local threshold owners nonce
Ensure compatibility with both bash and zsh for arrays.
In zsh, arrays start at 1 by default and it handles word splitting differently.
Setting KSH_ARRAYS makes array indexing start at 0 in zsh, similar to bash,
and we disable it at the end of the function.
if [[ -n "$ZSH_VERSION" ]]; then setopt localoptions ksharrays fi
nonce=safe" "nonce()(uint256)" "(cast call "@") owners=safe" "getOwners()(address[])" "$@" | tr -d '[]' | tr ',' '\n')
Convert string to array.
This works in both bash and zsh without needing to change IFS.
local ownersArray=() while IFS= read -r line; do ownersArray+=("owners"
echo "Current Nonce: threshold" echo "Number of Owners: ${#ownersArray[@]}"
Lookup each owner address
for address in "{address#"{address%%[![:space:]]*}"}" # remove leading whitespace ownerName=(cast lookup-address "MAINNET_RPC_URL" 2>/dev/null) if [[ -n address address" fi done
If previously set KSH_ARRAYS for zsh, unset it to revert back to normal zsh behavior
if [[ -n "$ZSH_VERSION" ]]; then unsetopt ksharrays fi }
Usage: getOpStackAddresses <L1> <L2> [Contract] OR getOpStackAddresses <address>
If one argument is provided, it's an address, and we search the local superchain registry for it.
NOTE: This mode has hardcoded superchain-registry path you must edit, as well as a dependency on
ripgrep (https://github.com/ajeetdsouza/zoxide, https://github.com/BurntSushi/ripgrep)
getOpStackAddresses 0x1234...
If no contract is provided, it will return all contracts:
getOpStackAddresses mainnet op
If a contract is provided and there is an exact match, it will return the address only
getOpStackAddresses mainnet op SystemConfigProxy
If a contract is provided and there are multiple matches, it will return all matches (i.e. fuzzy match)
getOpStackAddresses mainnet op sys
getOpStackAddresses() { if [[ # -eq 1 ]]; then local address="\1" pwd=address" rgExitCode=pwd" || exit return rgExitCode else local REGISTRY_URL=https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/main/superchain/extra/addresses local L1="\1" local L2="$2" local Contract="$3" if [[ -z "REGISTRY_URL/L2.json" | jq -r else local fuzzyMatch=REGISTRY_URL/L2.json" | jq -r "to_entries[] | select(.key | test("$Contract"; "i")) | "(.key): (.value)"")
if [[ -n "$fuzzyMatch" ]]; then
local matchCount=$(echo "$fuzzyMatch" | wc -l)
if [[ $matchCount -eq 1 ]]; then
echo "$fuzzyMatch" | cut -d ':' -f 2 | tr -d ' '
else
echo "$fuzzyMatch"
fi
else
echo "No matching contract found for '$Contract'" >&2
return 1
fi
fi
fi }