Skip to content

Commit 63d0017

Browse files
authored
feat: Remove dangling deployment artifacts (#300)
1 parent 17e57bc commit 63d0017

File tree

6 files changed

+80
-23
lines changed

6 files changed

+80
-23
lines changed

.github/workflows/upgrade-facets.yml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
- arbitrumSepolia
1515
- arbitrum
1616
default: 'arbitrumSepolia'
17-
dry_run:
17+
dry-run:
1818
description: 'Dry Run (fork test only, no actual deployment)'
1919
required: false
2020
type: boolean
@@ -27,13 +27,12 @@ jobs:
2727
upgrade:
2828
needs: pre-upgrade
2929
runs-on: ubuntu-latest
30-
environment: ${{ inputs.network }} # Use the selected environment
30+
environment: ${{ inputs.network }}
3131
permissions:
3232
contents: write # Required to commit artifacts.
3333
pull-requests: write # Required to create pull requests.
3434
env:
3535
UPGRADE_SCRIPT: 'v6.1.0-bulk-processing.ts' # Update this for each specific upgrade.
36-
# For commit action
3736
COMMIT_MESSAGE: 'chore: Save upgrade artifacts - ${{ inputs.network }} (runId:${{ github.run_id }})'
3837
GHA_BOT_NAME: 'GitHub Actions Bot'
3938
GHA_BOT_EMAIL: 'github-actions[bot]@users.noreply.github.com'
@@ -54,7 +53,7 @@ jobs:
5453
run: npm run build
5554

5655
- name: Run fork test (dry run)
57-
if: inputs.dry_run == true
56+
if: inputs.dry-run == true
5857
env:
5958
# Note: it is required to define both private key env variables when calling Hardhat.
6059
DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}
@@ -66,10 +65,10 @@ jobs:
6665
elif [ "${{ inputs.network }}" == "arbitrum" ]; then
6766
export ARBITRUM_FORK=true
6867
fi
69-
npx hardhat run scripts/upgrades/${{ env.UPGRADE_SCRIPT }} --network hardhat
68+
UPGRADE_SCRIPT=${{ env.UPGRADE_SCRIPT }} bash ./scripts/upgrades/dry-run.sh
7069
7170
- name: Execute upgrade on live network
72-
if: inputs.dry_run == false
71+
if: inputs.dry-run == false
7372
env:
7473
# Note: it is required to define both private key env variables when calling Hardhat.
7574
DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}
@@ -80,7 +79,7 @@ jobs:
8079
npx hardhat run scripts/upgrades/${{ env.UPGRADE_SCRIPT }} --network ${{ inputs.network }}
8180
8281
- name: Push artifacts to the current branch
83-
if: inputs.dry_run == false && github.ref != 'refs/heads/main'
82+
if: inputs.dry-run == false && github.ref != 'refs/heads/main'
8483
uses: stefanzweifel/git-auto-commit-action@v5
8584
with:
8685
file_pattern: |
@@ -92,7 +91,7 @@ jobs:
9291

9392
# Since the `main` branch is protected, create a PR to push artifacts.
9493
- name: Push artifacts through a pull request
95-
if: inputs.dry_run == false && github.ref == 'refs/heads/main'
94+
if: inputs.dry-run == false && github.ref == 'refs/heads/main'
9695
uses: peter-evans/create-pull-request@v7
9796
with:
9897
add-paths: |

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ cache
1515
artifacts
1616

1717
contracts/hardhat-dependency-compiler/
18-
deployments/*hardhat*
18+
deployments/hardhat
19+
deployments/external-hardhat
1920
deployments/dev-*
2021

2122
# Flattened Solidity file used to generate diagrams.

scripts/upgrades/README.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,41 @@
11
# PoCo Smart Contracts Upgrade Guide
22

3-
This document explains the recommended steps for creating and applying
4-
a safe, traceable, and reproducible upgrade to the PoCo Diamond proxy.
3+
This document explains the recommended steps for creating and applying a safe, traceable,
4+
and reproducible upgrade to the PoCo Diamond proxy.
55

66
## Upgrade Steps
77

88
0. **Ensure all tests pass**:<br>
99
Run the full test suite to make sure everything is working before starting an upgrade.
1010

1111
1. **Create a new upgrade script**:<br>
12-
Name the script using the version and upgrade name format: `vX.Y.Z-upgrade-name.ts`.
12+
Name the script using the version and upgrade name in the form `vX.Y.Z-upgrade-name.ts`
13+
and implement the upgrade logic.
1314

1415
2. **Create a corresponding Markdown report**:<br>
15-
Copy the template file `v0.0.0-template.md` and rename it to match the script: `vX.Y.Z-upgrade-name.md`.
16+
Copy the template file `v0.0.0-template.md` and rename it to match the script's name
17+
(`vX.Y.Z-upgrade-name.md`).
1618

17-
3. **Update GitHub Actions**:<br>
19+
3. **Test dry-runs locally**:<br>
20+
Use the script [./dry-run.sh](./dry-run.sh) and check the logs.
21+
22+
4. **Update GitHub Actions**:<br>
1823
Modify `upgrade-facets.yml` workflow to call the new upgrade script.
1924

20-
4. **Upgrade on Testnet**:
25+
5. **Upgrade on Testnet**:
2126
- ⚠️ Always upgrade on the testnet first.
2227
- Trigger the upgrade workflow on GitHub and choose the testnet network.
2328
- Start with a **dry run** to simulate the upgrade.
2429
- Once verified, apply the upgrade on the live testnet.
2530

26-
5. **Upgrade on Mainnet**:
31+
6. **Upgrade on Mainnet**:
2732
- Trigger the upgrade workflow on GitHub and choose the mainnet network.
2833
- Perform a dry run first.
2934
- Apply the upgrade on the mainnet.
3035
- Merge the artifacts PR after successful execution.
3136

32-
6. **Update upgrade report**:<br>
37+
7. **Update upgrade report**:<br>
3338
Fill in all required information in `vX.Y.Z-upgrade-name.ts` (tx hashes, logs, ...).
3439

35-
7. **Create a release**
40+
8. **Create a release**
3641
- Use **Release Please** to tag the upgrade version and create the release on GitHub.
37-
38-
39-
Following these steps ensures upgrades are **safe, traceable, and reproducible**.

scripts/upgrades/dry-run.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Usage:
5+
# ARBITRUM_SEPOLIA_FORK=true \
6+
# UPGRADE_SCRIPT=<script_name> \
7+
# bash ./scripts/upgrades/dry-run.sh
8+
9+
10+
DEPLOYMENTS_FOLDER=''
11+
if [ "${ARBITRUM_SEPOLIA_FORK}" == "true" ]; then
12+
DEPLOYMENTS_FOLDER=arbitrumSepolia
13+
elif [ "${ARBITRUM_FORK}" == "true" ]; then
14+
DEPLOYMENTS_FOLDER=arbitrum
15+
fi
16+
if [ -z "${DEPLOYMENTS_FOLDER}" ]; then
17+
echo "Error: You must set either ARBITRUM_SEPOLIA_FORK=true or ARBITRUM_FORK=true."
18+
exit 1
19+
fi
20+
# Copy the forked network deployments to the hardhat network folder.
21+
rm -rf deployments/hardhat
22+
cp -r deployments/${DEPLOYMENTS_FOLDER} deployments/hardhat
23+
# Stage the old deployments to have a clean diff after the upgrade script run.
24+
cp .gitignore .gitignore.bak
25+
sed -i.bak '/deployments\/hardhat/d' .gitignore
26+
git add deployments/hardhat
27+
# Run the upgrade and print the git diff.
28+
npx hardhat run scripts/upgrades/${UPGRADE_SCRIPT} --network hardhat
29+
echo "\n=== Upgrade diff ==="
30+
git --no-pager diff --name-status
31+
mv .gitignore.bak .gitignore

scripts/upgrades/v6.1.0-bulk-processing.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
getUpgradeContext,
1010
linkFacetsToDiamond,
1111
printOnchainProxyFunctions,
12+
removeDanglingFacetDeploymentArtifacts,
1213
removeFacetsFromDiamond,
1314
removeFunctionsFromDiamond,
1415
} from '../../utils/proxy-tools';
@@ -103,6 +104,7 @@ async function main() {
103104
await linkFacetsToDiamond(proxyAddress, proxyOwner, facetsToAdd);
104105
await printOnchainProxyFunctions(proxyAddress);
105106
console.log('Upgrade performed successfully!');
107+
await removeDanglingFacetDeploymentArtifacts(proxyAddress);
106108
await tryVerify(facetsToAdd.map((facet) => facet.name));
107109
}
108110

utils/proxy-tools.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';
55
import { ContractFactory, FunctionFragment, Interface, ZeroAddress } from 'ethers';
6-
import { ethers } from 'hardhat';
6+
import { deployments, ethers } from 'hardhat';
77
import { FacetCut, FacetCutAction } from 'hardhat-deploy/dist/types';
88
import type { IDiamond } from '../typechain';
99
import {
@@ -279,7 +279,7 @@ export async function removeFacetsFromDiamond(
279279
throw new Error(`Facet ${facet.name} is empty or does not exist on-chain`);
280280
}
281281
console.log(
282-
`Will remove the whole facet ${facet.name} [address: ${facet.address}, functions:${selectors.length}]`,
282+
`Will remove facet [name:${facet.name}, address: ${facet.address}, functions:${selectors.length}]`,
283283
);
284284
facetCuts.push({
285285
facetAddress: ZeroAddress,
@@ -350,3 +350,25 @@ export async function removeFunctionsFromDiamond(
350350
await tx.wait();
351351
console.log('Functions removed successfully!');
352352
}
353+
354+
/**
355+
* Removes dangling deployment artifacts for facets that are no longer linked to the diamond proxy.
356+
* This is not done automatically in `removeFacetsFromDiamond` because we deploy new facets first,
357+
* then remove old facets, which sometimes overwrites the existing ones.
358+
* @param proxyAddress address of the diamond proxy
359+
*/
360+
export async function removeDanglingFacetDeploymentArtifacts(proxyAddress: string) {
361+
console.log('\n=== Removing dangling deployment artifacts ===');
362+
const allDeployments = await deployments.all();
363+
const diamondLoupe = DiamondLoupeFacet__factory.connect(proxyAddress, ethers.provider);
364+
const onchainFacets = await diamondLoupe.facetAddresses();
365+
for (const deploymentName of Object.keys(allDeployments)) {
366+
const deploymentAddress = allDeployments[deploymentName].address;
367+
if (deploymentName.endsWith('Facet') && !onchainFacets.includes(deploymentAddress)) {
368+
console.log(
369+
`Deleting dangling facet artifact [name:${deploymentName}, address:${deploymentAddress}]`,
370+
);
371+
await deployments.delete(deploymentName);
372+
}
373+
}
374+
}

0 commit comments

Comments
 (0)