Skip to content

Instantly share code, notes, and snippets.

@mds1
Last active July 18, 2024 19:20
Show Gist options
  • Save mds1/3f070676129a095dec372c2d02cedfdd to your computer and use it in GitHub Desktop.
Save mds1/3f070676129a095dec372c2d02cedfdd to your computer and use it in GitHub Desktop.
# Ethereum helper methods
# 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 completions zsh > $FOUNDRY_PLUGIN_DIR/_anvil
cast completions zsh > $FOUNDRY_PLUGIN_DIR/_cast
forge 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/simple/price\?ids\=ethereum\&vs_currencies\=usd | jq '.ethereum.usd')
else
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=$(price eth)
price_gas=$(cast base-fee)
price_gas_gwei=$(cast --to-fix 9 "$price_gas")
# Calculating costs in USD
cost_transfer_usd=$(echo "scale=10;$price_eth*$price_gas*21000/$wad" | bc) # ETH transfer cost in USD
cost_swap_usd=$(echo "scale=10;$price_eth*$price_gas*130000/$wad" | bc) # Uniswap V3 swap cost in USD
cost_deploy_usd=$(echo "scale=10;$price_eth*$price_gas*1000000/$wad" | bc) # Contract deployment cost in USD
# Calculating costs in ETH
cost_transfer_eth=$(echo "scale=10;$price_gas*21000/$wad" | bc) # ETH transfer cost in ETH
cost_swap_eth=$(echo "scale=10;$price_gas*130000/$wad" | bc) # Uniswap V3 swap cost in ETH
cost_deploy_eth=$(echo "scale=10;$price_gas*1000000/$wad" | bc) # Contract deployment cost in ETH
printf 'ETH price \t%1.3f USD\n' "$price_eth"
printf 'Basefee \t%1.3f gwei\n' "$price_gas_gwei"
echo ""
printf 'Transfer \t21k gas \t $%1.3f \t%1.5f ETH\n' "$cost_transfer_usd" "$cost_transfer_eth"
printf 'Swap \t130k gas \t $%1.3f \t%1.5f ETH\n' "$cost_swap_usd" "$cost_swap_eth"
printf 'Deploy \t1M gas \t $%1.3f \t%1.5f ETH\n' "$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 "${2:-mainnet}" --etherscan-api-key "${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 # <toContractName>.sol is often leftover, so delete that manually afterwards
}
# Returns the slot used to store the value of a Solidity mapping for a specified address
# example: `getSolidityAddressSlot <address> <mappingSlot>`
getSolidityAddressSlot() {
echo $(seth keccak $(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 $(seth keccak $(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=$(cast call "$safe" "nonce()(uint256)" "$@")
threshold=$(cast call "$safe" "getThreshold()(uint256)" "$@")
owners=$(cast call "$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+=("$line")
done <<< "$owners"
echo "Current Nonce: $nonce"
echo "Threshold: $threshold"
echo "Number of Owners: ${#ownersArray[@]}"
# Lookup each owner address
for address in "${ownersArray[@]}"; do
address="${address#"${address%%[![:space:]]*}"}" # remove leading whitespace
ownerName=$(cast lookup-address "$address" -r "$MAINNET_RPC_URL" 2>/dev/null)
if [[ -n $ownerName ]]; then
echo " $address $ownerName"
else
echo " $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=$(pwd)
cd /Users/mds/Documents/projects/superchain-registry || exit
rg --ignore-case "$address"
rgExitCode=$?
cd "$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 "$Contract" ]]; then
curl --silent "$REGISTRY_URL/$L1/$L2.json" | jq -r
else
local fuzzyMatch=$(curl --silent "$REGISTRY_URL/$L1/$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
}
@pyk
Copy link

pyk commented Sep 18, 2021

@mds1 btw is there any example on how to use getSolidityAddressSlot() ser?

I tried to use it like this:

$ getSolidityAddressSlot 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 1
0x49e349d4f386739afdf8e55db7675b584d46c3b2a603eef62554e21dc437b5db

But I don't know how to use it for slot loc in hevm.store. https://github.com/dapphub/dapptools/tree/master/src/hevm#cheat-codes

Thank you ser

@pyk
Copy link

pyk commented Sep 18, 2021

Eh nvm ser, I found https://github.com/kendricktan/slot20 that I can use to find the slot number ser

@maurelian
Copy link

Updates to use TOML in the registry mean that code should look more like this now:

  #for op chain addresses
  local REGISTRY_URL=https://raw.githubusercontent.com/ethereum-optimism/superchain-
  local URL="$REGISTRY_URL/$L1/$L2.toml"

  # for superchain wide addresses
  local REGISTRY_URL=https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/main/superchain/configs
  local URL="$REGISTRY_URL/$L1/superchain.toml"

  curl --silent $URL | yq --input-format=toml '.addresses'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment