Skip to content

Add upgrade script#5

Open
ae2079 wants to merge 12 commits intomainfrom
AddUpgradeFlow
Open

Add upgrade script#5
ae2079 wants to merge 12 commits intomainfrom
AddUpgradeFlow

Conversation

@ae2079
Copy link
Contributor

@ae2079 ae2079 commented Feb 19, 2026

Add an script to deploy a new version of the contract and upgrade it in the proxy contract

Summary by CodeRabbit

  • New Features

    • Added multi-network upgrade support and new environment placeholders for many L2s and chains; added direct and multisig upgrade workflows and Safe submission tooling.
  • Documentation

    • Added comprehensive production, testnet, and multisig upgrade guides; simplified environment setup and added upgrade instructions.
  • Tests

    • Added upgrade test suite validating implementation swaps and donation behavior preservation.
  • Chores

    • Added CLI scripts and package scripts for deploying implementations, generating multisig payloads, submitting Safe proposals, and direct upgrades; updated ignore list.

- Updated `deploy:sepolia` script to deploy the DonationHandler implementation.
- Added new upgrade scripts for both mainnet and sepolia, including a test fork option.
- Expanded README with detailed instructions on upgrading existing deployments and verifying upgrades.
- Improved logging in the deployment script for better visibility of deployment details.
- Added .env.local to .gitignore to prevent local environment files from being tracked.
- Included a new script path for retrieving proxy addresses in package.json.
- Changed `upgrade:mainnet:fork` to `upgrade:mainnet:simulate` in package.json for consistency.
- Updated documentation across README and PRODUCTION_UPGRADE.md to reflect the new simulate command.
- Enhanced comments in deployment scripts for better understanding of deployment and upgrade processes.
- Introduced new scripts for deploying the DonationHandler implementation on both mainnet and sepolia in package.json.
- Expanded PRODUCTION_UPGRADE.md with instructions for deploying the implementation without ProxyAdmin access, including verification steps and handoff to the proxy owner.
- Added new RPC endpoints for Base, Arbitrum, Gnosis, Celo, Optimism, and Polygon in .env.example and foundry.toml.
- Updated PRODUCTION_UPGRADE.md to reflect the new deployment command format for different chains.
- Added new environment variables for Safe upgrade proposals in .env.example.
- Updated .gitignore to include upgrade-payload.json.
- Introduced new scripts in package.json for generating upgrade payloads and submitting upgrades to Safe.
- Added dependencies for Safe API and protocol kits to support the new upgrade functionality.
- Added new parameters to .env.example for Safe upgrade proposals.
- Modified package.json to streamline the upgrade submission script.
- Expanded MULTISIG_UPGRADE_GUIDE.md with detailed steps for proposing upgrades.
- Enhanced submit-upgrade-to-safe.mjs to load env and improve error messaging.

Made-with: Cursor
@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds end-to-end upgrade infrastructure for DonationHandler: multi-chain RPC config, Foundry deploy/upgrade scripts (implementation + proxy), Safe multisig payload generation and submission, shell/Node automation, docs/runbooks for testnet/production/multisig, CI-friendly ignore entries, package script/dependency updates, and upgrade-focused tests.

Changes

Cohort / File(s) Summary
Environment & Ignore
\.env\.example, \.gitignore, foundry\.toml
Expanded .env.example with RPC placeholders for Base/Arbitrum/Gnosis/Celo/Optimism/Polygon, PRIVATE_KEY, and Safe upgrade placeholders (SAFE_ADDRESS, PROXY_ADMIN_ADDRESS, PROXY_ADDRESS, NEW_IMPLEMENTATION_ADDRESS, PROPOSER_PRIVATE_KEY, SAFE_API_KEY). Added .env.local and upgrade-payload.json to .gitignore. Added network RPC and Etherscan entries to foundry.toml.
Docs & Runbooks
README.md, PRODUCTION_UPGRADE.md, TESTNET_DEPLOYMENT.md, docs/MULTISIG_UPGRADE_GUIDE.md
New and expanded documentation: README upgrade section and simpler PRIVATE_KEY instruction; full production and testnet (Sepolia) upgrade/runbook steps, verification, rollback, and monitoring guidance; multisig/Safe upgrade guide and checklist.
Package & Tooling
package.json
Added scripts: addresses, deploy:implementation, upgrade:direct, upgrade:generate-payload, upgrade:submit-to-safe. Updated deploy scripts to use DeployDonationHandler entrypoint. Added dev deps: Safe kits, dotenv, ethers; updated forge-std tag.
Forge Scripts
script/DeployDonationHandler.s.sol, script/DeployDonationHandlerImplementation.s.sol, script/UpgradeDonationHandler.s.sol
Modified DeployDonationHandler to deploy implementation then proxy, derive deployer from PRIVATE_KEY, and read proxy admin via EIP‑1967 slot. Added DeployDonationHandlerImplementation (implementation-only deploy) and UpgradeDonationHandler (ProxyAdmin.upgradeAndCall driven upgrade).
Shell & Node Automation
scripts/deploy-implementation.sh, scripts/upgrade-direct.sh, scripts/generate-upgrade-payload.sh, scripts/submit-upgrade-to-safe.mjs
Added CLI scripts: implementation deployer with chain/legacy handling, direct upgrade runner with simulate mode, Safe payload generator (writes upgrade-payload.json), and Node script to submit and sign Safe proposals via Safe Transaction Service.
Testing
test/UpgradeDonationHandler.t.sol
New Foundry test suite DonationHandlerUpgradeTest validating proxy deployment, upgrade mechanics (implementation slot update), owner preservation, donation behavior post-upgrade, and revert on mismatched amounts.

Sequence Diagrams

sequenceDiagram
    participant Deployer as Deployer (EOA)
    participant Scripts as Shell/Node Scripts
    participant Forge as Forge Script
    participant Impl as DonationHandler Impl
    participant ProxyAdmin as ProxyAdmin
    participant Proxy as TransparentProxy
    participant SafeAPI as Safe Transaction Service

    Deployer->>Forge: deploy implementation (forge/script or yarn)
    Forge->>Impl: Deploy DonationHandler implementation
    Impl-->>Forge: Return implementation address
    Forge-->>Deployer: Log NEW_IMPLEMENTATION_ADDRESS

    Deployer->>Scripts: generate Safe payload (generate-upgrade-payload.sh)
    Scripts->>Scripts: compute calldata for upgradeAndCall(proxy, impl, 0x)
    Scripts-->>Deployer: write upgrade-payload.json / UI link

    Deployer->>SafeAPI: submit proposal (submit-upgrade-to-safe.mjs)
    SafeAPI-->>Deployer: proposal hash / nonce / queue URL

    Deployer->>Forge: execute direct upgrade (upgrade-direct.sh) OR Safe executes upgrade
    Forge->>ProxyAdmin: upgradeAndCall(proxy, impl, 0x)
    ProxyAdmin->>Proxy: invoke upgrade (delegatecall flow)
    Proxy->>Proxy: set implementation slot (EIP-1967)
    Proxy-->>Forge: upgrade confirmed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through scripts and docs so bright,

Impl deployed by day, upgraded by night,
Safe proposals snug in JSON and key,
Proxies swap code — donations stay free! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'Add upgrade script' is vague and generic, referring only to 'upgrade script' without indicating the broader scope of changes including deployment scripts, documentation, configuration updates, environment setup, and test coverage for production upgrades across multiple networks. Consider a more specific title such as 'Add production upgrade infrastructure with multi-chain support' or 'Add upgrade and deployment tooling for production and testnet environments' to better convey the comprehensive nature of the changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch AddUpgradeFlow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (4)
script/DeployDonationHandler.s.sol (1)

14-17: Unused constants - consider removing or documenting purpose.

The ETHEREUM_PROXY_ADMIN, GNOSIS_PROXY_ADMIN, OPTIMISM_PROXY_ADMIN, and POLYGON_PROXY_ADMIN constants are declared but never used in the run() function. The script creates a new ProxyAdmin automatically via the TransparentUpgradeableProxy constructor.

If these are meant for reference/documentation of existing deployments, consider moving them to a separate constants file or adding a comment explaining their purpose. Otherwise, they could be removed to reduce confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@script/DeployDonationHandler.s.sol` around lines 14 - 17, The four unused
constants ETHEREUM_PROXY_ADMIN, GNOSIS_PROXY_ADMIN, OPTIMISM_PROXY_ADMIN, and
POLYGON_PROXY_ADMIN in DeployDonationHandler.s.sol are declared but never
referenced (run() creates a new ProxyAdmin via TransparentUpgradeableProxy);
either remove these constants to avoid confusion or clearly document their
purpose (e.g., comment that they are addresses of existing deployed ProxyAdmin
contracts) or move them into a dedicated constants/config file for reference;
update any top-of-file declarations and add a short comment referencing
TransparentUpgradeableProxy and run() so it’s clear why they’re unused.
package.json (2)

19-19: Inconsistent private key handling between deploy scripts.

deploy:mainnet uses --account $MAINNET_DEPLOYER_NAME (Foundry's encrypted keystore), but deploy:sepolia does not specify an account flag, implying it relies on PRIVATE_KEY env var. This inconsistency could cause confusion or deployment failures.

Consider aligning the approach—either both use keystore accounts or both use PRIVATE_KEY:

Option: Use keystore for Sepolia
-    "deploy:sepolia": "bash -c 'source .env && forge script script/DeployDonationHandler.s.sol:DeployDonationHandler --rpc-url $SEPOLIA_RPC --broadcast --verify -vvvv'",
+    "deploy:sepolia": "bash -c 'source .env && forge script script/DeployDonationHandler.s.sol:DeployDonationHandler --rpc-url $SEPOLIA_RPC --account $SEPOLIA_DEPLOYER_NAME --broadcast --verify -vvvv'",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 19, The deploy scripts are inconsistent:
"deploy:mainnet" uses Forge's keystore account flag (--account
$MAINNET_DEPLOYER_NAME) while "deploy:sepolia" relies on an env private key;
pick one approach and make both scripts consistent. Update the "deploy:sepolia"
npm script (the value that runs forge script
script/DeployDonationHandler.s.sol:DeployDonationHandler ...) to either add the
same keystore flag (--account $SEPOLIA_DEPLOYER_NAME) if you want keystore-based
deploys, or explicitly pass the private key flag (e.g., using the equivalent
Forge option or ensuring $PRIVATE_KEY is used consistently), and mirror the
choice in "deploy:mainnet" so both scripts use the same authentication method.

32-34: Upgrade scripts also lack --account flag for key management.

The upgrade scripts read PRIVATE_KEY directly from env (as seen in UpgradeDonationHandler.s.sol line 13), while deploy:mainnet uses Foundry's encrypted keystore via --account. For production mainnet upgrades, using the keystore is more secure as it avoids plaintext private keys in environment variables.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 32 - 34, The upgrade npm scripts
("upgrade:mainnet", "upgrade:mainnet:simulate", "upgrade:sepolia") currently
rely on a plaintext PRIVATE_KEY in env (used in UpgradeDonationHandler.s.sol
line 13); update these scripts to pass Foundry's keystore via the --account flag
instead of relying on PRIVATE_KEY: change each script to include --account
$ACCOUNT (or the appropriate keystore identifier/environment variable) and
ensure your .env and deployment docs set ACCOUNT to the encrypted keystore/alias
used by Foundry; keep the simulate script behavior but also pass --account so
key management is consistent for testing and production upgrades.
script/UpgradeDonationHandler.s.sol (1)

12-39: Consider logging the deployer address for auditability.

The script logs proxy and implementation addresses but not the deployer/caller address. Adding this would help with post-deployment verification and audit trails.

💡 Suggested enhancement
     address proxyAddress = vm.envAddress('PROXY_ADDRESS');
     address proxyAdminAddress = vm.envAddress('PROXY_ADMIN_ADDRESS');
+    address deployer = vm.addr(deployerPrivateKey);
 
     console.log('=== Upgrading DonationHandler ===');
+    console.log('Deployer:', deployer);
     console.log('Proxy Address:', proxyAddress);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@script/UpgradeDonationHandler.s.sol` around lines 12 - 39, Add logging for
the deployer address in run(): derive the deployer address from the existing
deployerPrivateKey (use vm.addr(deployerPrivateKey)) and log it before
vm.startBroadcast so the script prints who is performing the upgrade; reference
the run() function, deployerPrivateKey variable, and vm.startBroadcast to locate
where to insert the new deployer address calculation and console.log.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Around line 14-20: The .env.example is missing the PRIVATE_KEY entry required
by the deployment/upgrade flow; add a PRIVATE_KEY= line to the template
(alongside SAFE_ADDRESS, PROXY_ADMIN_ADDRESS, etc.) with a short comment noting
it's required for Forge scripts (e.g., "required: private key for deployer") so
users copying .env.example won't have missing env variable errors when code
reads process.env.PRIVATE_KEY or any script referencing PRIVATE_KEY.

In `@docs/MULTISIG_UPGRADE_GUIDE.md`:
- Around line 144-163: Replace the placeholder selector guidance with a
reference to the repository script and/or the exact cast command so signers get
deterministic calldata; specifically tell them to run the provided yarn script
named upgrade:generate-payload (which invokes cast calldata
"upgrade(address,address)" <proxy> <implementation>) or run cast calldata
directly to produce the full calldata instead of manually using a partial
selector like 0x99.... Update the text around the upgrade(address,address)
example and the “Selector: 0x99...” line to point to the yarn script or the cast
command and explain that it outputs the exact hex calldata to paste into the
Safe UI.

In `@PRODUCTION_UPGRADE.md`:
- Around line 158-163: The guide's manual upgrade commands for Gnosis and Celo
use the forge script invocation "forge script
script/UpgradeDonationHandler.s.sol:UpgradeDonationHandler" without the legacy
flag; update the PRODUCTION_UPGRADE.md commands that target Gnosis and Celo to
include the "--legacy" flag (i.e., add "--legacy" to the existing forge script
lines) so those transactions use legacy-style signing consistent with
scripts/deploy-implementation.sh handling of gnosis and celo.

In `@README.md`:
- Around line 296-303: The README shows using cast call to invoke
implementation() on a TransparentUpgradeableProxy, which will revert for
non-admin callers; instead update the verification instructions to read the
implementation address from the EIP-1967 storage slot (the bytes32 slot computed
from keccak256("eip1967.proxy.implementation") - 1) for the given PROXY_ADDRESS
and MAINNET_RPC; reference TransparentUpgradeableProxy and implementation() in
the explanation and replace the failing cast call with a command that reads the
implementation storage slot directly.

In `@script/DeployDonationHandlerImplementation.s.sol`:
- Around line 25-28: The printed shell export includes an unintended space
because console.log is called with two arguments; update the hand-off console
output (the three console.log lines around the proxy handoff that reference
address(implementation)) so the export is emitted as a single contiguous string
(no space after '=') — e.g., concatenate or interpolate 'export
NEW_IMPLEMENTATION_ADDRESS=' and address(implementation) into one argument so a
user can copy-paste a valid shell assignment.

In `@scripts/generate-upgrade-payload.sh`:
- Around line 73-100: The JSON currently declares contractMethod name "upgrade"
with two inputs; update it to "upgradeAndCall" and add the third parameter for
the call data: in the contractMethod object change "name" from "upgrade" to
"upgradeAndCall" and add an inputs entry { "name": "data", "type": "bytes" };
then update contractInputsValues to include "data": "$CALLDATA" (using the
existing CALLDATA variable) alongside "proxy": "$PROXY" and "implementation":
"$NEW_IMPL" so the JSON matches the upgradeAndCall(proxy, implementation, data)
signature.
- Around line 36-37: The script currently builds calldata using the removed
ProxyAdmin signature "upgrade(address,address)"; update all occurrences to use
"upgradeAndCall(address,address,bytes)" with the empty bytes argument "0x"
(e.g., set CALLDATA via cast calldata "upgradeAndCall(address,address,bytes)"
"$PROXY" "$NEW_IMPL" "0x"), and mirror this change in all user-visible strings
and JSON fields (console docs, safe UI instructions, tx description and the JSON
contractMethod) so they reference upgradeAndCall and include the empty data
parameter.

In `@scripts/submit-upgrade-to-safe.mjs`:
- Around line 125-127: The script is encoding the wrong function signature;
replace the use of ethers.Interface with the ProxyAdmin v5 signature by creating
an Interface for 'function upgradeAndCall(address proxy, address implementation,
bytes data)' and change the encodeFunctionData call from 'upgrade' to
'upgradeAndCall', passing [proxy, newImplementation, "0x"] (or an appropriate
init calldata) so the generated tx matches OpenZeppelin v5 ProxyAdmin's
upgradeAndCall(proxy, implementation, bytes) expectation; update any reference
to iface.encodeFunctionData('upgrade', ...) accordingly.

In `@TESTNET_DEPLOYMENT.md`:
- Around line 134-137: The script uses a direct selector call ("0x5c60da1b")
against $PROXY_ADDRESS to read the implementation; replace that with an EIP-1967
storage-slot read or a ProxyAdmin helper: use eth_getStorageAt for slot
0x360894a13ba1a3210667c828492db98dca3e2848 (or call
ProxyAdmin.getProxyImplementation(proxy) / ERC1967Utils.getImplementation()) to
retrieve the implementation address reliably, updating the occurrences where
OLD_IMPL is set (the block referencing $PROXY_ADDRESS and selector 0x5c60da1b
and the similar block later) so the script reads the storage slot or calls the
admin helper instead of using the selector.

---

Nitpick comments:
In `@package.json`:
- Line 19: The deploy scripts are inconsistent: "deploy:mainnet" uses Forge's
keystore account flag (--account $MAINNET_DEPLOYER_NAME) while "deploy:sepolia"
relies on an env private key; pick one approach and make both scripts
consistent. Update the "deploy:sepolia" npm script (the value that runs forge
script script/DeployDonationHandler.s.sol:DeployDonationHandler ...) to either
add the same keystore flag (--account $SEPOLIA_DEPLOYER_NAME) if you want
keystore-based deploys, or explicitly pass the private key flag (e.g., using the
equivalent Forge option or ensuring $PRIVATE_KEY is used consistently), and
mirror the choice in "deploy:mainnet" so both scripts use the same
authentication method.
- Around line 32-34: The upgrade npm scripts ("upgrade:mainnet",
"upgrade:mainnet:simulate", "upgrade:sepolia") currently rely on a plaintext
PRIVATE_KEY in env (used in UpgradeDonationHandler.s.sol line 13); update these
scripts to pass Foundry's keystore via the --account flag instead of relying on
PRIVATE_KEY: change each script to include --account $ACCOUNT (or the
appropriate keystore identifier/environment variable) and ensure your .env and
deployment docs set ACCOUNT to the encrypted keystore/alias used by Foundry;
keep the simulate script behavior but also pass --account so key management is
consistent for testing and production upgrades.

In `@script/DeployDonationHandler.s.sol`:
- Around line 14-17: The four unused constants ETHEREUM_PROXY_ADMIN,
GNOSIS_PROXY_ADMIN, OPTIMISM_PROXY_ADMIN, and POLYGON_PROXY_ADMIN in
DeployDonationHandler.s.sol are declared but never referenced (run() creates a
new ProxyAdmin via TransparentUpgradeableProxy); either remove these constants
to avoid confusion or clearly document their purpose (e.g., comment that they
are addresses of existing deployed ProxyAdmin contracts) or move them into a
dedicated constants/config file for reference; update any top-of-file
declarations and add a short comment referencing TransparentUpgradeableProxy and
run() so it’s clear why they’re unused.

In `@script/UpgradeDonationHandler.s.sol`:
- Around line 12-39: Add logging for the deployer address in run(): derive the
deployer address from the existing deployerPrivateKey (use
vm.addr(deployerPrivateKey)) and log it before vm.startBroadcast so the script
prints who is performing the upgrade; reference the run() function,
deployerPrivateKey variable, and vm.startBroadcast to locate where to insert the
new deployer address calculation and console.log.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ae3789e-e1c9-404d-83fd-b03f80d3ac4e

📥 Commits

Reviewing files that changed from the base of the PR and between 2567ba3 and 9c7e095.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • .env.example
  • .gitignore
  • PRODUCTION_UPGRADE.md
  • README.md
  • TESTNET_DEPLOYMENT.md
  • docs/MULTISIG_UPGRADE_GUIDE.md
  • foundry.toml
  • package.json
  • script/DeployDonationHandler.s.sol
  • script/DeployDonationHandlerImplementation.s.sol
  • script/TestUpgrade.s.sol
  • script/UpgradeDonationHandler.s.sol
  • scripts/deploy-implementation.sh
  • scripts/generate-upgrade-payload.sh
  • scripts/submit-upgrade-to-safe.mjs

- Replaced upgrade scripts in package.json with a new direct upgrade command.
- Expanded MULTISIG_UPGRADE_GUIDE.md to clarify upgrade flows for both multisig and EOA owners.
- Updated UpgradeDonationHandler script to utilize pre-deployed implementation addresses.
- Improved clarity in upgrade instructions and environment variable requirements.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/upgrade-direct.sh`:
- Around line 21-22: The script sources .env but doesn't export those variables
to child processes, causing forge script to run without PRIVATE_KEY and other
vars; wrap the sourcing with automatic export using set -a before sourcing and
set +a after to ensure variables (e.g., PRIVATE_KEY used/validated and consumed
by forge script calls) are exported to child processes, update the block around
the source .env lines accordingly so validation still runs but forge script
inherits the env.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c45a89c2-5806-4e89-b958-5c08e129b50f

📥 Commits

Reviewing files that changed from the base of the PR and between 9c7e095 and 347c4a4.

📒 Files selected for processing (4)
  • docs/MULTISIG_UPGRADE_GUIDE.md
  • package.json
  • script/UpgradeDonationHandler.s.sol
  • scripts/upgrade-direct.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/MULTISIG_UPGRADE_GUIDE.md

- Update PRODUCTION_UPGRADE.md and MULTISIG_UPGRADE_GUIDE.md for `upgradeAndCall`.
- Generate Safe calldata with empty `data` so upgrades do not trigger initialization.
- Clarify upgrade instructions and warn against calling `initialize()` during upgrades.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PRODUCTION_UPGRADE.md`:
- Around line 94-99: The fenced code block under the "Upgrading DonationHandler"
section currently has no language hint causing markdownlint MD040; update the
triple-backtick fence for the expected-output block (the block showing "Proxy
Address: 0x97b2..." through "Proxy upgraded successfully!") to include a
language token such as text (e.g., ```text) so the block is treated as plain
output in PRODUCTION_UPGRADE.md.
- Around line 235-247: The example uses cast send which broadcasts a real
transaction and can consume funds; change the test to use cast call to simulate
the revert without broadcasting or spending gas. Update the command that targets
donateManyETH (the proxy interaction referencing $PROXY_ADDRESS,
donateManyETH(uint256,address[],uint256[],bytes[]), the arrays and --value
1ether) to use cast call instead of cast send so the revert "Amounts do not
match total" is validated safely.
- Around line 113-118: Replace the cast call that uses selector 0x5c60da1b
(which attempts to call implementation()) with a direct read of the ERC‑1967
implementation storage slot; specifically, use the proxy address variable
(PROXY_ADDRESS) and read storage at slot
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc via cast
storage and the RPC URL variable (RPC_URL) instead of calling the non‑exposed
implementation() getter.

In `@scripts/submit-upgrade-to-safe.mjs`:
- Around line 199-200: The error message inside the err handler that checks
"err.message.includes('Unprocessable') || err.message.includes('422')" currently
hardcodes "Polygon"; update that message to be chain-aware by interpolating the
existing chain variable (e.g., use `${chain}`) or make the hint generic (remove
the network name) so it doesn't mislead users on non-Polygon networks; change
the console.error call that prints "Use the correct Safe address for Polygon."
to use the dynamic chain identifier or a generic wording.
- Around line 148-156: The current flow sets txOptions.nonce from the local
nonce variable and calls protocolKit.createTransaction which, when nonce is
undefined, reads the on-chain nonce and can collide with pending queued
transactions; instantiate SafeApiKit (apiKit) before creating the transaction
and, if nonce is undefined, call apiKit.getNextNonce(safeAddress) and assign
that value to txOptions.nonce so protocolKit.createTransaction always receives a
concrete nonce (use the existing safeAddress and safeTransactionData variables);
apply the same change for the later block covering lines 160-177 that creates a
transaction.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 28bd1268-aa86-4d56-abbb-c47642cf67fa

📥 Commits

Reviewing files that changed from the base of the PR and between 347c4a4 and 6480271.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • PRODUCTION_UPGRADE.md
  • docs/MULTISIG_UPGRADE_GUIDE.md
  • scripts/generate-upgrade-payload.sh
  • scripts/submit-upgrade-to-safe.mjs
✅ Files skipped from review due to trivial changes (1)
  • docs/MULTISIG_UPGRADE_GUIDE.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/generate-upgrade-payload.sh

ae2079 added 2 commits March 23, 2026 20:51
change the test file naming to be compatible with the repo convention
@ae2079 ae2079 requested a review from divine-comedian March 23, 2026 18:00
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Line 12: Remove the trailing spaces between the = and inline comment for the
affected environment variables so the exported values won't include spaces or
comment text; specifically update the PRIVATE_KEY entry (and the variable on
line 20) to either remove the inline comment entirely or place the comment with
no space after = (e.g., set "VARIABLE=" then add a comment after a space),
ensuring the format is VARIABLE=VALUE or VARIABLE= followed immediately by the
comment delimiter so sourcing .env yields the correct empty or assigned value.

In `@script/DeployDonationHandler.s.sol`:
- Around line 46-47: The console.log calls produce a space because they pass
multiple args; change them to emit a single concatenated string so shell export
lines have no space. Replace the two lines that reference console.log('export
PROXY_ADDRESS=', address(proxy)) and console.log('export PROXY_ADMIN_ADDRESS=',
proxyAdmin) with single-string logs built via concatenation (e.g., string.concat
or building a string from abi.encodePacked) and convert addresses to hex strings
(using a helper like Strings.toHexString(uint160(address), 20) or your existing
address-to-string helper) before concatenation so the output becomes "export
PROXY_ADDRESS=0x..." and "export PROXY_ADMIN_ADDRESS=0x...".

In `@TESTNET_DEPLOYMENT.md`:
- Around line 19-20: The "Alchemy Sepolia Faucet" link currently points to the
same URL as "Sepolia Faucet"; update the second link target so the markdown link
[Alchemy Sepolia Faucet] uses the correct Alchemy URL
(https://www.alchemy.com/faucets/ethereum-sepolia) instead of
https://sepoliafaucet.com/, ensuring the visible link text stays the same.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b38286d-76fd-4043-ae73-2e6943278ced

📥 Commits

Reviewing files that changed from the base of the PR and between 6480271 and fe45f25.

📒 Files selected for processing (12)
  • .env.example
  • PRODUCTION_UPGRADE.md
  • README.md
  • TESTNET_DEPLOYMENT.md
  • docs/MULTISIG_UPGRADE_GUIDE.md
  • package.json
  • script/DeployDonationHandler.s.sol
  • script/DeployDonationHandlerImplementation.s.sol
  • script/UpgradeDonationHandler.s.sol
  • scripts/submit-upgrade-to-safe.mjs
  • scripts/upgrade-direct.sh
  • test/UpgradeDonationHandler.t.sol
✅ Files skipped from review due to trivial changes (2)
  • docs/MULTISIG_UPGRADE_GUIDE.md
  • PRODUCTION_UPGRADE.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • script/DeployDonationHandlerImplementation.s.sol
  • scripts/upgrade-direct.sh
  • README.md
  • scripts/submit-upgrade-to-safe.mjs
  • package.json

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
TESTNET_DEPLOYMENT.md (1)

69-79: ⚠️ Potential issue | 🟡 Minor

Expected output block is stale and includes invalid export examples.

Line 77 and Line 78 still show = 0x... (extra space), and the headings don’t match current script output. Please align this block with the script so users can copy safely.

Suggested doc patch
-=== Deploying DonationHandler to Sepolia ===
+=== Deploying DonationHandler ===
 Deployer: 0x...
 Implementation deployed to: 0x...
 Proxy deployed to: 0x...
 ProxyAdmin deployed to: 0x...
 
-=== Save these addresses for upgrading! ===
-export PROXY_ADDRESS= 0x...
-export PROXY_ADMIN_ADDRESS= 0x...
+=== Save these for upgrading ===
+export PROXY_ADDRESS=0x...
+export PROXY_ADMIN_ADDRESS=0x...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TESTNET_DEPLOYMENT.md` around lines 69 - 79, Update the example output block
for the deployment to match the actual script output: change the heading "===
Deploying DonationHandler to Sepolia ===" and any other headings to the current
script's headings, and fix the export lines by removing the extra space so they
read exactly "export PROXY_ADDRESS=0x..." and "export
PROXY_ADMIN_ADDRESS=0x..."; ensure the Implementation/Proxy/ProxyAdmin lines and
the "Save these addresses for upgrading!" heading match the script's exact
wording and formatting so users can copy the exports safely.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@TESTNET_DEPLOYMENT.md`:
- Line 69: The fenced code blocks containing the deployment and upgrade sections
(the blocks wrapping "=== Deploying DonationHandler ===" and "=== Upgrading
DonationHandler ===" / "=== Upgrading DonationHandler ===") are missing a
language identifier and trigger MD040; update those triple-backtick fences to
include a language token (e.g., add "text" after ```), so the blocks become
```text ... ``` for both the Deploying DonationHandler and Upgrading
DonationHandler fenced sections.

---

Duplicate comments:
In `@TESTNET_DEPLOYMENT.md`:
- Around line 69-79: Update the example output block for the deployment to match
the actual script output: change the heading "=== Deploying DonationHandler to
Sepolia ===" and any other headings to the current script's headings, and fix
the export lines by removing the extra space so they read exactly "export
PROXY_ADDRESS=0x..." and "export PROXY_ADMIN_ADDRESS=0x..."; ensure the
Implementation/Proxy/ProxyAdmin lines and the "Save these addresses for
upgrading!" heading match the script's exact wording and formatting so users can
copy the exports safely.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e71654a2-fb81-4d13-979b-547b6914ef12

📥 Commits

Reviewing files that changed from the base of the PR and between fe45f25 and 9d309eb.

📒 Files selected for processing (3)
  • .env.example
  • TESTNET_DEPLOYMENT.md
  • script/DeployDonationHandler.s.sol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants