From f3b47f1fb3924bfdca57cedb7b60085bbc07cb37 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 11:32:42 +0200 Subject: [PATCH 01/15] chore: Update gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c4772434..c0d9e792 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,8 @@ cache artifacts contracts/hardhat-dependency-compiler/ -deployments/*hardhat* +deployments/hardhat +deployments/external-hardhat deployments/dev-* # Flattened Solidity file used to generate diagrams. From 6dadbe83794a7f45fb7c329fd8106648c8ad1926 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 11:43:21 +0200 Subject: [PATCH 02/15] feat: Remove dangling artifact deployments after upgrade --- scripts/upgrades/v6.1.0-bulk-processing.ts | 2 ++ utils/proxy-tools.ts | 26 ++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/upgrades/v6.1.0-bulk-processing.ts b/scripts/upgrades/v6.1.0-bulk-processing.ts index 188aca09..883b2df0 100644 --- a/scripts/upgrades/v6.1.0-bulk-processing.ts +++ b/scripts/upgrades/v6.1.0-bulk-processing.ts @@ -9,6 +9,7 @@ import { getUpgradeContext, linkFacetsToDiamond, printOnchainProxyFunctions, + removeDanglingFacetDeploymentArtifacts, removeFacetsFromDiamond, removeFunctionsFromDiamond, } from '../../utils/proxy-tools'; @@ -103,6 +104,7 @@ async function main() { await linkFacetsToDiamond(proxyAddress, proxyOwner, facetsToAdd); await printOnchainProxyFunctions(proxyAddress); console.log('Upgrade performed successfully!'); + await removeDanglingFacetDeploymentArtifacts(proxyAddress); await tryVerify(facetsToAdd.map((facet) => facet.name)); } diff --git a/utils/proxy-tools.ts b/utils/proxy-tools.ts index 2ac253f5..f12fae65 100644 --- a/utils/proxy-tools.ts +++ b/utils/proxy-tools.ts @@ -3,7 +3,7 @@ import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'; import { ContractFactory, FunctionFragment, Interface, ZeroAddress } from 'ethers'; -import { ethers } from 'hardhat'; +import { deployments, ethers } from 'hardhat'; import { FacetCut, FacetCutAction } from 'hardhat-deploy/dist/types'; import type { IDiamond } from '../typechain'; import { @@ -279,7 +279,7 @@ export async function removeFacetsFromDiamond( throw new Error(`Facet ${facet.name} is empty or does not exist on-chain`); } console.log( - `Will remove the whole facet ${facet.name} [address: ${facet.address}, functions:${selectors.length}]`, + `Will remove facet [name:${facet.name}, address: ${facet.address}, functions:${selectors.length}]`, ); facetCuts.push({ facetAddress: ZeroAddress, @@ -350,3 +350,25 @@ export async function removeFunctionsFromDiamond( await tx.wait(); console.log('Functions removed successfully!'); } + +/** + * Removes dangling deployment artifacts for facets that are no longer linked to the diamond proxy. + * This is not done automatically in `removeFacetsFromDiamond` because we deploy new facets first, + * then remove old facets, which sometimes overwrites the existing ones. + * @param proxyAddress address of the diamond proxy + */ +export async function removeDanglingFacetDeploymentArtifacts(proxyAddress: string) { + console.log('\n=== Removing dangling deployment artifacts ==='); + const allDeployments = await deployments.all(); + const diamondLoupe = DiamondLoupeFacet__factory.connect(proxyAddress, ethers.provider); + const onchainFacets = await diamondLoupe.facetAddresses(); + for (const deploymentName of Object.keys(allDeployments)) { + const deploymentAddress = allDeployments[deploymentName].address; + if (deploymentName.endsWith('Facet') && !onchainFacets.includes(deploymentAddress)) { + console.log( + `Deleting dangling facet artifact [name:${deploymentName}, address:${deploymentAddress}]`, + ); + await deployments.delete(deploymentName); + } + } +} From 5f167858d108e85e38b7b10228cf109cdcfc543a Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 12:05:06 +0200 Subject: [PATCH 03/15] chore: Rename input variable --- .github/workflows/upgrade-facets.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index c9967d91..178df008 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -14,7 +14,7 @@ on: - arbitrumSepolia - arbitrum default: 'arbitrumSepolia' - dry_run: + dry-run: description: 'Dry Run (fork test only, no actual deployment)' required: false type: boolean @@ -54,7 +54,7 @@ jobs: run: npm run build - name: Run fork test (dry run) - if: inputs.dry_run == true + if: inputs.dry-run == true env: # Note: it is required to define both private key env variables when calling Hardhat. DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }} @@ -69,7 +69,7 @@ jobs: npx hardhat run scripts/upgrades/${{ env.UPGRADE_SCRIPT }} --network hardhat - name: Execute upgrade on live network - if: inputs.dry_run == false + if: inputs.dry-run == false env: # Note: it is required to define both private key env variables when calling Hardhat. DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }} @@ -80,7 +80,7 @@ jobs: npx hardhat run scripts/upgrades/${{ env.UPGRADE_SCRIPT }} --network ${{ inputs.network }} - name: Push artifacts to the current branch - if: inputs.dry_run == false && github.ref != 'refs/heads/main' + if: inputs.dry-run == false && github.ref != 'refs/heads/main' uses: stefanzweifel/git-auto-commit-action@v5 with: file_pattern: | @@ -92,7 +92,7 @@ jobs: # Since the `main` branch is protected, create a PR to push artifacts. - name: Push artifacts through a pull request - if: inputs.dry_run == false && github.ref == 'refs/heads/main' + if: inputs.dry-run == false && github.ref == 'refs/heads/main' uses: peter-evans/create-pull-request@v7 with: add-paths: | From 16d538c7b3a126975e7caf218f7ee1394c0d58d2 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 12:37:33 +0200 Subject: [PATCH 04/15] chore: Add dry-run script --- .github/workflows/upgrade-facets.yml | 2 +- scripts/upgrades/dry-run.sh | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 scripts/upgrades/dry-run.sh diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index 178df008..bcc56e7b 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -66,7 +66,7 @@ jobs: elif [ "${{ inputs.network }}" == "arbitrum" ]; then export ARBITRUM_FORK=true fi - npx hardhat run scripts/upgrades/${{ env.UPGRADE_SCRIPT }} --network hardhat + UPGRADE_SCRIPT=${{ env.UPGRADE_SCRIPT }} bash ./scripts/upgrades/dry-run.sh - name: Execute upgrade on live network if: inputs.dry-run == false diff --git a/scripts/upgrades/dry-run.sh b/scripts/upgrades/dry-run.sh new file mode 100644 index 00000000..164a936c --- /dev/null +++ b/scripts/upgrades/dry-run.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +# Usage: +# ARBITRUM_SEPOLIA_FORK=true UPGRADE_SCRIPT= bash ./scripts/upgrades/dry-run.sh +# ARBITRUM_FORK=true UPGRADE_SCRIPT= bash ./scripts/upgrades/dry-run.sh + + +DEPLOYMENTS_FOLDER='' +if [ "${ARBITRUM_SEPOLIA_FORK}" == "true" ]; then + DEPLOYMENTS_FOLDER=arbitrumSepolia +elif [ "${ARBITRUM_FORK}" == "true" ]; then + DEPLOYMENTS_FOLDER=arbitrum +fi + +rm -rf deployments/hardhat +cp -r deployments/${DEPLOYMENTS_FOLDER} deployments/hardhat +cp .gitignore .gitignore.bak +sed -i '/deployments\/hardhat/d' .gitignore +git add deployments/hardhat +npx hardhat run scripts/upgrades/${UPGRADE_SCRIPT} --network hardhat +# Print the changes made during the dry run +git --no-pager diff --name-status +mv .gitignore.bak .gitignore From 3d38bf82fb7518eef5487e030742c061872e6f59 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 14:49:56 +0200 Subject: [PATCH 05/15] chore: Comment script --- scripts/upgrades/dry-run.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/upgrades/dry-run.sh b/scripts/upgrades/dry-run.sh index 164a936c..2098cb2a 100644 --- a/scripts/upgrades/dry-run.sh +++ b/scripts/upgrades/dry-run.sh @@ -2,8 +2,9 @@ set -e # Usage: -# ARBITRUM_SEPOLIA_FORK=true UPGRADE_SCRIPT= bash ./scripts/upgrades/dry-run.sh -# ARBITRUM_FORK=true UPGRADE_SCRIPT= bash ./scripts/upgrades/dry-run.sh +# ARBITRUM_SEPOLIA_FORK=true \ +# UPGRADE_SCRIPT= \ +# bash ./scripts/upgrades/dry-run.sh DEPLOYMENTS_FOLDER='' @@ -12,13 +13,14 @@ if [ "${ARBITRUM_SEPOLIA_FORK}" == "true" ]; then elif [ "${ARBITRUM_FORK}" == "true" ]; then DEPLOYMENTS_FOLDER=arbitrum fi - +# Copy the forked network deployments to the hardhat network folder. rm -rf deployments/hardhat cp -r deployments/${DEPLOYMENTS_FOLDER} deployments/hardhat +# Stage the old deployments to have a clean diff after the upgrade script run. cp .gitignore .gitignore.bak sed -i '/deployments\/hardhat/d' .gitignore git add deployments/hardhat +# Run the upgrade and print the git diff. npx hardhat run scripts/upgrades/${UPGRADE_SCRIPT} --network hardhat -# Print the changes made during the dry run git --no-pager diff --name-status mv .gitignore.bak .gitignore From 40aaee4bfb504f7b65b931085b258092b7cac6b5 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 14:52:29 +0200 Subject: [PATCH 06/15] chore: Don't run pre-upgrade stage on dry-run --- .github/workflows/upgrade-facets.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index bcc56e7b..6a0577c3 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -22,18 +22,18 @@ on: jobs: pre-upgrade: + if: inputs.dry-run == false uses: ./.github/workflows/main.yml upgrade: needs: pre-upgrade runs-on: ubuntu-latest - environment: ${{ inputs.network }} # Use the selected environment + environment: ${{ inputs.network }} permissions: contents: write # Required to commit artifacts. pull-requests: write # Required to create pull requests. env: UPGRADE_SCRIPT: 'v6.1.0-bulk-processing.ts' # Update this for each specific upgrade. - # For commit action COMMIT_MESSAGE: 'chore: Save upgrade artifacts - ${{ inputs.network }} (runId:${{ github.run_id }})' GHA_BOT_NAME: 'GitHub Actions Bot' GHA_BOT_EMAIL: 'github-actions[bot]@users.noreply.github.com' From 6a99359b93bd0f3e91e827ad1859cbc287205e5c Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:03:27 +0200 Subject: [PATCH 07/15] docs: Update upgrade guide --- scripts/upgrades/README.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/scripts/upgrades/README.md b/scripts/upgrades/README.md index 8ca2fd71..fe786ee7 100644 --- a/scripts/upgrades/README.md +++ b/scripts/upgrades/README.md @@ -1,7 +1,7 @@ # PoCo Smart Contracts Upgrade Guide -This document explains the recommended steps for creating and applying -a safe, traceable, and reproducible upgrade to the PoCo Diamond proxy. +This document explains the recommended steps for creating and applying a safe, traceable, +and reproducible upgrade to the PoCo Diamond proxy. ## Upgrade Steps @@ -9,31 +9,33 @@ a safe, traceable, and reproducible upgrade to the PoCo Diamond proxy. Run the full test suite to make sure everything is working before starting an upgrade. 1. **Create a new upgrade script**:
- Name the script using the version and upgrade name format: `vX.Y.Z-upgrade-name.ts`. + Name the script using the version and upgrade name in the form `vX.Y.Z-upgrade-name.ts` + and implement the upgrade logic. 2. **Create a corresponding Markdown report**:
- Copy the template file `v0.0.0-template.md` and rename it to match the script: `vX.Y.Z-upgrade-name.md`. + Copy the template file `v0.0.0-template.md` and rename it to match the script's name + (`vX.Y.Z-upgrade-name.md`). -3. **Update GitHub Actions**:
+3. **Test dry-runs locally**:
+ Use the script [./dry-run.sh](./dry-run.sh) and check the logs. + +4. **Update GitHub Actions**:
Modify `upgrade-facets.yml` workflow to call the new upgrade script. -4. **Upgrade on Testnet**: +5. **Upgrade on Testnet**: - ⚠️ Always upgrade on the testnet first. - Trigger the upgrade workflow on GitHub and choose the testnet network. - Start with a **dry run** to simulate the upgrade. - Once verified, apply the upgrade on the live testnet. -5. **Upgrade on Mainnet**: +6. **Upgrade on Mainnet**: - Trigger the upgrade workflow on GitHub and choose the mainnet network. - Perform a dry run first. - Apply the upgrade on the mainnet. - Merge the artifacts PR after successful execution. -6. **Update upgrade report**:
+7. **Update upgrade report**:
Fill in all required information in `vX.Y.Z-upgrade-name.ts` (tx hashes, logs, ...). -7. **Create a release** +8. **Create a release** - Use **Release Please** to tag the upgrade version and create the release on GitHub. - - -Following these steps ensures upgrades are **safe, traceable, and reproducible**. From 572b8987a25255fea9e9c170eee330afd2023112 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:15:43 +0200 Subject: [PATCH 08/15] chore: Proceed with upgrade if pre-upgrade succeeded or was skipped --- .github/workflows/upgrade-facets.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index 6a0577c3..0d964f2a 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -27,6 +27,8 @@ jobs: upgrade: needs: pre-upgrade + # Proceed if pre-upgrade succeeded or was skipped (in case of dry-run). + if: needs.pre-upgrade.result == 'success' || needs.pre-upgrade.result == 'skipped' runs-on: ubuntu-latest environment: ${{ inputs.network }} permissions: From 9594fc51f105b96f90385fed0816aa204203a994 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:22:06 +0200 Subject: [PATCH 09/15] chore: Revert "chore: Proceed with upgrade if pre-upgrade succeeded or was skipped" This reverts commit 572b8987a25255fea9e9c170eee330afd2023112. --- .github/workflows/upgrade-facets.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index 0d964f2a..6a0577c3 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -27,8 +27,6 @@ jobs: upgrade: needs: pre-upgrade - # Proceed if pre-upgrade succeeded or was skipped (in case of dry-run). - if: needs.pre-upgrade.result == 'success' || needs.pre-upgrade.result == 'skipped' runs-on: ubuntu-latest environment: ${{ inputs.network }} permissions: From b2fcf328fae2a63885643a5d69afc4211acb29e8 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:23:13 +0200 Subject: [PATCH 10/15] chore: Revert if check --- .github/workflows/upgrade-facets.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index 6a0577c3..f174100d 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -22,7 +22,6 @@ on: jobs: pre-upgrade: - if: inputs.dry-run == false uses: ./.github/workflows/main.yml upgrade: From 20db8ebbb5164d38bddbc6095aa5a2c131f78198 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:25:05 +0200 Subject: [PATCH 11/15] ci: Test upgrade ci --- .github/workflows/upgrade-facets.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index f174100d..cf5cbaca 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -22,7 +22,11 @@ on: jobs: pre-upgrade: - uses: ./.github/workflows/main.yml + # uses: ./.github/workflows/main.yml + runs-on: ubuntu-latest + steps: + - name: Test + run: echo "Pre-upgrade checks passed." upgrade: needs: pre-upgrade From ac4be00e0485a45df3362e54936fb0ce4fd781c5 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:29:22 +0200 Subject: [PATCH 12/15] chore: Add log message --- scripts/upgrades/dry-run.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/upgrades/dry-run.sh b/scripts/upgrades/dry-run.sh index 2098cb2a..ed690cfb 100644 --- a/scripts/upgrades/dry-run.sh +++ b/scripts/upgrades/dry-run.sh @@ -22,5 +22,6 @@ sed -i '/deployments\/hardhat/d' .gitignore git add deployments/hardhat # Run the upgrade and print the git diff. npx hardhat run scripts/upgrades/${UPGRADE_SCRIPT} --network hardhat +echo "=== Upgrade diff ===" git --no-pager diff --name-status mv .gitignore.bak .gitignore From d59469387b7bae8d35dc3870f9d03887f6678a9b Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:30:15 +0200 Subject: [PATCH 13/15] chore: Restore default workflow --- .github/workflows/upgrade-facets.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/upgrade-facets.yml b/.github/workflows/upgrade-facets.yml index cf5cbaca..f174100d 100644 --- a/.github/workflows/upgrade-facets.yml +++ b/.github/workflows/upgrade-facets.yml @@ -22,11 +22,7 @@ on: jobs: pre-upgrade: - # uses: ./.github/workflows/main.yml - runs-on: ubuntu-latest - steps: - - name: Test - run: echo "Pre-upgrade checks passed." + uses: ./.github/workflows/main.yml upgrade: needs: pre-upgrade From beb0cd8381455b1db2032c1fb94bae3954b36498 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:33:13 +0200 Subject: [PATCH 14/15] chore: Apply copilot suggestions (MacOS compatibility and script check) --- scripts/upgrades/dry-run.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/upgrades/dry-run.sh b/scripts/upgrades/dry-run.sh index ed690cfb..9c3b5d0e 100644 --- a/scripts/upgrades/dry-run.sh +++ b/scripts/upgrades/dry-run.sh @@ -13,12 +13,16 @@ if [ "${ARBITRUM_SEPOLIA_FORK}" == "true" ]; then elif [ "${ARBITRUM_FORK}" == "true" ]; then DEPLOYMENTS_FOLDER=arbitrum fi +if [ -z "${DEPLOYMENTS_FOLDER}" ]; then + echo "Error: You must set either ARBITRUM_SEPOLIA_FORK=true or ARBITRUM_FORK=true." + exit 1 +fi # Copy the forked network deployments to the hardhat network folder. rm -rf deployments/hardhat cp -r deployments/${DEPLOYMENTS_FOLDER} deployments/hardhat # Stage the old deployments to have a clean diff after the upgrade script run. cp .gitignore .gitignore.bak -sed -i '/deployments\/hardhat/d' .gitignore +sed -i '' '/deployments\/hardhat/d' .gitignore git add deployments/hardhat # Run the upgrade and print the git diff. npx hardhat run scripts/upgrades/${UPGRADE_SCRIPT} --network hardhat From e34e577eed2b6a32ebf08f5945dc6eb173f05b3a Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:04:09 +0200 Subject: [PATCH 15/15] chore: Fix dry run script --- scripts/upgrades/dry-run.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/upgrades/dry-run.sh b/scripts/upgrades/dry-run.sh index 9c3b5d0e..39b2c9a9 100644 --- a/scripts/upgrades/dry-run.sh +++ b/scripts/upgrades/dry-run.sh @@ -22,10 +22,10 @@ rm -rf deployments/hardhat cp -r deployments/${DEPLOYMENTS_FOLDER} deployments/hardhat # Stage the old deployments to have a clean diff after the upgrade script run. cp .gitignore .gitignore.bak -sed -i '' '/deployments\/hardhat/d' .gitignore +sed -i.bak '/deployments\/hardhat/d' .gitignore git add deployments/hardhat # Run the upgrade and print the git diff. npx hardhat run scripts/upgrades/${UPGRADE_SCRIPT} --network hardhat -echo "=== Upgrade diff ===" +echo "\n=== Upgrade diff ===" git --no-pager diff --name-status mv .gitignore.bak .gitignore