diff --git a/.github/template/create-pull-request.md b/.github/template/create-pull-request.md new file mode 100644 index 0000000..c992ec6 --- /dev/null +++ b/.github/template/create-pull-request.md @@ -0,0 +1,2 @@ +### Description +PR to merge from [{{ .fromBranch }}](/axieinfinity/foundry-deployment-kit/tree/{{ .fromBranch }}) to [{{ .toBranch }}](/axieinfinity/foundry-deployment-kit/tree/{{ .toBranch }}). diff --git a/.github/workflows/create-PR-implement.yml b/.github/workflows/create-PR-implement.yml new file mode 100644 index 0000000..33d00a5 --- /dev/null +++ b/.github/workflows/create-PR-implement.yml @@ -0,0 +1,50 @@ +name: Create Pull Request From Implement To Feature +on: + push: + branches: + - 'implement-feature/**' + - 'implement-feature/**/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + createPullRequest: + runs-on: ubuntu-latest + steps: + - name: Set env + run: | + echo "FEATURE_NAME=$(echo ${HEAD_BRANCH} | cut -d'/' -f2)" >> $GITHUB_ENV + echo "FEATURE_BRANCH=feature/$(echo ${HEAD_BRANCH} | cut -d'/' -f2)" >> $GITHUB_ENV + echo "IMPLEMENT_NAME=$(echo ${HEAD_BRANCH} | cut -d'/' -f3)" >> $GITHUB_ENV + + - uses: actions/checkout@v3 + with: + ref: ${{env.FEATURE_BRANCH}} + + - name: Reset promotion branch + run: | + git fetch origin ${HEAD_BRANCH}:${HEAD_BRANCH} + git reset --hard ${HEAD_BRANCH} + + - name: Render template + id: template + uses: chuhlomin/render-template@v1.4 + with: + template: .github/template/create-pull-request.md + vars: | + fromBranch: ${{env.HEAD_BRANCH}} + toBranch: ${{ env.FEATURE_BRANCH }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + branch: ${{ env.HEAD_BRANCH }} + base: ${{env.FEATURE_BRANCH}} + labels: automated PR + title: 'feat(${{env.FEATURE_NAME}}): implement `${{env.IMPLEMENT_NAME}}`' + body: ${{ steps.template.outputs.result }} \ No newline at end of file diff --git a/.github/workflows/create-PR-release.yml b/.github/workflows/create-PR-release.yml new file mode 100644 index 0000000..87944c6 --- /dev/null +++ b/.github/workflows/create-PR-release.yml @@ -0,0 +1,84 @@ +name: Create Pull Request From Release to Feature +on: + push: + branches: + - 'release/*' + - 'release*/*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + fetchAllFeatureBranches: + runs-on: ubuntu-latest + + steps: + - id: step1 + name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - id: step2 + name: List all the remote feature branches + run: | + branches=$(git branch -r | grep -E '.*origin\/feature\/.*' | sed -e "s/.*origin\///" | tr "\n" " ") + JSON="[" + for branch in ${branches[@]}; do + echo $branch + JSONline="\"$branch\"," + # we don't need to iterate on the same branch over and over, so + # onnly include it when it wasn't included + if [[ "$JSON" != *"$JSONline"* ]]; then + JSON="$JSON$JSONline" + fi + done + # Remove last "," and add the closing bracket + if [[ $JSON == *, ]]; then + JSON="${JSON%?}" + fi + JSON="$JSON]" + echo $JSON + echo "BRANCHES={\"branch_name\": $( echo "$JSON" )}" >> "$GITHUB_OUTPUT" + outputs: + BRANCHES: ${{ steps.step2.outputs.BRANCHES }} + + mergeRelease2FeatureRepo: + runs-on: ubuntu-latest + needs: fetchAllFeatureBranches + strategy: + matrix: ${{ fromJSON(needs.fetchAllFeatureBranches.outputs.BRANCHES) }} + steps: + - name: Set env + run: | + echo "PR_BRANCH=merge/${HEAD_BRANCH}-${{matrix.branch_name}}" >> $GITHUB_ENV + echo "FEATURE_NAME=$(echo ${{matrix.branch_name}} | cut -d'/' -f2)" >> $GITHUB_ENV + - uses: actions/checkout@v3 + with: + ref: ${{matrix.branch_name}} + - name: Reset promotion branch + run: | + git fetch origin ${HEAD_BRANCH}:${HEAD_BRANCH} + git reset --hard ${HEAD_BRANCH} + + - name: Render template + id: template + uses: chuhlomin/render-template@v1.4 + with: + template: .github/template/create-pull-request.md + vars: | + fromBranch: ${{env.HEAD_BRANCH}} + toBranch: ${{matrix.branch_name}} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + labels: automated PR + delete-branch: true + title: 'chore(`${{env.FEATURE_NAME}}`): merge from `${{env.HEAD_BRANCH}}`' + body: ${{ steps.template.outputs.result }} + branch: ${{env.PR_BRANCH}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..1e8d435 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,46 @@ +name: test + +on: + push: + branches: + - mainnet + - testnet + - 'feature/*' + - 'features/*' + pull_request: + branches: + - mainnet + - testnet + - 'feature/*' + - 'features/*' + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/.gitignore b/.gitignore index 0b2b5b7..89bcfbd 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ out/ docs/ # Dotenv file -.env +*.env .vscode node_modules/ diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..c9cdc63 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/.husky/generate-layout.sh b/.husky/generate-layout.sh new file mode 100644 index 0000000..8fc74d4 --- /dev/null +++ b/.husky/generate-layout.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +rm -rf logs/storage/* +dirOutputs=$(ls out | grep '^[^.]*\.sol$') # assuming the out dir is at 'out' +while IFS= read -r contractDir; do + innerdirOutputs=$(ls out/$contractDir) + + while IFS= read -r jsonFile; do + fileIn=out/$contractDir/$jsonFile + fileOut=logs/storage/$contractDir:${jsonFile%.json}.log + node .husky/storage-logger.js $fileIn $fileOut & + done <<< "$innerdirOutputs" +done <<< "$dirOutputs" + +# Wait for all background jobs to finish +wait \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100755 new mode 100644 diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 0000000..6397927 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,26 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +set -ex + +# Workaround: git stash no changes doesn't cause failure but git stash pop cause +output=$(git stash) +stashed=true +if [[ $output == *"No local changes to save"* ]]; then + stashed=false +fi + +forge build --sizes 2>&1 | sed -n '/Contract/,$p' > logs/contract-code-sizes.log +.husky/generate-layout.sh + +git add logs + +output=$(git status -s) +line_count=$(echo "$output" | wc -l) +if [ "$line_count" -gt 1 ]; then + git commit -m "chore: storage layout" +fi + +if $stashed; then + git stash pop +fi \ No newline at end of file diff --git a/.husky/storage-logger.js b/.husky/storage-logger.js new file mode 100644 index 0000000..ec74913 --- /dev/null +++ b/.husky/storage-logger.js @@ -0,0 +1,46 @@ +const fs = require('fs'); +const fileIn = process.argv[2]; +const fileOut = process.argv[3]; + +if (!fileIn) { + console.error('Invalid input'); +} + +fs.readFile(fileIn, 'utf8', (err, data) => { + if (err) { + console.error('Error reading file:', err); + return; + } + + try { + const jsonData = JSON.parse(data); + if (typeof jsonData.storageLayout == 'undefined') { + return; + } + + if (jsonData.storageLayout.storage.length == 0) { + return; + } + + const outputData = jsonData.storageLayout.storage + .map(({ contract, label, offset, slot, type: typeId }) => { + const typeObj = jsonData.storageLayout.types[typeId]; + const typeLabel = typeObj.label; + const numberOfBytes = typeObj.numberOfBytes; + return `${contract}:${label} (storage_slot: ${slot}) (offset: ${offset}) (type: ${typeLabel}) (numberOfBytes: ${numberOfBytes})`; + }) + .join('\n'); + if (!fileOut) { + console.log(outputData); + } else { + fs.writeFile(fileOut, outputData, 'utf-8', err => { + if (err) { + console.error('Error writing file:', err); + return; + } + }); + } + } catch (err) { + console.error('Error parsing JSON:', err); + } +}); \ No newline at end of file diff --git a/README.md b/README.md index f375f8a..1acfa04 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ # foundry-deployment-kit + +The collections of smart contracts that support writing deployment scripts. + +## Development + +### Requirement + +- [Foundry forge@^0.2.0](https://book.getfoundry.sh/) + +### Build & Test + +- Install packages + +```shell +$ forge install +``` + +- Build contracts + +```shell +$ forge build +``` + +- Run test + +```shell +$ forge test +``` + +### Deploy + +```shell +$ forge script -f --private-key +``` \ No newline at end of file diff --git a/lib/forge-std b/lib/forge-std index 1d9650e..2f11269 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 1d9650e951204a0ddce9ff89c32f1997984cef4d +Subproject commit 2f112697506eab12d433a65fdc31a639548fe365 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 932fddf..fd81a96 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 932fddf69a699a9a80fd2396fd1a2ab91cdda123 +Subproject commit fd81a96f01cc42ef1c9a5399364968d0e07e9e90 diff --git a/lib/solady b/lib/solady index 2ba1cc1..acab902 160000 --- a/lib/solady +++ b/lib/solady @@ -1 +1 @@ -Subproject commit 2ba1cc1eaa3bffd5c093d94f76ef1b87b167ff3c +Subproject commit acab9022c6472a578134d3dd7e7b38efa61df871 diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/logs/storage/.gitkeep b/logs/storage/.gitkeep new file mode 100644 index 0000000..e69de29