├── __tests__ ├── test.justfile ├── fix-chmod.sh └── justfile ├── scripts ├── cls.sh ├── git │ ├── fetch-recurse │ ├── spull │ ├── sub-pull │ ├── git-ssh │ ├── submodule-discard-changes.sh │ ├── add-module │ ├── ssh-checkout │ ├── README.md │ ├── submodules.sh │ └── ssh-handler-github.sh ├── solc │ ├── dump-io.sh │ └── solidity-shebang ├── mod │ ├── json-fix.sh │ └── README.md ├── configure.sh ├── hevm.sh ├── remove-color-output.sh ├── build │ ├── ABI.sh │ ├── get-output.sh │ └── _jq ├── env-check.sh ├── dapptest.sh ├── estimate-gas.sh ├── codehash.sh ├── clone-and-checkout.sh ├── deploy.sh ├── sort-tests.sh ├── contract-size.sh ├── helper-config.sh ├── test-deploy.sh ├── run-temp-testnet.sh ├── dapptools │ ├── etherscacn-version.sh │ └── verify.sh ├── coverageSnapshotPhase.sh ├── combine_artifact_json.sh ├── harness │ └── prepare-certora.sh ├── deploy │ ├── verify-after-deploy.sh │ ├── interactive.sh │ └── calculate-init-code-hash.sh ├── utils.sh └── common.sh ├── .vscode └── extensions.json ├── .gitattributes ├── foundry.install ├── .gitignore ├── gas-snapshot └── src │ └── reporting │ ├── coverage-snapshot.sh │ └── README.md ├── forge ├── minify-output.sh └── gen-abi.sh ├── cast └── checksum-address.sh ├── .shellcheckrc ├── package.json ├── src └── init.sh ├── foundry.toml ├── .editorconfig ├── COPYING ├── .github └── workflows │ └── justfile.yml ├── LICENSE.spdx ├── forge.justfile ├── README.md └── justfile /__tests__/test.justfile: -------------------------------------------------------------------------------- 1 | test: 2 | @echo "Test..." -------------------------------------------------------------------------------- /scripts/cls.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | printf "\033[2J\033[3J\033[1;1H" 3 | -------------------------------------------------------------------------------- /scripts/git/fetch-recurse: -------------------------------------------------------------------------------- 1 | git fetch --recurse-submodules -j10 2 | -------------------------------------------------------------------------------- /scripts/solc/dump-io.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | export ETHERS_SOLC_LOG=in=in.json,out=out.json 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "skellock.just" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /__tests__/fix-chmod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | [[ "$0" != "$BASH_SOURCE" ]] || main "$@" 3 | chmod -R 755 scripts/ -------------------------------------------------------------------------------- /scripts/mod/json-fix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # removes top line from output 3 | forge $cmd | tail -n +2 | tee $outputFile 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # -*- mode: gitattributes; -*- 2 | justfile linguist-language=Make 3 | VERSION export-subst 4 | *.sh text eol=lf 5 | -------------------------------------------------------------------------------- /scripts/configure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export LANG='en_US.UTF-8' 4 | export LANGUAGE='en_US:en' 5 | export LC_ALL='en_US.UTF-8' 6 | -------------------------------------------------------------------------------- /scripts/git/spull: -------------------------------------------------------------------------------- 1 | git config --global alias.spull '!git pull && git submodule sync --recursive && git submodule update --init --recursive' 2 | -------------------------------------------------------------------------------- /scripts/mod/README.md: -------------------------------------------------------------------------------- 1 | ### modifying scripts 2 | 3 | use sed to remove color codes 4 | ```bash 5 | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" 6 | ``` 7 | -------------------------------------------------------------------------------- /scripts/hevm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | hevm() { 3 | hevm dapp-test --rpc="${ETH_RPC_URL}" --json-file=out/dapp.sol.json --dapp-root=. --verbose 2 --match "test_gas" 4 | } 5 | -------------------------------------------------------------------------------- /scripts/remove-color-output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # remove colour output in results 3 | forge $cmd | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee $outputFile 4 | -------------------------------------------------------------------------------- /scripts/git/sub-pull: -------------------------------------------------------------------------------- 1 | git config --global alias.spull '__git_spull() { git pull "$@" && git submodule sync --recursive && git submodule update --init --recursive; }; __git_spull' 2 | -------------------------------------------------------------------------------- /scripts/solc/solidity-shebang: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | tmpfile=$(mktemp --suffix=.sol /tmp/forge-script.XXXXXX) 4 | tail --lines=+2 "$@" >$tmpfile 5 | forge run $tmpfile 6 | rm $tmpfile 7 | -------------------------------------------------------------------------------- /foundry.install: -------------------------------------------------------------------------------- 1 | Package name: 2 | Version number: 3 | Original author: 4 | Original URL: 5 | EVM: 6 | Install tree: 7 | Report bugs to: 8 | Tested on: 9 | Solidity compiler used: 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | .DS_Store 3 | node_modules 4 | cache/ 5 | artifacts 6 | coverage/ 7 | __tests__/src 8 | __tests__/out 9 | __tests__/lib 10 | __tests__/cache 11 | __tests__/foundry.toml 12 | -------------------------------------------------------------------------------- /scripts/git/git-ssh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | git remote set-url origin $(git remote show origin | grep "Fetch URL" | sed 's/ *Fetch URL: //' | sed 's/https:\/\/github.com\//git@github.com:/') 4 | -------------------------------------------------------------------------------- /scripts/build/ABI.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | export LC_ALL=C 4 | forge build 5 | ABI=$(cat out/$CONTRACT_NAME.sol/$CONTRACT_NAME.json | jq .abi) 6 | echo $ABI > ../dist/src/abi/$CONTRACT_NAME.json 7 | -------------------------------------------------------------------------------- /scripts/git/submodule-discard-changes.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "[TASK]: Discarding local changes made in all submodules..." 3 | git status 4 | git gc 5 | git restore . --recurse-submodules 6 | git push --recurse-submodules=on-demand 7 | -------------------------------------------------------------------------------- /scripts/build/get-output.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | shopt -s globstar 3 | set -euo pipefail 4 | 5 | # passing out/*.json in CMD gets escaped incorrectly 6 | args=("$@") 7 | if [ "${#args[@]}" -lt 2 ]; then args+=(out/**/*.json); fi 8 | 9 | sleep 1 10 | -------------------------------------------------------------------------------- /scripts/env-check.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # cd to the root of the repo 4 | cd "$(git rev-parse --show-toplevel)" 5 | 6 | # avoids re-compilation during publishing of both packages 7 | if [[ ! -v ALREADY_COMPILED ]]; then 8 | forge build 9 | fi 10 | -------------------------------------------------------------------------------- /gas-snapshot/src/reporting/coverage-snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | patdiff -version || exit 127 3 | forge snapshot --allow-failures >02_gas_report 4 | patdiff .gas-snapshot 02_gas_report -html >gas_report.html 5 | rm 02_gas_report 6 | echo "Gas Report generated" 7 | -------------------------------------------------------------------------------- /scripts/dapptest.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export LANG=C.UTF-8 4 | function dapptest() { 5 | ETH_RPC_URL=${1##*/} 6 | if [ -d "$cloneDirName" ]; then 7 | hevm dapp-test --rpc="${ETH_RPC_URL}" --json-file=out/dapp.sol.json --dapp-root=. --verbose 2 --match "test_gas" 8 | return 9 | fi 10 | exit 127 11 | } 12 | -------------------------------------------------------------------------------- /scripts/build/_jq: -------------------------------------------------------------------------------- 1 | path= 2 | options=() 3 | set -- "$@" -c . 4 | for arg; do 5 | if [ -f "$arg" ]; then 6 | if [ -n "$path" ]; then 7 | echo "Cannot specify multiple paths to jq-write" >&2 8 | exit 1 9 | fi 10 | path="$arg" 11 | else 12 | options+=("$arg") 13 | fi 14 | done 15 | tmp=$(mktemp) 16 | jq "${options[@]}" "$path" >"$tmp" 17 | cat "$tmp" >"$path" 18 | -------------------------------------------------------------------------------- /scripts/estimate-gas.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | . $(dirname $0)/common.sh 6 | 7 | if [[ -z $contract ]]; then 8 | if [[ -z ${1} ]]; then 9 | echo '"$contract" env variable is not set. Set it to the name of the contract you want to estimate gas cost for.' 10 | exit 1 11 | else 12 | contract=${1} 13 | fi 14 | fi 15 | 16 | estimate_gas $contract 17 | -------------------------------------------------------------------------------- /scripts/codehash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | codehash() { 5 | if [[ -z $CONTRACT ]]; then 6 | if [[ -z ${1} ]]; then 7 | echo '"$CONTRACT" env variable is not set. Set it to the name of the contract you want the codehash for.' 8 | exit 127 9 | else 10 | CONTRACT=${1} 11 | cat out/$CONTRACT.sol/$CONTRACT.json | jq -r .bytecode.object | xargs cast keccak 12 | fi 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /forge/minify-output.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | path= 4 | options=() 5 | # change -c to -r to get pretty-print 6 | set -- "$@" -c . 7 | for arg; do 8 | if [ -f "$arg" ]; then 9 | if [ -n "$path" ]; then 10 | echo "Cannot specify multiple paths to jq-minify" >&2 11 | exit 1 12 | fi 13 | path="$arg" 14 | else 15 | options+=("$arg") 16 | fi 17 | done 18 | tmp=$(mktemp) 19 | jq "${options[@]}" "$path" >"$tmp" 20 | cat "$tmp" >"$path" 21 | -------------------------------------------------------------------------------- /cast/checksum-address.sh: -------------------------------------------------------------------------------- 1 | #!/usr/env/bin bash 2 | 3 | # ensure ETH_FROM is set and give a meaningful error message 4 | if [[ -z ${ETH_FROM} ]]; then 5 | echo "ETH_FROM not found, please set it and re-run the last command." 6 | exit 1 7 | fi 8 | 9 | # Make sure address is checksummed 10 | if [ "$ETH_FROM" != "$(seth --to-checksum-address "$ETH_FROM")" ]; then 11 | echo "ETH_FROM not checksummed, please format it with 'seth --to-checksum-address
'" 12 | exit 1 13 | fi 14 | -------------------------------------------------------------------------------- /scripts/clone-and-checkout.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONTRACT_DIR=source/contracts 4 | 5 | set -e 6 | mkdir -p $CONTRACT_DIR && cd $CONTRACT_DIR 7 | 8 | function cloneCheckoutCommit() { 9 | cloneDirName=${1##*/} 10 | if [ -d "$cloneDirName" ]; then 11 | (cd "$cloneDirName" && [ -z "$(git status --porcelain)" ] && git checkout "$2") || (echo Unclean "$cloneDirName" && exit 1) 12 | return 13 | fi 14 | git clone https://github.com/"$1" && (cd "$cloneDirName" && git checkout "$2") 15 | } 16 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # import the deployment helpers 6 | . $(dirname $0)/common.sh 7 | 8 | # import config with arguments based on contract and network 9 | . $(dirname $0)/helper-config.sh 10 | 11 | # Deploy 12 | # Contract will be counter unless overriden on the command line 13 | : ${CONTRACT:=Greeter} 14 | echo "Deploying $CONTRACT to $NETWORK with arguments: $arguments" 15 | Addr=$(deploy $CONTRACT $arguments) 16 | log "$CONTRACT deployed at:" $Addr 17 | -------------------------------------------------------------------------------- /scripts/git/add-module: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "$1" -o $(echo "$1" | tr '/' '\n' | wc -l) != 2 ] && 4 | { 5 | echo "Usage: $(basename "$0") / [destdir] # 'destdir' defaults to 'vendor/repo'" 6 | exit 1 7 | } 8 | REPO="$1" 9 | 10 | DEST="vendor/${REPO#*/}" 11 | [ -n "$2" ] && DEST="$2" 12 | 13 | git submodule add --force https://github.com/"${REPO}".git "$DEST" 14 | git config -f .gitmodules submodule."${DEST}".ignore untracked 15 | git config -f .gitmodules submodule."${DEST}".branch master 16 | -------------------------------------------------------------------------------- /scripts/git/ssh-checkout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | failed_checkout() { 4 | echo "Failed to git clone $1" 5 | exit -1 6 | } 7 | 8 | checkout() { 9 | [ -d "$2" ] || git -c advice.detachedHead=0 clone --branch "$3" --depth 1 "$1" "$2" || failed_checkout "$1" 10 | } 11 | 12 | if ! command -v git 1>/dev/null 2>&1; then 13 | echo "✘ Git is not installed, can't continue." >&2 14 | exit 1 15 | fi 16 | 17 | if [ -n "${USE_GIT_URI}" ]; then 18 | GITHUB="git://github.com" 19 | else 20 | GITHUB="https://github.com" 21 | fi 22 | -------------------------------------------------------------------------------- /scripts/sort-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "Reading src dir and sorting files..." 3 | export LC_ALL=C 4 | tests_to_run=$(find src/ -name "*.sol" ! -path "src" | LC_ALL=C sort -t. -k 1,1 -k 2,2n -k 3,3 -o index.txt) 5 | echo $tests_to_run 6 | if [ "$?" -eq "0" ] 7 | then 8 | echo "...." 9 | echo "Success:" 10 | echo "Output witten to index.txt file" 11 | exit 0 12 | else 13 | echo "...." 14 | echo "Error:" 15 | echo "Exception, unable to write results" 16 | exit 1 17 | fi 18 | 19 | exit 127 20 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | shell=bash 2 | enable=deprecate-which 3 | enable=require-double-brackets 4 | disable=SC1090 # sourcing files; ENV SHELLCHECK_OPTS='--shell=bash --exclude=SC1090' 5 | disable=SC2034 # for localizing variables set in called functions 6 | disable=SC2128 # style choice 7 | 8 | # TODO: check these 9 | #disable=SC2016 10 | #disable=SC2086 11 | #disable=SC2155 12 | #disable=SC2206 # suggested alternatives fail in posix mode or use temp files 13 | #disable=SC2207 # suggested alternatives fail in posix mode or use temp files 14 | -------------------------------------------------------------------------------- /scripts/contract-size.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | . $(dirname $0)/common.sh 6 | 7 | if [[ -z $contract ]]; then 8 | if [[ -z ${1} ]]; then 9 | echo '"$contract" env variable is not set. Set it to the name of the contract you want to estimate size for.' 10 | exit 1 11 | else 12 | contract=${1} 13 | fi 14 | fi 15 | contract_size=$(contract_size ${contract}) 16 | echo "Contract Name: ${contract}" 17 | echo "Contract Size: ${contract_size} bytes" 18 | echo "$((24576 - ${contract_size})) bytes left to reach the smart contract size limit of 24576 bytes." 19 | -------------------------------------------------------------------------------- /forge/gen-abi.sh: -------------------------------------------------------------------------------- 1 | #!usr/bin/env bash 2 | 3 | echo "Generating ABI..." 4 | 5 | # Clean old build artifacts 6 | rm -rf abi 7 | rm -rf out 8 | 9 | # forge build: outputs normal forge .json files and .abi.json files to out/ 10 | FOUNDRY_IGNORED_ERROR_CODES='[5574,5740,1878]' forge build --extra-output-files abi 11 | 12 | # Move .abi.json files to abi/ 13 | # Cange extension from .abi.json to .json so typechain doesn't generate types like 'ContractNameAbi' 14 | mkdir -p abi/ 15 | cp out/**/*.abi.json abi/ 16 | for file in abi/*.abi.json; do 17 | mv -- "$file" "${file%.abi.json}.json" 18 | done 19 | 20 | echo "Generated ABI!" 21 | exit 0 22 | -------------------------------------------------------------------------------- /scripts/git/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### GitHub Actions 3 | 4 | > actions/checkout@v2 fixed git@github.com problem 5 | 6 | ```yaml 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 15 | submodules: true 16 | token: ${{ secrets.PAT }} 17 | ``` 18 | 19 | ```yaml 20 | - name: Fix submodules 21 | run: echo -e '[url "https://github.com/"]\n insteadOf = "git@github.com:"' >> ~/.gitconfig 22 | - name: Checkout 23 | uses: actions/checkout@v1 24 | with: 25 | fetch-depth: 0 26 | submodules: true 27 | token: ${{ secrets.PAT }} 28 | ``` 29 | -------------------------------------------------------------------------------- /scripts/helper-config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Defaults 4 | # Add your defaults here 5 | # For example: 6 | # address=0x01be23585060835e02b77ef475b0cc51aa1e0709 7 | 8 | # Add your contract arguments default here 9 | arguments="" 10 | 11 | if [ "$NETWORK" = "rinkeby" ]; then 12 | : # Add arguments only for rinkeby here! 13 | # like: 14 | # address=0x01be23585060835e02b77ef475b0cc51aa1e0709 15 | elif [ "$NETWORK" = "mainnet" ]; then 16 | : # Add arguments only for mainnet here! 17 | # like: 18 | # address=0x01be23585060835e02b77ef475b0cc51aa1e0709 19 | fi 20 | 21 | if [ "$CONTRACT" = "Greeter" ]; then 22 | : # Add conditional arguments here for contracts 23 | # arguments=$interval 24 | fi 25 | -------------------------------------------------------------------------------- /scripts/test-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # bring up the network 6 | . $(dirname $0)/run-temp-testnet.sh 7 | 8 | # run the deploy script 9 | . $(dirname $0)/deploy.sh 10 | 11 | # get the address 12 | addr=$(jq -r '.Greeter' out/addresses.json) 13 | 14 | # the initial greeting must be empty 15 | greeting=$(seth call $addr 'greeting()(string)') 16 | [[ $greeting = "" ]] || error 17 | 18 | # set it to a value 19 | seth send $addr \ 20 | 'greet(string memory)' '"yo"' \ 21 | --keystore $TMPDIR/8545/keystore \ 22 | --password /dev/null 23 | 24 | sleep 1 25 | 26 | # should be set afterwards 27 | greeting=$(seth call $addr 'greeting()(string)') 28 | [[ $greeting = "yo" ]] || error 29 | 30 | echo "Success." 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foundry-scripts", 3 | "version": "0.2.0", 4 | "description": "ds-test, cheat codes, and scripts for foundry", 5 | "main": "justfile", 6 | "directories": { 7 | "scripts": "scripts", 8 | "src": "src" 9 | }, 10 | "files": [ 11 | "justfile", 12 | "/scripts/**/*.sh", 13 | "/src/**/*.sol", 14 | "!/src/mocks/**/*" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/sambacha/foundry-scripts.git" 19 | }, 20 | "keywords": [ 21 | "foundry", 22 | "scripts", 23 | "ethereum", 24 | "solidity" 25 | ], 26 | "author": "SEE CONTRIBUTORS", 27 | "license": "SEE LICENSE IN LICENSE", 28 | "bugs": { 29 | "url": "https://github.com/sambacha/foundry-scripts/issues" 30 | }, 31 | "homepage": "https://github.com/sambacha/foundry-scripts#readme" 32 | } 33 | -------------------------------------------------------------------------------- /scripts/run-temp-testnet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # Utility for running a temporary dapp testnet w/ an ephemeral account 6 | # to be used for deployment tests 7 | 8 | # make a temp dir to store testnet info 9 | export TMPDIR=$(mktemp -d) 10 | 11 | # clean up 12 | trap 'killall geth && sleep 3 && rm -rf "$TMPDIR"' EXIT 13 | trap "exit 1" SIGINT SIGTERM 14 | 15 | # test helper 16 | error() { 17 | printf 1>&2 "fail: function '%s' at line %d.\n" "${FUNCNAME[1]}" "${BASH_LINENO[0]}" 18 | printf 1>&2 "got: %s" "$output" 19 | exit 1 20 | } 21 | 22 | # launch the testnet 23 | dapp testnet --dir "$TMPDIR" & 24 | # wait for it to launch (can't go <3s) 25 | sleep 3 26 | 27 | # set the RPC URL to the local testnet 28 | export ETH_RPC_URL=http://127.0.0.1:8545 29 | 30 | # get the created account (it's unlocked so we only need to set the address) 31 | export ETH_FROM=$(seth ls --keystore $TMPDIR/8545/keystore | cut -f1) 32 | -------------------------------------------------------------------------------- /scripts/dapptools/etherscacn-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Etherscan requires leading 'v' which isn't in the artifacts 4 | version="v${version}" 5 | 6 | # Get the list of supported solc versions and compare 7 | # Etherscan uses the js solc, which is not guaranteed to match the C distribution signature 8 | 9 | version_list=$(curl -fsS "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/list.txt") 10 | # There have been a couple of instances where the solc js release used by 11 | # Etherscan does not match the tag of the C distributions. 12 | if [[ $version_list != *"$version"* ]]; then 13 | regex="(.+commit+.)" 14 | # Version incompatible with js release 15 | echo "Compiler version $version is not compatible with etherscan" 16 | if [[ $version =~ $regex ]]; then 17 | version_proto=${BASH_REMATCH[1]} 18 | version=$(echo "$version_list" | grep -o "${version_proto}\{8\}") 19 | echo "Attempting ${version}" 20 | fi 21 | fi 22 | -------------------------------------------------------------------------------- /src/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | test "${DEBUG:-}" && set -x 5 | 6 | ## Override any user-supplied umask that could cause problems 7 | umask 002 8 | 9 | ## @note https://unix.stackexchange.com/a/145654/108960 10 | log_file="foundry_script_log-`date +'%Y-%m-%d_%H-%M-%S'`.txt" 11 | exec &> >(tee -a "$log_file") 12 | 13 | # Work from /install/ for install.sh, project root otherwise 14 | if [[ "$(basename $0)" = "install.sh" ]]; then 15 | cd "$(dirname $0)/install/" 16 | else 17 | cd "$(dirname $0)" # assume we're a test script or some such 18 | fi 19 | 20 | ## Allow `.env` overrides using the `.env.custom` file 21 | if [[ -f "../.env.custom" ]]; then 22 | _ENV="$(realpath ../.env.custom)" 23 | else 24 | _ENV="$(realpath ../.env)" 25 | fi 26 | 27 | ## Read .env for default values 28 | ## @see https://stackoverflow.com/a/59831605/90297 29 | t=$(mktemp) && export -p > "$t" && set -a && . $_ENV && set +a && . "$t" && rm "$t" && unset t 30 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | auto_detect_solc = true 3 | block_base_fee_per_gas = 0 4 | block_coinbase = '0x0000000000000000000000000000000000000000' 5 | block_difficulty = 0 6 | block_number = 0 7 | block_timestamp = 0 8 | cache = true 9 | cache_path = 'cache' 10 | evm_version = 'london' 11 | extra_output = [] 12 | extra_output_files = [] 13 | ffi = false 14 | force = false 15 | fuzz_max_global_rejects = 65536 16 | fuzz_max_local_rejects = 1024 17 | fuzz_runs = 256 18 | gas_limit = 9223372036854775807 19 | gas_price = 0 20 | gas_reports = ['*'] 21 | ignored_error_codes = [1878] 22 | initial_balance = '0xffffffffffffffffffffffff' 23 | libraries = [] 24 | libs = ['lib'] 25 | names = false 26 | offline = false 27 | optimizer = true 28 | optimizer_runs = 200 29 | out = 'out' 30 | remappings = [] 31 | sender = '0x00a329c0648769a73afac7f9381e08fb43dbea72' 32 | sizes = false 33 | src = 'src' 34 | test = 'test' 35 | tx_origin = '0x00a329c0648769a73afac7f9381e08fb43dbea72' 36 | verbosity = 0 37 | 38 | -------------------------------------------------------------------------------- /scripts/coverageSnapshotPhase.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # cd to the root of the repo 4 | cd "$(git rev-parse --show-toplevel)" 5 | 6 | coverageSnapshotPhase() 7 | { 8 | mkdir -p $PWD/.coverage/ 9 | genhtml lcov.info -o $PWD/.coverage > log 10 | # genhtml lcov.info $lcovExtraTraceFiles -o $out/.coverage > log 11 | lineCoverage="$(sed 's/.*lines\.*: \([0-9\.]\+\)%.*/\1/; t ; d' log)" 12 | functionCoverage="$(sed 's/.*functions\.*: \([0-9\.]\+\)%.*/\1/; t ; d' log)" 13 | if [ -z "$lineCoverage" -o -z "$functionCoverage" ]; then 14 | echo "⛔︎ Failed to get coverage statistics" 15 | exit 1 16 | fi 17 | echo "lineCoverage $lineCoverage %" >> $out/.coverage/snapshot 18 | echo "functionCoverage $functionCoverage %" >> $out/.coverage/snapshot 19 | touch $out/.coverage/snapshot/CI_TIMESTAMP 20 | date +"%Y%m%d%H%M%S" >> $out/.coverage/snapshot/CI_TIMESTAMP 21 | # cleanup 22 | rm log 23 | } 24 | 25 | coverageSnapshotPhase 26 | -------------------------------------------------------------------------------- /scripts/combine_artifact_json.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function combine_artifact_json { 4 | # Combine all dicts into a list with `jq --slurp` and then use `reduce` to merge them into one 5 | # big dict with keys of the form `":"`. Then run jq again to filter out items 6 | # with zero size and put the rest under under a top-level `bytecode_size` key. Also add another 7 | # key with total bytecode size. 8 | # NOTE: The extra inner `bytecode_size` key is there only to make diffs more readable. 9 | cat - | 10 | jq --slurp 'reduce (.[] | to_entries[]) as {$key, $value} ({}; . + { 11 | ($key + ":" + ($value | to_entries[].key)): { 12 | bytecode_size: $value | to_entries[].value 13 | } 14 | })' | 15 | jq --indent 4 --sort-keys '{ 16 | bytecode_size: [. | to_entries[] | select(.value.bytecode_size > 0)] | from_entries, 17 | total_bytecode_size: (reduce (. | to_entries[]) as {$key, $value} (0; . + $value.bytecode_size)) 18 | }' 19 | } 20 | -------------------------------------------------------------------------------- /scripts/git/submodules.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | 4 | 5 | for branch in $fetch; do 6 | git fetch origin $branch 7 | git branch $branch FETCH_HEAD 8 | done 9 | 10 | if [ "$ref" == "HEAD" ]; then 11 | return_ref=$(git rev-parse HEAD) 12 | else 13 | return_ref=$ref 14 | fi 15 | 16 | 17 | git --version 18 | 19 | echo "[CONFIG]: Updating and init submodules" 20 | git config --global diff.submodule log 21 | git config --global status.submoduleSummary true 22 | git config --global fetch.recurseSubmodules true 23 | git config --global push.recurseSubmodules on-demand 24 | 25 | sleep 1 26 | 27 | # 28 | # Git Submodules syncing 29 | # 30 | # Alternative oneliner 31 | # 32 | # ```shell 33 | # git submodule status | awk '{print $2}' | xargs -P5 -n1 git submodule update --init $depthflag --recursive 34 | #``` 35 | # 36 | 37 | echo "[TASK]: Syncing submodules" 38 | git submodule sync --recursive && git submodule update --init --recursive 39 | git submodule foreach --recursive git clean -ffdx 40 | sleep 1 41 | 42 | 43 | 44 | git submodule foreach git reset --hard HEAD 45 | git submodule update --remote --rebase lib/ 46 | git status 47 | exit 0 48 | -------------------------------------------------------------------------------- /scripts/git/ssh-handler-github.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | INPUT_FORCE=${INPUT_FORCE:-false} 4 | INPUT_FORCE_WITH_LEASE=${INPUT_FORCE_WITH_LEASE:-false} 5 | INPUT_SSH=${INPUT_SSH:-false} 6 | INPUT_TAGS=${INPUT_TAGS:-false} 7 | INPUT_DIRECTORY=${INPUT_DIRECTORY:-'.'} 8 | _FORCE_OPTION='' 9 | REPOSITORY=${INPUT_REPOSITORY:-$GITHUB_REPOSITORY} 10 | echo "Push to branch $INPUT_BRANCH"; 11 | [ -z "${INPUT_GITHUB_TOKEN}" ] && { 12 | echo 'Missing input "github_token: ${{ secrets.GITHUB_TOKEN }}".'; 13 | exit 1; 14 | }; 15 | if ${INPUT_FORCE} && ${INPUT_FORCE_WITH_LEASE}; then 16 | echo 'Please, specify only force or force_with_lease and not both.'; 17 | exit 1; 18 | fi 19 | if ${INPUT_FORCE}; then 20 | _FORCE_OPTION='--force' 21 | fi 22 | if ${INPUT_FORCE_WITH_LEASE}; then 23 | _FORCE_OPTION='--force-with-lease' 24 | fi 25 | if ${INPUT_TAGS}; then 26 | _TAGS='--tags' 27 | fi 28 | cd ${INPUT_DIRECTORY} 29 | if ${INPUT_SSH}; then 30 | remote_repo="git@${INPUT_GITHUB_URL}:${REPOSITORY}.git" 31 | else 32 | remote_repo="${INPUT_GITHUB_URL_PROTOCOL}//${GITHUB_ACTOR}:${INPUT_GITHUB_TOKEN}@${INPUT_GITHUB_URL}/${REPOSITORY}.git" 33 | fi 34 | 35 | git config --local --add safe.directory ${INPUT_DIRECTORY} 36 | 37 | if ${INPUT_FORCE_WITH_LEASE}; then 38 | git push --follow-tags $_FORCE_OPTION $_TAGS; 39 | else 40 | git push "${remote_repo}" HEAD:${INPUT_BRANCH} --verbose --follow-tags $_FORCE_OPTION $_TAGS; 41 | fi 42 | -------------------------------------------------------------------------------- /scripts/harness/prepare-certora.sh: -------------------------------------------------------------------------------- 1 | # change import "../Utils/SafeTransfer.sol" to harness code in spec/harness/SafeTransfer.sol 2 | perl -0777 -i -pe 's/\.\.\/Utils\/SafeTransfer\.sol/\.\.\/\.\.\/spec\/harness\/SafeTransfer.sol/g' contracts/Auctions/DutchAuction.sol 3 | 4 | # change import "../Access/AccessControls.sol" to harness code in spec/harness/AccessControls.sol 5 | perl -0777 -i -pe 's/\.\.\/Access\/AccessControls\.sol/\.\.\/\.\.\/spec\/harness\/AccessControls.sol/g' contracts/Auctions/DutchAuction.sol 6 | 7 | # change import ""../Utils/Documents.sol" to harnness code in spec/harness/Document.sol 8 | perl -0777 -i -pe 's/\.\.\/Utils\/Documents\.sol/\.\.\/\.\.\/spec\/harness\/Documents.sol/g' contracts/Auctions/DutchAuction.sol 9 | 10 | # virtualize private function 11 | perl -0777 -i -pe 's/\) private view /\) internal virtual view /g' contracts/Auctions/DutchAuction.sol 12 | 13 | # change eth transfer 14 | perl -0777 -i -pe 's/_beneficiary.transfer\(/_safeTokenPayment\(paymentCurrency,_beneficiary,/g' contracts/Auctions/DutchAuction.sol 15 | # virtualize public function 16 | perl -0777 -i -pe 's/public view returns/public virtual view returns/g' contracts/Auctions/DutchAuction.sol 17 | 18 | # virtualize batch 19 | perl -0777 -i -pe 's/function batch\(bytes\[\] calldata calls, bool revertOnFail\) external/function batch\(bytes\[\] calldata calls, bool revertOnFail\) external virtual/g' contracts/Utils/BoringBatchable.sol 20 | -------------------------------------------------------------------------------- /scripts/deploy/verify-after-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # takes in a deployment file and verifies on etherscan (useful if you deploy without verification) 3 | # needs to have ETHERSCAN_API_KEY set in the environment 4 | # Taken from Telegram user @riordant 5 | # thanks bud 6 | set -e 7 | set -o xtrace 8 | INPUT_FILE=$1 9 | ARG_FILE="$(dirname $INPUT_FILE)/arguments.json" 10 | CHAIN_ID=$(jq -r '.chain' < $INPUT_FILE) 11 | COMPILER_VERSION="0.8.17" 12 | 13 | LIBRARIES="" 14 | for row in $(jq -c '.libraries[]' < ${INPUT_FILE}); do 15 | LIBRARIES+=" --libraries ${row}" 16 | done 17 | LIBRARIES=$( echo $LIBRARIES | tr -d '"' | tr -d "'" ) 18 | 19 | for row in $(jq -c '.transactions[]' < ${INPUT_FILE}); do 20 | _jq() { 21 | echo ${row} | jq -r ${1} 22 | } 23 | 24 | if [[ $(_jq '.transactionType') == "CREATE" ]]; then 25 | 26 | args=$(_jq '.arguments') 27 | CONSTRUCTOR_ARGS_PATH="" 28 | if [[ ${args} != "null" ]]; then 29 | echo ${args} > "${ARG_FILE}" 30 | CONSTRUCTOR_ARGS_PATH=--constructor-args-path=${ARG_FILE} 31 | fi 32 | if ! forge verify-contract "$(_jq '.contractAddress')" "$(_jq '.contractName')" --etherscan-api-key ${ETHERSCAN_API_KEY} --chain ${CHAIN_ID} --compiler-version ${COMPILER_VERSION} ${CONSTRUCTOR_ARGS_PATH} {$LIBRARIES} 33 | then 34 | echo "failed to verify $(_jq '.contractName')" 35 | continue 36 | fi 37 | 38 | fi 39 | done 40 | -------------------------------------------------------------------------------- /scripts/deploy/interactive.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export LC_ALL=C 4 | 5 | # CONTRACT=src/Entrypoint.sol:EntrypointName 6 | CONTRACT= 7 | BASE_ARGS="--rpc-url $ETH_RPC_URL --json $CONTRACT" 8 | 9 | if [ "$1" == "-e" ]; then 10 | SHOW_ENV=1 11 | fi 12 | 13 | printEnv() { 14 | if [ "$SHOW_ENV" == "1" ]; then 15 | echo -e "ETH_RPC_URL\t\t\t$ETH_RPC_URL" 16 | echo -e "SIGNER_KEY\t\t$SIGNER_KEY" 17 | echo -e "CHAIN_OVERRIDE\t\t$CHAIN_OVERRIDE" 18 | fi 19 | } 20 | 21 | fsSync_Address() { 22 | echo $OUTPUT 23 | ADDRESS=$(echo $OUTPUT | jq .deployedTo) 24 | echo "{\"address\": $ADDRESS}" > contract.json 25 | } 26 | 27 | if [[ -f .env ]]; then 28 | echo "Loading '.env'..." 29 | # load .env 30 | set -a 31 | source <(cat .env | sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g") 32 | set +a 33 | printEnv 34 | 35 | if [ -z $CHAIN_OVERRIDE ]; then 36 | echo "Using default CHAIN_ID" 37 | else 38 | CHAIN_ARG="--chain $CHAIN_OVERRIDE" 39 | fi 40 | 41 | echo "Deploying ($CONTRACT)..." 42 | OUTPUT=$(forge create --private-key $SIGNER_KEY $CHAIN_ARG $BASE_ARGS) 43 | fsSync_Address 44 | else 45 | if [ -z $ETH_RPC_URL ]; then 46 | echo "![ERR]: ETH_RPC_URL must be set." 47 | else 48 | printEnv 49 | echo "[TASK]: Deploying..." 50 | # use interactive prompt 51 | script -q -c "forge create --interactive $BASE_ARGS" tmpOutput 52 | OUTPUT=$(cat tmpOutput | tail -n +4 | head -n 1) 53 | fsSync_Address 54 | fi 55 | fi 56 | 57 | echo "You can remove tmpOutput file after deployment." 58 | 59 | # LC_ALL=C sort -t. -k 1,1 -k 2,2n -k 3,3n 60 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | 3 | root = true 4 | [*] 5 | indent_style = space 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | max_line_length = 100 11 | indent_size = 2 12 | 13 | # Markdown 14 | [*.{md,adoc,asciidoc}] 15 | charset = utf-8 16 | end_of_line = lf 17 | insert_final_newline = true 18 | trim_trailing_whitespace = false 19 | 20 | # Match nix files, set indent to spaces with width of two 21 | [*.nix] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | # JavaScript, JSON, JSX, JavaScript Modules, TypeScript 26 | # https://github.com/feross/standard 27 | # https://prettier.io 28 | [*.{cjs,js,json,jsx,mjs,ts,tsx}] 29 | indent_size = 2 30 | indent_style = space 31 | 32 | # HTML 33 | # https://google.github.io/styleguide/htmlcssguide.xml#General_Formatting_Rules 34 | [*.{htm,html}] 35 | indent_size = 2 36 | indent_style = space 37 | trim_trailing_whitespace = true 38 | # TOML 39 | # https://github.com/toml-lang/toml/tree/master/examples 40 | [*.toml] 41 | indent_size = 2 42 | indent_style = space 43 | 44 | # YAML 45 | # http://yaml.org/spec/1.2/2009-07-21/spec.html#id2576668 46 | [*.{yaml,yml}] 47 | indent_size = 2 48 | indent_style = space 49 | 50 | # Shell 51 | # https://google.github.io/styleguide/shell.xml#Indentation 52 | [*.{bash,sh,zsh}] 53 | indent_size = 2 54 | indent_style = space 55 | 56 | # confg + cfg 57 | [*.{conf,cfg}] 58 | charset = UTF-8 59 | end_of_line = LF 60 | indent_size = 4 61 | indent_style = tab 62 | insert_final_newline = true 63 | tab_width = 4 64 | trim_trailing_whitespace = true 65 | 66 | # Match diffs, avoid to trim trailing whitespace 67 | [*.{diff,patch}] 68 | trim_trailing_whitespace = false 69 | -------------------------------------------------------------------------------- /gas-snapshot/src/reporting/README.md: -------------------------------------------------------------------------------- 1 | 2 | ```diff 3 | ------ 01_gas_snapshot 2022-05-09 10:01:19.000000Z 4 | ++++++ 02_gas_snapshot 2022-05-09 10:01:30.000000Z 5 | @|-1,16 +1,16 ============================================================ 6 | |testAllPairs() (gas: 3165) 7 | -|testLiquidityEth() (gas: 26657) 8 | +|testLiquidityEth() (gas: 23229) 9 | -|testLiquidityEthSupportingFeeOnTransfer() (gas: 26736) 10 | +|testLiquidityEthSupportingFeeOnTransfer() (gas: 23308) 11 | -|testLiquidityTokens() (gas: 26424) 12 | +|testLiquidityTokens() (gas: 22996) 13 | -|testSwapETHForExactTokens() (gas: 19828) 14 | +|testSwapETHForExactTokens() (gas: 16457) 15 | -|testSwapExactETHForTokens() (gas: 26686) 16 | +|testSwapExactETHForTokens() (gas: 23258) 17 | -|testSwapExactETHForTokensBackrun() (gas: 22170) 18 | +|testSwapExactETHForTokensBackrun() (gas: 18742) 19 | -|testSwapExactETHForTokensSupportingFeeOnTransferTokens() (gas: 9999) 20 | -|testSwapExactTokensForETH() (gas: 26632) 21 | +|testSwapExactETHForTokensSupportingFeeOnTransferTokens() (gas: 9999) 22 | +|testSwapExactTokensForETH() (gas: 23204) 23 | -|testSwapExactTokensForETHSupportingFeeOnTransferTokens() (gas: 26711) 24 | +|testSwapExactTokensForETHSupportingFeeOnTransferTokens() (gas: 23283) 25 | -|testSwapExactTokensForTokens() (gas: 26500) 26 | +|testSwapExactTokensForTokens() (gas: 23072) 27 | -|testSwapExactTokensForTokensSupportingFeeOnTransferTokens() (gas: 26607) 28 | +|testSwapExactTokensForTokensSupportingFeeOnTransferTokens() (gas: 23179) 29 | -|testSwapTokensForExactETH() (gas: 26571) 30 | +|testSwapTokensForExactETH() (gas: 23143) 31 | -|testSwapTokensForExactTokens() (gas: 26791) 32 | +|testSwapTokensForExactTokens() (gas: 23363) 33 | -|testZapLiquidity() (gas: 26461) 34 | +|testZapLiquidity() (gas: 23033) 35 | -|testZapLiquidityEth() (gas: 21614) 36 | +|testZapLiquidityEth() (gas: 18067) 37 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Foundry Scripts 2 | 3 | Copyright (c) 2022 Foundry Scripts Contributors. 4 | 5 | The Universal Permissive License (UPL), Version 1.0 6 | 7 | Subject to the condition set forth below, permission is hereby granted to any 8 | person obtaining a copy of this software, associated documentation and/or data 9 | (collectively the "Software"), free of charge and under any and all copyright 10 | rights in the Software, and any and all patent rights owned or freely 11 | licensable by each licensor hereunder covering either (i) the unmodified 12 | Software as contributed to or provided by such licensor, or (ii) the Larger 13 | Works (as defined below), to deal in both 14 | 15 | (a) the Software, and 16 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 17 | one is included with the Software (each a "Larger Work" to which the Software 18 | is contributed by such licensors), 19 | 20 | without restriction, including without limitation the rights to copy, create 21 | derivative works of, display, perform, and distribute the Software and make, 22 | use, sell, offer for sale, import, export, have made, and have sold the 23 | Software and the Larger Work(s), and to sublicense the foregoing rights on 24 | either these or other terms. 25 | 26 | This license is subject to the following condition: 27 | The above copyright notice and either this complete permission notice or at 28 | a minimum a reference to the UPL must be included in all copies or 29 | substantial portions of the Software. 30 | 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 37 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 38 | SOFTWARE. 39 | -------------------------------------------------------------------------------- /.github/workflows/justfile.yml: -------------------------------------------------------------------------------- 1 | name: justfile 2 | 3 | on: push 4 | 5 | # ensure multiple CI processes are not running analysis on contracts 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }} 8 | cancel-in-progress: true 9 | 10 | env: 11 | CI: true 12 | COMMIT_SHA: ${{ github.event.pull_request.head.sha }} 13 | PULL_NUMBER: ${{ github.event.pull_request.number }} 14 | RUN_ID: ${{ github.run_id }} 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | jobs: 18 | pipeline: 19 | name: Justfile ${{ matrix.just-version }} on ${{ matrix.os }} 20 | runs-on: ${{ matrix.os }} 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | os: ['ubuntu-latest'] 26 | just-version: [0.9.9, 0.10.0] 27 | 28 | 29 | 30 | steps: 31 | - uses: actions/checkout@v3 32 | - uses: extractions/setup-just@v1 33 | 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | - name: Test 37 | id: quicktest 38 | run: | 39 | [ "$(just --justfile __tests__/test.justfile)" = "Test..." ] 40 | shell: bash 41 | 42 | test-version: 43 | needs: pipeline 44 | strategy: 45 | matrix: 46 | just-version: [0.10.0] 47 | 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v3 51 | - name: Install Foundry 52 | uses: foundry-rs/foundry-toolchain@v1 53 | with: 54 | version: nightly 55 | - uses: extractions/setup-just@v1 56 | with: 57 | just-version: ${{ matrix.just-version }} 58 | 59 | - name: Check version 60 | run: just --version | grep -E "^just v?${{ matrix.just-version }}$" 61 | - name: Test 62 | id: justfile 63 | run: | 64 | [ "$(just --justfile __tests__/test.justfile)" = "Test..." ] 65 | 66 | 67 | - name: foundry test package 68 | id: foundry 69 | run: | 70 | cd __tests__; forge init --no-commit --no-git --force; 71 | echo $PWD 72 | just dumpbuild -------------------------------------------------------------------------------- /LICENSE.spdx: -------------------------------------------------------------------------------- 1 | SPDXVersion: SPDX-2.0 2 | Creator: Sam Bacha 3 | PackageName: foundry-scripts 4 | PackageOriginator: Sam Bacha 5 | PackageHomePage: https://github.com/sambacha/foundry-scripts 6 | PackageLicenseDeclared: UPL-1.0 7 | 8 | Foundry Scripts 9 | 10 | Copyright (c) 2022 Foundry Scripts Contributors. 11 | 12 | The Universal Permissive License (UPL), Version 1.0 13 | 14 | Subject to the condition set forth below, permission is hereby granted to any 15 | person obtaining a copy of this software, associated documentation and/or data 16 | (collectively the "Software"), free of charge and under any and all copyright 17 | rights in the Software, and any and all patent rights owned or freely 18 | licensable by each licensor hereunder covering either (i) the unmodified 19 | Software as contributed to or provided by such licensor, or (ii) the Larger 20 | Works (as defined below), to deal in both 21 | 22 | (a) the Software, and 23 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 24 | one is included with the Software (each a "Larger Work" to which the Software 25 | is contributed by such licensors), 26 | 27 | without restriction, including without limitation the rights to copy, create 28 | derivative works of, display, perform, and distribute the Software and make, 29 | use, sell, offer for sale, import, export, have made, and have sold the 30 | Software and the Larger Work(s), and to sublicense the foregoing rights on 31 | either these or other terms. 32 | 33 | This license is subject to the following condition: 34 | The above copyright notice and either this complete permission notice or at 35 | a minimum a reference to the UPL must be included in all copies or 36 | substantial portions of the Software. 37 | 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 45 | SOFTWARE. 46 | -------------------------------------------------------------------------------- /scripts/deploy/calculate-init-code-hash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Function to exit on error with a message 4 | exit_on_error() { 5 | echo "Error: $1" 6 | exit 1 7 | } 8 | 9 | # Function to display usage information 10 | usage() { 11 | echo "Usage: $0 [-v]" 12 | echo " : Path to the Solidity contract file" 13 | echo " : Name of the Solidity contract" 14 | echo " -v : Enable verbose output (optional)" 15 | exit 1 16 | } 17 | 18 | # Check if foundry is installed 19 | if ! command -v forge &> /dev/null; then 20 | exit_on_error "Foundry is not installed. Please install it first." 21 | fi 22 | 23 | # Check if correct number of arguments is provided 24 | if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then 25 | usage 26 | fi 27 | 28 | # Variables from arguments 29 | CONTRACT_PATH=$1 30 | CONTRACT_NAME=$2 31 | VERBOSE=false 32 | 33 | # Check if verbose option is enabled 34 | if [ "$#" -eq 3 ] && [ "$3" == "-v" ]; then 35 | VERBOSE=true 36 | fi 37 | 38 | # Validate contract path 39 | if [ ! -f "$CONTRACT_PATH" ]; then 40 | exit_on_error "Contract path '$CONTRACT_PATH' does not exist or is not a file." 41 | fi 42 | 43 | # Get the directory of the contract path 44 | CONTRACT_DIR=$(dirname "$CONTRACT_PATH") 45 | # shellcheck disable=SC2034 46 | CONTRACT_FILE=$(basename "$CONTRACT_PATH") 47 | 48 | # Change to the contract directory 49 | cd "$CONTRACT_DIR" || exit_on_error "Failed to change directory to '$CONTRACT_DIR'." 50 | 51 | # Step 1: Compile the contract 52 | echo "Compiling the contract..." 53 | if $VERBOSE; then 54 | forge build --contracts . || exit_on_error "Failed to compile the contract." 55 | else 56 | forge build --contracts . > /dev/null 2>&1 || exit_on_error "Failed to compile the contract." 57 | fi 58 | 59 | # Step 2: Generate the init code 60 | echo "Generating init code..." 61 | INIT_CODE=$(forge inspect "$CONTRACT_NAME" bytecode) || exit_on_error "Failed to generate init code." 62 | 63 | # Step 3: Calculate the init code hash 64 | echo "Calculating init code hash..." 65 | INIT_CODE_HASH=$(echo -n "$INIT_CODE" | keccak-256sum | cut -d' ' -f1) || exit_on_error "Failed to calculate init code hash." 66 | 67 | # Step 4: Write the init code hash to a file 68 | OUTPUT_FILE="${CONTRACT_NAME}_INIT_CODE_HASH.txt" 69 | echo "$INIT_CODE_HASH" > "$OUTPUT_FILE" || exit_on_error "Failed to write init code hash to file." 70 | 71 | # Clean up temporary files (if any) 72 | # (Add commands here if there are any temporary files to clean up) 73 | 74 | # Step 5: Output the result 75 | echo "Init code hash has been written to $OUTPUT_FILE" 76 | if $VERBOSE; then 77 | echo "Init code: $INIT_CODE" 78 | echo "Init code hash: $INIT_CODE_HASH" 79 | fi 80 | 81 | # Revert to original directory 82 | cd - > /dev/null || exit 83 | -------------------------------------------------------------------------------- /scripts/utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #################################################################################################################################### 4 | # SPDX-License-Identifier: ISC 5 | # Bash Utilities 6 | #################################################################################################################################### 7 | 8 | # exit_with_error - Prints passed message with [Error] prefix and exits with error code 1 9 | # exit_on_error - If current status is non-0, prints passed message with [Error] prefix and exits with error code 1 10 | 11 | # check_for_required_variables - Validates whether passed array each element is defined as variable 12 | 13 | # Global variable ${logging_lvl} is used for logging level. Currently supported: debug, info, error(default) 14 | 15 | # log_debug - If global variable ${logging_lvl} set up to debug, log passed message with [Debug] prefix 16 | # log_info - If global variable ${logging_lvl} set at least to info, log passed message with [Info] prefix 17 | # log_debug - If global variable ${logging_lvl} set at least to error(default), log passed message with [Error] prefix 18 | # log_set_logging_lvl - If not defined, set ${logging_lvl}=error 19 | # logging_lvl_validate - Validate if set loging level is suported. 20 | 21 | ### Error Handling 22 | 23 | exit_with_error() { 24 | echo 25 | echo "[Error] $@" 26 | exit 1 27 | } 28 | 29 | exit_on_error() { 30 | if [ $? -ne 0 ]; then 31 | echo 32 | echo "[Error] $@" 33 | exit 1 34 | fi 35 | } 36 | 37 | random() { 38 | cat /dev/urandom | env LC_CTYPE=C tr -dc $1 | head -c $2 39 | echo 40 | } 41 | 42 | randompw() { 43 | # Generate a random password (16 characters) that starts with an alpha character 44 | echo $(random [:alpha:] 1)$(random [:alnum:] 15) 45 | } 46 | 47 | ### Logging 48 | 49 | log_debug() { 50 | if [ "${logging_lvl}" == "debug" ]; then echo "[Debug] $@"; fi 51 | } 52 | 53 | log_info() { 54 | if [[ "${logging_lvl}" =~ (debug|info) ]]; then echo "[Info] $@"; fi 55 | } 56 | 57 | log_error() { 58 | if [[ "${logging_lvl}" =~ (debug|info|error) ]]; then echo && echo "[Error] $@"; fi 59 | } 60 | 61 | log_set_logging_lvl() { 62 | if [ -z ${logging_lvl} ]; then 63 | logging_lvl="error" 64 | echo "[Info] Logging level not set, defaulting to 'error'." 65 | fi 66 | } 67 | 68 | logging_lvl_validate() { 69 | if [[ "${logging_lvl}" =~ (debug|info|error) ]]; then 70 | log_debug " [Validation Passed] logging_lvl = '${logging_lvl}'" 71 | else 72 | exit_with_error " [Validation Failed] Unsupported logging level '${logging_lvl}'. Supported loggin levels are 'debug|info|error'." 73 | fi 74 | } 75 | 76 | # Function wrapper for friendly logging and basic timing 77 | run() { 78 | SECONDS=0 79 | echo "[Running '$@']" 80 | $@ 81 | echo "['$@' finished in $SECONDS seconds]" 82 | echo "" 83 | } 84 | -------------------------------------------------------------------------------- /forge.justfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env just --justfile 2 | 3 | bt := '0' 4 | export RUST_BACKTRACE := bt 5 | log := "warn" 6 | export JUST_LOG := log 7 | 8 | 9 | _default: 10 | just --list 11 | 12 | # load .env file 13 | set dotenv-load 14 | 15 | # pass justfile recipe args as positional arguments to commands 16 | set positional-arguments 17 | 18 | DAPP_BUILD_OPTIMIZE := "1" 19 | DAPP_COVERAGE := "1" 20 | # when developing we only want to fuzz briefly 21 | DAPP_TEST_FUZZ_RUNS := "100" 22 | 23 | # 1e18 decimals 24 | HEX_18 := "0x0000000000000000000000000000000000000000000000000000000000000012" 25 | 26 | # set mock target to 18 decimals by default 27 | FORGE_MOCK_TARGET_DECIMALS := env_var_or_default("FORGE_MOCK_TARGET_DECIMALS", HEX_18) 28 | FORGE_MOCK_UNDERLYING_DECIMALS := env_var_or_default("FORGE_MOCK_UNDERLYING_DECIMALS", HEX_18) 29 | 30 | #ALCHEMY_KEY := env_var("ALCHEMY_KEY") 31 | #MAINNET_RPC := "https://eth-mainnet.alchemyapi.io/v2/" + ALCHEMY_KEY 32 | #MNEMONIC := env_var("MNEMONIC") 33 | 34 | # export just vars as env vars 35 | set export 36 | 37 | 38 | size: 39 | forge build --sizes --force 40 | 41 | 42 | # build using forge 43 | build: && _timer 44 | cd {{ invocation_directory() }}; forge build --sizes --names --force 45 | 46 | 47 | # [TEST] default test scripts 48 | test: test-local 49 | 50 | # [TEST] run local forge test using --match 51 | test-local *commands="": && _timer 52 | cd {{ invocation_directory() }}; forge test -m ".t.sol" {{ commands }} 53 | 54 | # [TEST] run mainnet fork forge tests (all files with the extension .t.sol) 55 | #test-mainnet *commands="": && _timer 56 | # cd {{ invocation_directory() }}; forge test --rpc-url {{ MAINNET_RPC }} -m ".t.sol" {{ commands }} 57 | 58 | # [TEST] run mainnet fork forge debug tests (all files with the extension .t.sol) 59 | #test-debug *commands="": && _timer 60 | # cd {{ invocation_directory() }}; forge test --rpc-url {{ MAINNET_RPC }} -m ".t.sol" {{ commands }} 61 | 62 | 63 | # [GAS] default gas snapshot script 64 | gas-snapshot: gas-snapshot-local 65 | 66 | # [GAS] get gas snapshot from local tests and save it to file 67 | gas-snapshot-local: 68 | cd {{ invocation_directory() }}; \ 69 | just test-local | grep 'gas:' | cut -d " " -f 2-4 | sort > \ 70 | {{ justfile_directory() }}/gas-snapshots/.$( \ 71 | cat {{ invocation_directory() }}/package.json | jq .name | tr -d '"' | cut -d"/" -f2- \ 72 | ) 73 | # [GAS] get gas snapshot timer 74 | forge-gas-snapshot: && _timer 75 | @cd {{ invocation_directory() }}; forge snapshot --no-match-path ".*.*" 76 | 77 | forge-gas-snapshot-diff: && _timer 78 | @cd {{ invocation_directory() }}; forge snapshot --no-match-path ".*.*" --diff 79 | 80 | # Solidity test ffi callback to get Target decimals for the base Mock Target token 81 | _forge_mock_target_decimals: 82 | @printf {{ FORGE_MOCK_TARGET_DECIMALS }} 83 | 84 | _forge_mock_underlying_decimals: 85 | @printf {{ FORGE_MOCK_UNDERLYING_DECIMALS }} 86 | 87 | # [UTILS] utility functions 88 | start_time := `date +%s` 89 | _timer: 90 | @echo "[TASK]: Executed in $(($(date +%s) - {{ start_time }})) seconds" 91 | 92 | # mode: makefile 93 | # End: 94 | # vim: set ft=make : 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [`foundry-scripts`](https://github.com/sambacha/foundry-scripts) 2 | 3 | a picture of justfile recipes 4 | 5 |
6 | 7 |
8 |
9 | 10 | 11 |

tldr

12 | 13 | [![justfile](https://github.com/sambacha/foundry-scripts/actions/workflows/justfile.yml/badge.svg)](https://github.com/sambacha/foundry-scripts/actions/workflows/justfile.yml) 14 | 15 | > `just` is the new `make` 16 | > 17 | 18 | > load `.env` via `justfile` 19 | > run tests; etc; 20 | > `TLR`s; two letter recipes 21 |
22 | 23 | ## Quickstart 24 | 25 | > download the latest `justfile` 26 | 27 | ```shell 28 | curl https://raw.githubusercontent.com/sambacha/foundry-scripts/master/justfile --output justfile --silent 29 | ``` 30 | 31 | In your repo, you execute the command `just` and add a `recipe` after to run the process 32 | 33 | ```shell 34 | $ just 35 | Available recipes: 36 | build # [BUILD]: Timer 37 | build-mainnet # [TEST] mainnet test 38 | deploy-contract # [DEPLOY]: Deploy contract 39 | dumpbuild # Executes a dump of ethers-rs solc compiler output into two json files 40 | forge-gas-snapshot # [GAS] get gas snapshot timer 41 | forge-gas-snapshot-diff 42 | gas-cov 43 | gas-snapshot # [GAS] default gas snapshot script 44 | gas-snapshot-local # [GAS] get gas snapshot from local tests and save it to file 45 | size # Contract Size 46 | sl 47 | test # [TEST] default test scripts 48 | test-debug *commands="" # [TEST] run mainnet fork forge debug tests (all files with the extension .t.sol) 49 | test-local *commands="" # [TEST] run local forge test using --match-path 50 | test-mainnet *commands="" # [TEST] run mainnet fork forge tests (all files with the extension .t.sol) 51 | tl 52 | verify-contract # [DEPLOY]: Verify contract 53 | ``` 54 | 55 | ## using ssh 56 | 57 | ```bash 58 | USE_SSH=true 59 | GIT_USER= forge deploy 60 | ``` 61 | 62 | ### Two Letter Recipes 63 | 64 | > TLR's 65 | 66 | ```shell 67 | just tl 68 | ``` 69 | 70 | ```makefile 71 | tl: 72 | forge test --list 73 | 74 | sl: 75 | forge snapshot --list 76 | ``` 77 | 78 | 79 | ### ZSH Completions 80 | 81 | In .zlogin: 82 | ```zsh 83 | fpath=($HOME/.zsh/completion $fpath) 84 | 85 | autoload -Uz compinit 86 | compinit -u 87 | ``` 88 | In `$HOME/.zsh/completion/_just` 89 | 90 | ```shell 91 | #compdef _just just 92 | #autoload 93 | 94 | _just () { 95 | local -a subcmds 96 | 97 | subcmds=($(just --summary)) 98 | 99 | _describe 'command' subcmds 100 | } 101 | 102 | _just "$@" 103 | ``` 104 | 105 | ### FiSH 106 | 107 | > completions/just.fish: 108 | 109 | ```fish 110 | complete -c just -a (just --summary) 111 | ``` 112 | 113 | ### Bash v4.2+ 114 | 115 | > Simple. 116 | > 117 | ```bash 118 | complete -W '$(just --summary)' just 119 | ``` 120 | > Alt. 121 | 122 | ```bash 123 | if [ -f justfile ]; then 124 | complete -W "`just --summary`" just 125 | fi 126 | 127 | # ex: ts=4 sw=4 et filetype=sh 128 | ``` 129 | 130 | ### Developer notes 131 | 132 | If you modify the `justfile`, run `just --fmt --unstable` and it will format the `justfile` itself. 133 | 134 | ## Repo Example 135 | 136 | [see https://github.com/sambacha/foundry-justfile-example](https://github.com/sambacha/foundry-justfile-example) 137 | 138 | 139 | ## License 140 | 141 | Apache-2.0/MIT 142 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env just --justfile 2 | # @title Foundry Justfile 3 | # @version: 0.4.4 4 | # @license UPL-1.0 5 | # @see {@link https://github.com/sambacha/foundry-scripts} 6 | 7 | bt := '0' 8 | export RUST_BACKTRACE := bt 9 | et := 'ethers=trace' 10 | export RUST_LOG := et 11 | log := "warn" 12 | export JUST_LOG := log 13 | 14 | # RUST_LOG=node,backend,api,rpc=warn anvil 15 | _default: 16 | just --list 17 | 18 | tl: 19 | forge test --list 20 | 21 | sl: 22 | forge snapshot --list 23 | 24 | dumpbuild: 25 | ETHERS_SOLC_LOG=in=in.json,out=out.json; forge build --force 26 | 27 | dumplists: 28 | FORGE_GAS_REPORT=''; forge test -l -j > test-list.json 29 | 30 | # load .env file 31 | 32 | set dotenv-load := true 33 | 34 | # pass justfile recipe args as positional arguments to commands 35 | 36 | set positional-arguments := true 37 | 38 | # 1e18 decimals 39 | 40 | HEX_18 := "0x0000000000000000000000000000000000000000000000000000000000000012" 41 | HEX_12 := "0x000000000000000000000000000000000000000000000000000000000000000c" 42 | HEX_8 := "0x0000000000000000000000000000000000000000000000000000000000000008" 43 | HEX_6 := "0x0000000000000000000000000000000000000000000000000000000000000006" 44 | 45 | # set mock target to 18 decimals by default 46 | 47 | FORGE_MOCK_TARGET_DECIMALS := env_var_or_default("FORGE_MOCK_TARGET_DECIMALS", HEX_18) 48 | FORGE_MOCK_UNDERLYING_DECIMALS := env_var_or_default("FORGE_MOCK_UNDERLYING_DECIMALS", HEX_18) 49 | 50 | # Alchemy API Key is public 51 | # Mnemonic is hardhat's default 52 | 53 | ALCHEMY_KEY := env_var_or_default("ALCHEMY_KEY", "vI8OBZj4Wue9yNPSDVa7Klqt-UeRywrx") 54 | MAINNET_RPC := "https://eth-mainnet.alchemyapi.io/v2/" + ALCHEMY_KEY 55 | MNEMONIC := env_var_or_default("MNEMONIC", "test test test test test test test test test test test junk") 56 | 57 | # export just vars as env vars 58 | 59 | set export := true 60 | 61 | # Contract Size 62 | size: 63 | forge build --sizes --force 64 | 65 | # [DEPLOY]: Env Config 66 | 67 | DEPLOYED_ADDRESS := '' 68 | CONTRACT_NAME := '' 69 | ETHERSCAN_API_KEY := '' 70 | 71 | # [DEPLOY]: Deploy contract 72 | deploy-contract: 73 | forge create $(contract) \ 74 | --constructor-args $(constructorArgs) \ 75 | --rpc-url $(url) \ 76 | --private-key $(privateKey) 77 | 78 | # [DEPLOY]: Verify contract 79 | verify-contract: 80 | forge verify-contract \ 81 | --chain-id $(chainId) \ 82 | --constructor-args `cast abi-encode "$(constructorSig)" $(constructorArgs)` \ 83 | --compiler-version $(compilerVersion) \ 84 | --num-of-optimizations 200 \ 85 | {{ DEPLOYED_ADDRESS }} \ 86 | {{ CONTRACT_NAME }} \ 87 | {{ ETHERSCAN_API_KEY }} 88 | 89 | # [BUILD]: Timer 90 | build: && _timer 91 | cd {{ invocation_directory() }}; forge build --sizes --names --force 92 | 93 | # [TEST] mainnet test 94 | build-mainnet: && _timer 95 | cd {{ invocation_directory() }}; forge test --match-path "*.t.sol" --fork-url {{ MAINNET_RPC }} 96 | 97 | # [TEST] default test scripts 98 | test: test-local 99 | 100 | # [TEST] run local forge test using --match-path 101 | test-local *commands="": && _timer 102 | cd {{ invocation_directory() }}; forge test --match-path "*.t.sol" {{ commands }} 103 | 104 | # [TEST] run mainnet fork forge tests (all files with the extension .t.sol) 105 | test-mainnet *commands="": && _timer 106 | cd {{ invocation_directory() }}; forge test --rpc-url {{ MAINNET_RPC }} --match-path "*.t.sol" {{ commands }} 107 | 108 | # [TEST] run mainnet fork forge debug tests (all files with the extension .t.sol) 109 | test-debug *commands="": && _timer 110 | cd {{ invocation_directory() }}; forge test --rpc-url {{ MAINNET_RPC }} --match-path "*.t.sol" {{ commands }} 111 | 112 | gas-cov: 113 | forge test --gas-report 114 | 115 | # [GAS] default gas snapshot script 116 | gas-snapshot: gas-snapshot-local 117 | 118 | # [GAS] get gas snapshot from local tests and save it to file 119 | gas-snapshot-local: 120 | cd {{ invocation_directory() }}; \ 121 | just test-local | grep 'gas:' | cut -d " " -f 2-4 | sort > \ 122 | {{ justfile_directory() }}/gas-snapshots/.$( \ 123 | cat {{ invocation_directory() }}/package.json | jq .name | tr -d '"' | cut -d"/" -f2- \ 124 | ) 125 | 126 | # [GAS] get gas snapshot timer 127 | forge-gas-snapshot: && _timer 128 | @cd {{ invocation_directory() }}; forge snapshot --no-match-path ".*.*" 129 | 130 | forge-gas-snapshot-diff: && _timer 131 | @cd {{ invocation_directory() }}; forge snapshot --no-match-path ".*.*" --diff 132 | 133 | # Solidity test ffi callback to get Target decimals for the base Mock Target token 134 | _forge_mock_target_decimals: 135 | @printf {{ FORGE_MOCK_TARGET_DECIMALS }} 136 | 137 | _forge_mock_underlying_decimals: 138 | @printf {{ FORGE_MOCK_UNDERLYING_DECIMALS }} 139 | 140 | # [UTILS] utility functions 141 | 142 | start_time := `date +%s` 143 | 144 | _timer: 145 | @echo "[TASK]: Executed in $(($(date +%s) - {{ start_time }})) seconds" 146 | 147 | # mode: makefile 148 | # End: 149 | # vim: set ft=make : 150 | # vi: set ts=4 sw=4 noet : 151 | -------------------------------------------------------------------------------- /__tests__/justfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env just --justfile 2 | # @title Foundry Justfile 3 | # @version: 0.4.2 4 | # @license Apache-2.0 OR MIT 5 | # @see {@link https://github.com/sambacha/foundry-scripts} 6 | 7 | bt := '0' 8 | export RUST_BACKTRACE := bt 9 | et := 'ethers=trace' 10 | export RUST_LOG := et 11 | log := "warn" 12 | export JUST_LOG := log 13 | 14 | _default: 15 | just --list 16 | 17 | tl: 18 | forge test --list 19 | 20 | sl: 21 | forge snapshot --list 22 | 23 | dumpbuild: 24 | ETHERS_SOLC_LOG=in=in.json,out=out.json; forge build --force 25 | 26 | # load .env file 27 | 28 | set dotenv-load := true 29 | 30 | # pass justfile recipe args as positional arguments to commands 31 | 32 | set positional-arguments := true 33 | 34 | # sourced from https://github.com/sense-finance/sense-v1 35 | 36 | DAPP_BUILD_OPTIMIZE := "1" 37 | DAPP_COVERAGE := "1" 38 | DAPP_TEST_FUZZ_RUNS := "100" 39 | 40 | # 1e18 decimals 41 | 42 | HEX_18 := "0x0000000000000000000000000000000000000000000000000000000000000012" 43 | HEX_12 := "0x000000000000000000000000000000000000000000000000000000000000000c" 44 | HEX_8 := "0x0000000000000000000000000000000000000000000000000000000000000008" 45 | HEX_6 := "0x0000000000000000000000000000000000000000000000000000000000000006" 46 | 47 | # set mock target to 18 decimals by default 48 | 49 | FORGE_MOCK_TARGET_DECIMALS := env_var_or_default("FORGE_MOCK_TARGET_DECIMALS", HEX_18) 50 | FORGE_MOCK_UNDERLYING_DECIMALS := env_var_or_default("FORGE_MOCK_UNDERLYING_DECIMALS", HEX_18) 51 | 52 | # Alchemy API Key is public 53 | # Mnemonic is hardhat's default 54 | 55 | ALCHEMY_KEY := env_var_or_default("ALCHEMY_KEY", "vI8OBZj4Wue9yNPSDVa7Klqt-UeRywrx") 56 | MAINNET_RPC := "https://eth-mainnet.alchemyapi.io/v2/" + ALCHEMY_KEY 57 | MNEMONIC := env_var_or_default("MNEMONIC", "test test test test test test test test test test test junk") 58 | 59 | # export just vars as env vars 60 | 61 | set export := true 62 | 63 | # Contract Size 64 | size: 65 | forge build --sizes --force 66 | 67 | # [DEPLOY]: Environemtn Config 68 | 69 | DEPLOYED_ADDRESS := "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" 70 | CONTRACT_NAME := '' 71 | ETHERSCAN_API_KEY := '' 72 | 73 | # [DEPLOY]: Deploy contract 74 | deploy-contract: 75 | forge create $(contract) \ 76 | --constructor-args $(constructorArgs) \ 77 | --rpc-url $(url) \ 78 | --private-key $(privateKey) 79 | 80 | # [DEPLOY]: Verify contract 81 | verify-contract: 82 | forge verify-contract \ 83 | --chain-id $(chainId) \ 84 | --constructor-args `cast abi-encode "$(constructorSig)" $(constructorArgs)` \ 85 | --compiler-version $(compilerVersion) \ 86 | --num-of-optimizations 200 \ 87 | {{ DEPLOYED_ADDRESS }} \ 88 | {{ CONTRACT_NAME }} \ 89 | {{ ETHERSCAN_API_KEY }} 90 | 91 | # [BUILD]: Timer 92 | build: && _timer 93 | cd {{ invocation_directory() }}; forge build --sizes --names --force 94 | 95 | # [TEST] mainnet test 96 | build-mainnet: && _timer 97 | cd {{ invocation_directory() }}; forge test --match-path "*.t.sol" --fork-url {{ MAINNET_RPC }} 98 | 99 | # [TEST] default test scripts 100 | test: test-local 101 | 102 | # [TEST] run local forge test using --match-path 103 | test-local *commands="": && _timer 104 | cd {{ invocation_directory() }}; forge test --match-path "*.t.sol" {{ commands }} 105 | 106 | # [TEST] run mainnet fork forge tests (all files with the extension .t.sol) 107 | test-mainnet *commands="": && _timer 108 | cd {{ invocation_directory() }}; forge test --rpc-url {{ MAINNET_RPC }} --match-path "*.t.sol" {{ commands }} 109 | 110 | # [TEST] run mainnet fork forge debug tests (all files with the extension .t.sol) 111 | test-debug *commands="": && _timer 112 | cd {{ invocation_directory() }}; forge test --rpc-url {{ MAINNET_RPC }} --match-path "*.t.sol" {{ commands }} 113 | 114 | gas-cov: 115 | forge test --gas-report 116 | 117 | # [GAS] default gas snapshot script 118 | gas-snapshot: gas-snapshot-local 119 | 120 | # [GAS] get gas snapshot from local tests and save it to file 121 | gas-snapshot-local: 122 | cd {{ invocation_directory() }}; \ 123 | just test-local | grep 'gas:' | cut -d " " -f 2-4 | sort > \ 124 | {{ justfile_directory() }}/gas-snapshots/.$( \ 125 | cat {{ invocation_directory() }}/package.json | jq .name | tr -d '"' | cut -d"/" -f2- \ 126 | ) 127 | 128 | # [GAS] get gas snapshot timer 129 | forge-gas-snapshot: && _timer 130 | @cd {{ invocation_directory() }}; forge snapshot --no-match-path ".*.*" 131 | 132 | forge-gas-snapshot-diff: && _timer 133 | @cd {{ invocation_directory() }}; forge snapshot --no-match-path ".*.*" --diff 134 | 135 | # Solidity test ffi callback to get Target decimals for the base Mock Target token 136 | _forge_mock_target_decimals: 137 | @printf {{ FORGE_MOCK_TARGET_DECIMALS }} 138 | 139 | _forge_mock_underlying_decimals: 140 | @printf {{ FORGE_MOCK_UNDERLYING_DECIMALS }} 141 | 142 | # [UTILS] utility functions 143 | 144 | start_time := `date +%s` 145 | 146 | _timer: 147 | @echo "[TASK]: Executed in $(($(date +%s) - {{ start_time }})) seconds" 148 | 149 | # mode: makefile 150 | # End: 151 | # vim: set ft=make : 152 | # vi: set ts=4 sw=4 noet : 153 | -------------------------------------------------------------------------------- /scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | if [[ ${DEBUG} ]]; then 6 | set -x 7 | fi 8 | 9 | # All contracts are output to `out/addresses.json` by default 10 | OUT_DIR=${OUT_DIR:-$PWD/out} 11 | ADDRESSES_FILE=${ADDRESSES_FILE:-$OUT_DIR/"addresses.json"} 12 | # default to localhost rpc 13 | ETH_RPC_URL=${ETH_RPC_URL:-http://localhost:8545} 14 | 15 | # green log helper 16 | GREEN='\033[0;32m' 17 | NC='\033[0m' # No Color 18 | log() { 19 | printf '%b\n' "${GREEN}${*}${NC}" 20 | echo "" 21 | } 22 | 23 | # Coloured output helpers 24 | if command -v tput >/dev/null 2>&1; then 25 | if [ $(($(tput colors 2>/dev/null))) -ge 8 ]; then 26 | # Enable colors 27 | TPUT_RESET="$(tput sgr 0)" 28 | TPUT_YELLOW="$(tput setaf 3)" 29 | TPUT_RED="$(tput setaf 1)" 30 | TPUT_BLUE="$(tput setaf 4)" 31 | TPUT_GREEN="$(tput setaf 2)" 32 | TPUT_WHITE="$(tput setaf 7)" 33 | TPUT_BOLD="$(tput bold)" 34 | fi 35 | fi 36 | 37 | # ensure ETH_FROM is set and give a meaningful error message 38 | if [[ -z ${ETH_FROM} ]]; then 39 | echo "ETH_FROM not found, please set it and re-run the last command." 40 | exit 1 41 | fi 42 | 43 | # Make sure address is checksummed 44 | if [ "$ETH_FROM" != "$(cast --to-checksum-address "$ETH_FROM")" ]; then 45 | echo "ETH_FROM not checksummed, please format it with 'cast --to-checksum-address
'" 46 | exit 1 47 | fi 48 | 49 | # Setup addresses file 50 | cat >"$ADDRESSES_FILE" < deploy ContractName arg1 arg2 arg3` 57 | # (or omit the env vars if you have already set them) 58 | deploy() { 59 | NAME=$1 60 | ARGS=${@:2} 61 | 62 | # find file path 63 | CONTRACT_PATH=$(find ./src -name $NAME.sol) 64 | CONTRACT_PATH=${CONTRACT_PATH:2} 65 | 66 | # select the filename and the contract in it 67 | PATTERN=".contracts[\"$CONTRACT_PATH\"].$NAME" 68 | 69 | # get the constructor's signature 70 | ABI=$(jq -r "$PATTERN.abi" out/dapp.sol.json) 71 | SIG=$(echo "$ABI" | seth --abi-constructor) 72 | 73 | # get the bytecode from the compiled file 74 | BYTECODE=0x$(jq -r "$PATTERN.evm.bytecode.object" out/dapp.sol.json) 75 | 76 | # estimate gas 77 | GAS=$(seth estimate --create "$BYTECODE" "$SIG" $ARGS --rpc-url "$ETH_RPC_URL") 78 | 79 | # deploy 80 | ADDRESS=$(dapp create "$NAME" $ARGS -- --gas "$GAS" --rpc-url "$ETH_RPC_URL") 81 | 82 | # save the addrs to the json 83 | # TODO: It'd be nice if we could evolve this into a minimal versioning system 84 | # e.g. via commit / chainid etc. 85 | saveContract "$NAME" "$ADDRESS" 86 | 87 | echo "$ADDRESS" 88 | } 89 | 90 | # Call as `saveContract ContractName 0xYourAddress` to store the contract name 91 | # & address to the addresses json file 92 | saveContract() { 93 | # create an empty json if it does not exist 94 | if [[ ! -e $ADDRESSES_FILE ]]; then 95 | echo "{}" >"$ADDRESSES_FILE" 96 | fi 97 | result=$(cat "$ADDRESSES_FILE" | jq -r ". + {\"$1\": \"$2\"}") 98 | printf %s "$result" >"$ADDRESSES_FILE" 99 | } 100 | 101 | estimate_gas() { 102 | NAME=$1 103 | ARGS=${@:2} 104 | # select the filename and the contract in it 105 | PATTERN=".contracts[\"src/$NAME.sol\"].$NAME" 106 | 107 | # get the constructor's signature 108 | ABI=$(jq -r "$PATTERN.abi" out/dapp.sol.json) 109 | SIG=$(echo "$ABI" | seth --abi-constructor) 110 | 111 | # get the bytecode from the compiled file 112 | BYTECODE=0x$(jq -r "$PATTERN.evm.bytecode.object" out/dapp.sol.json) 113 | # estimate gas 114 | GAS=$(seth estimate --create "$BYTECODE" "$SIG" $ARGS --rpc-url "$ETH_RPC_URL") 115 | 116 | TXPRICE_RESPONSE=$(curl -sL https://api.txprice.com/v1) 117 | response=$(jq '.code' <<<"$TXPRICE_RESPONSE") 118 | if [[ $response != "200" ]]; then 119 | echo "Could not get gas information from ${TPUT_BOLD}txprice.com${TPUT_RESET}: https://api.txprice.com/v1" 120 | echo "response code: $response" 121 | else 122 | rapid=$(($(jq '.blockPrices[0].estimatedPrices[0].maxFeePerGas' <<<"$TXPRICE_RESPONSE"))) 123 | fast=$(($(jq '.blockPrices[0].estimatedPrices[1].maxFeePerGas' <<<"$TXPRICE_RESPONSE"))) 124 | standard=$(($(jq '.blockPrices[0].estimatedPrices[2].maxFeePerGas' <<<"$TXPRICE_RESPONSE"))) 125 | slow=$(($(jq '.blockPrices[0].estimatedPrices[3].maxFeePerGas' <<<"$TXPRICE_RESPONSE"))) 126 | basefee$(($(jq '.blockPrices[0].baseFeePerGas' <<<"$TXPRICE_RESPONSE"))) 127 | echo "Gas prices from ${TPUT_BOLD}txprice.com${TPUT_RESET}: https://api.txprice.com/v1" 128 | echo " \ 129 | ${TPUT_RED}Rapid: $rapid gwei ${TPUT_RESET} \n 130 | ${TPUT_YELLOW}Fast: $fast gwei \n 131 | ${TPUT_BLUE}Standard: $standard gwei \n 132 | ${TPUT_GREEN}Slow: $slow gwei${TPUT_RESET}" | column -t 133 | size=$(contract_size "$NAME") 134 | echo "Estimated Gas cost for deployment of $NAME: ${TPUT_BOLD}$GAS${TPUT_RESET} units of gas" 135 | echo "Contract Size: ${size} bytes" 136 | echo "Total cost for deployment:" 137 | rapid_cost=$(echo "scale=5; $GAS*$rapid" | bc) 138 | fast_cost=$(echo "scale=5; $GAS*$fast" | bc) 139 | standard_cost=$(echo "scale=5; $GAS*$standard" | bc) 140 | slow_cost=$(echo "scale=5; $GAS*$slow" | bc) 141 | echo " \ 142 | ${TPUT_RED}Rapid: $rapid_cost ETH ${TPUT_RESET} \n 143 | ${TPUT_YELLOW}Fast: $fast_cost ETH \n 144 | ${TPUT_BLUE}Standard: $standard_cost ETH \n 145 | ${TPUT_GREEN}Slow: $slow_cost ETH ${TPUT_RESET}" | column -t 146 | fi 147 | } 148 | 149 | contract_size() { 150 | NAME=$1 151 | ARGS=${@:2} 152 | # select the filename and the contract in it 153 | PATTERN=".contracts[\"src/$NAME.sol\"].$NAME" 154 | 155 | # get the bytecode from the compiled file 156 | BYTECODE=0x$(jq -r "$PATTERN.evm.bytecode.object" out/dapp.sol.json) 157 | length=$(echo "$BYTECODE" | wc -m) 158 | echo $(($length / 2)) 159 | } 160 | -------------------------------------------------------------------------------- /scripts/dapptools/verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ### dapp-verify-contract -- verify contract souce on etherscan 3 | ### Usage: dapp verify-contract :
[constructorArgs] 4 | 5 | set -e 6 | 7 | [[ $ETHERSCAN_API_KEY ]] || { 8 | cat >&2 <<. 9 | You need an Etherscan Api Key to verify contracts. 10 | Create one at https://etherscan.io/myapikey 11 | Then export it with \`export ETHERSCAN_API_KEY=xxxxxxxx' 12 | . 13 | exit 1 14 | } 15 | 16 | [[ $1 ]] || (dapp verify-contract --help >&2 && exit 1) 17 | [[ $2 ]] || (dapp verify-contract --help >&2 && exit 1) 18 | 19 | chain=$(seth chain) 20 | case "$chain" in 21 | ethlive | mainnet) 22 | export ETHERSCAN_API_URL=https://api.etherscan.io/api 23 | export ETHERSCAN_URL=https://etherscan.io/address 24 | ;; 25 | ropsten | kovan | rinkeby | goerli) 26 | export ETHERSCAN_API_URL=https://api-$chain.etherscan.io/api 27 | export ETHERSCAN_URL=https://$chain.etherscan.io/address 28 | ;; 29 | optimism-mainnet) 30 | export ETHERSCAN_API_URL=https://api-optimistic.etherscan.io/api 31 | export ETHERSCAN_URL=https://optimistic.etherscan.io/address 32 | ;; 33 | optimism-kovan) 34 | export ETHERSCAN_API_URL=https://api-kovan-optimistic.etherscan.io/api 35 | export ETHERSCAN_URL=https://kovan-optimistic.etherscan.io/address 36 | ;; 37 | polygon) 38 | export ETHERSCAN_API_URL=https://api.polygonscan.com/api 39 | export ETHERSCAN_URL=https://polygonscan.com/address 40 | ;; 41 | polygon-mumbai) 42 | export ETHERSCAN_API_URL=https://api-testnet.polygonscan.com/api 43 | export ETHERSCAN_URL=https://mumbai.polygonscan.com/address 44 | ;; 45 | fantom) 46 | export ETHERSCAN_API_URL=https://api.ftmscan.com/api 47 | export ETHERSCAN_URL=https://ftmscan.com/address 48 | ;; 49 | arbitrum-mainnet) 50 | export ETHERSCAN_API_URL=https://api.arbiscan.io/api 51 | export ETHERSCAN_URL=https://arbiscan.io/address 52 | ;; 53 | arbitrum-rinkeby) 54 | export ETHERSCAN_API_URL=https://api-testnet.arbiscan.io/api 55 | export ETHERSCAN_URL=https://testnet.arbiscan.io/address 56 | ;; 57 | avax-mainnet) 58 | export ETHERSCAN_API_URL=https://api.snowtrace.io/api 59 | export ETHERSCAN_URL=https://snowtrace.io/address 60 | ;; 61 | avax-fuji) 62 | export ETHERSCAN_API_URL=https://api-testnet.snowtrace.io/api 63 | export ETHERSCAN_URL=https://testnet.snowtrace.io/address 64 | ;; 65 | *) 66 | echo >&2 "Verification only works on mainnet, ropsten, kovan, rinkeby, goerli, polygon, fantom, arbitrum and avax." 67 | exit 1 68 | ;; 69 | esac 70 | 71 | path=${1?contractname} 72 | name=${path#*:} 73 | address=${2?contractaddress} 74 | 75 | # combined-json has a sourceList field 76 | if [[ $(jq .sourceList "$DAPP_JSON") == null ]]; then 77 | contract=$(jq <"$DAPP_JSON" -r ".contracts[\"${path/:*/}\"][\"$name\"]") 78 | else 79 | contract=$(jq <"$DAPP_JSON" -r ".contracts[\"$path\"]") 80 | fi 81 | meta=$(jshon <<<"$contract" -e metadata -u) 82 | version=$(jshon <<<"$meta" -e compiler -e version -u) 83 | file=$(jshon <<<"$meta" -e settings -e compilationTarget -k) 84 | optimized=$(jshon <<<"$meta" -e settings -e optimizer -e enabled -u) 85 | runs=$(jshon <<<"$meta" -e settings -e optimizer -e runs -u) 86 | 87 | abi=$(jq '.["abi"]' -r <<<"$contract") 88 | type=$(seth --abi-constructor <<<"$abi") 89 | constructor=${type/constructor/${1#*:}} 90 | 91 | if [[ $3 ]]; then 92 | constructorArguments=$(seth calldata "$constructor" "${@:3}") 93 | constructorArguments=${constructorArguments#0x} 94 | constructorArguments=${constructorArguments:8} 95 | fi 96 | 97 | # Etherscan requires leading 'v' which isn't in the artifacts 98 | version="v${version}" 99 | 100 | # Get the list of supported solc versions and compare 101 | # Etherscan uses the js solc, which is not guaranteed to match the C distribution signature 102 | 103 | version_list=$(curl -fsS "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/list.txt") 104 | # There have been a couple of instances where the solc js release used by 105 | # Etherscan does not match the tag of the C distributions. 106 | if [[ $version_list != *"$version"* ]]; then 107 | regex="(.+commit+.)" 108 | # Version incompatible with js release 109 | echo "Compiler version $version is not compatible with etherscan" 110 | if [[ $version =~ $regex ]]; then 111 | version_proto=${BASH_REMATCH[1]} 112 | version=$(echo "$version_list" | grep -o "${version_proto}\{8\}") 113 | echo "Attempting ${version}" 114 | fi 115 | fi 116 | 117 | if [[ "$optimized" = "true" ]]; then 118 | optimized=1 119 | else 120 | optimized=0 121 | fi 122 | 123 | params=( 124 | "module=contract" "action=verifysourcecode" 125 | "contractname=$name" "contractaddress=$address" 126 | "optimizationUsed=$optimized" "runs=$runs" 127 | "apikey=$ETHERSCAN_API_KEY" 128 | ) 129 | 130 | source=$(hevm flatten --source-file "$file" --json-file "$DAPP_JSON" --dapp-root "$DAPP_ROOT") 131 | 132 | source=$( 133 | cat <<. 134 | // Verified using https://dapp.tools 135 | $source 136 | . 137 | ) 138 | 139 | query=$(printf "&%s" "${params[@]}") 140 | 141 | count=0 142 | while [ $count -lt 5 ]; do 143 | sleep 10 144 | 145 | response=$(curl -fsS "$ETHERSCAN_API_URL" -d "$query" \ 146 | --data-urlencode "compilerversion=$version" \ 147 | --data-urlencode "sourceCode@"<(echo "$source") \ 148 | --data-urlencode "constructorArguements=$constructorArguments" -X POST) 149 | # NOTE: the Arguements typo is in etherscan's API 150 | 151 | status=$(jshon <<<"$response" -e status -u) 152 | guid=$(jshon <<<"$response" -e result -u) 153 | message=$(jshon <<<"$response" -e message -u) 154 | count=$((count + 1)) 155 | 156 | [[ $status = 1 ]] && break 157 | done 158 | 159 | [[ $status = 0 && $message = "Contract source code already verified" ]] && { 160 | echo >&2 "Contract source code already verified." 161 | echo >&2 "Go to $ETHERSCAN_URL/$2#code" 162 | exit 0 163 | } 164 | 165 | [[ $status = 0 ]] && { 166 | echo >&2 "There was an error verifying this contract." 167 | echo >&2 "Response: $message" 168 | echo >&2 "Details: $guid" 169 | exit 1 170 | } 171 | 172 | [[ $DAPP_ASYNC == yes ]] && exit 173 | 174 | sleep 20 175 | response=$(curl -fsS "$ETHERSCAN_API_URL" \ 176 | -d "module=contract&action=checkverifystatus&guid=$guid&apikey=$ETHERSCAN_API_KEY") 177 | 178 | status=$(jshon <<<"$response" -e status -u) 179 | result=$(jshon <<<"$response" -e result -u) 180 | 181 | [[ $status = 1 ]] && { 182 | echo >&2 "$result" 183 | echo >&2 "Go to $ETHERSCAN_URL/$2#code" 184 | exit 0 185 | } 186 | 187 | [[ $status = 0 ]] && { 188 | echo >&2 "Failure" 189 | echo >&2 "$result" 190 | exit 1 191 | } 192 | --------------------------------------------------------------------------------