├── __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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | tldr
12 |
13 | [](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 |
--------------------------------------------------------------------------------