https://github.com/pcaversaccio/zksync-emergency-upgrades
ZKsync emergency upgrade verification tools.
https://github.com/pcaversaccio/zksync-emergency-upgrades
bash emergency-upgrades solidity zksync-era
Last synced: 12 months ago
JSON representation
ZKsync emergency upgrade verification tools.
- Host: GitHub
- URL: https://github.com/pcaversaccio/zksync-emergency-upgrades
- Owner: pcaversaccio
- License: agpl-3.0
- Created: 2025-03-08T17:28:19.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-06-13T15:05:22.000Z (about 1 year ago)
- Last Synced: 2025-06-13T16:24:55.878Z (about 1 year ago)
- Topics: bash, emergency-upgrades, solidity, zksync-era
- Language: Shell
- Homepage:
- Size: 75.2 KB
- Stars: 3
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# ZKsync Emergency Upgrade Verification Tools
- [Proposal ID Calculation for ZKsync `executeEmergencyUpgrade`s](#proposal-id-calculation-for-zksync-executeemergencyupgrades)
- [Usage](#usage)
- [Example 1 – Go-Live Emergency Upgrade](#example-1--go-live-emergency-upgrade)
- [Example 2 – Accept Ownership After ZIP5 Emergency Upgrade](#example-2--accept-ownership-after-zip5-emergency-upgrade)
- [EIP-712-Based Off-Chain Message Safe Hashes](#eip-712-based-off-chain-message-safe-hashes)
- [💸 Donation](#-donation)
## Proposal ID Calculation for ZKsync `executeEmergencyUpgrade`s
> [!NOTE]
> The original gist can be found [here](https://gist.github.com/pcaversaccio/0ef8fb8034594e012a4903dfa992369e).
The function `executeEmergencyUpgrade` will be invoked in the following contract:
- `ProtocolUpgradeHandler`: [`0x8f7a9912416e8AdC4D9c21FAe1415D3318A11897`](https://etherscan.io/address/0x8f7a9912416e8AdC4D9c21FAe1415D3318A11897#code)
```solidity
/// @dev Represents a call to be made during an upgrade.
/// @param target The address to which the call will be made.
/// @param value The amount of Ether (in wei) to be sent along with the call.
/// @param data The calldata to be executed on the `target` address.
struct Call {
address target;
uint256 value;
bytes data;
}
/// @dev Defines the structure of an upgrade that is executed by Protocol Upgrade Handler.
/// @param executor The L1 address that is authorized to perform the upgrade execution (if address(0) then anyone).
/// @param calls An array of `Call` structs, each representing a call to be made during the upgrade execution.
/// @param salt A bytes32 value used for creating unique upgrade proposal hashes.
struct UpgradeProposal {
Call[] calls;
address executor;
bytes32 salt;
}
/// @notice Executes an emergency upgrade proposal initiated by the emergency upgrade board.
/// @param _proposal The upgrade proposal details including proposed actions and the executor address.
function executeEmergencyUpgrade(UpgradeProposal calldata _proposal) external payable onlyEmergencyUpgradeBoard {
bytes32 id = keccak256(abi.encode(_proposal));
UpgradeState upgState = upgradeState(id);
// 1. Checks
require(upgState == UpgradeState.None, "Upgrade already exists");
require(_proposal.executor == msg.sender, "msg.sender is not authorized to perform the upgrade");
// 2. Effects
upgradeStatus[id].executed = true;
// Clear the freeze
lastFreezeStatusInUpgradeCycle = FreezeStatus.None;
protocolFrozenUntil = 0;
_unfreeze();
// 3. Interactions
_execute(_proposal.calls);
emit Unfreeze();
emit EmergencyUpgradeExecuted(id);
}
```
In order the retrieve the proposal ID, we need to calculate:
```solidity
keccak256(abi.encode(_proposal));
```
### Usage
> [!NOTE]
> Ensure that [`forge`](https://github.com/foundry-rs/foundry/tree/master/crates/forge) and [`cast`](https://github.com/foundry-rs/foundry/tree/master/crates/cast) are installed locally. For installation instructions, refer to this [guide](https://getfoundry.sh/introduction/installation/).
Adjust the `executor`, `salt`, and `calls` parameters either in [`ProposalIdGoLive.sol`](./ProposalIdGoLive.sol) or [`proposal_id_go_live.sh`](./proposal_id_go_live.sh) and invoke
```console
forge script ProposalIdGoLive.sol --target-contract ProposalIdGoLive --sig "computeProposalId()"
```
or
```console
./proposal_id_go_live.sh
```
#### Example 1 – Go-Live Emergency Upgrade
The proposal ID, given by the ZKsync Era UI is `0xdd9aadc3b6e3297fed40a2cf0a7e655ff5af02c9ce918ed0e86f538c1c53ce9d`. So we need to verify that one.
From the docs [here](https://hackmd.io/@alishaZK/BJ7-jEv2C#Transaction-20), we know:
```console
bytes32 salt = 0x646563656e7472616c697a6174696f6e206973206e6f74206f7074696f6e616c
```
The executor in our case is [`0xdEFd1eDEE3E8c5965216bd59C866f7f5307C9b29`](https://etherscan.io/address/0xdEFd1eDEE3E8c5965216bd59C866f7f5307C9b29), the `EmergencyUpgradeBoard` contract.
**Proposal ID Calculation**
```solidity
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.30;
contract ProposalIdGoLive {
struct Call {
address target;
uint256 value;
bytes data;
}
struct UpgradeProposal {
Call[] calls;
address executor;
bytes32 salt;
}
function computeProposalId() external pure returns (bytes32) {
Call[] memory calls = new Call[](4);
calls[0] = Call({target: 0xD7f9f54194C633F36CCD5F3da84ad4a1c38cB2cB, value: 0, data: hex"79ba5097"});
calls[1] = Call({target: 0x303a465B659cBB0ab36eE643eA362c509EEb5213, value: 0, data: hex"79ba5097"});
calls[2] = Call({target: 0xc2eE6b6af7d616f6e27ce7F4A451Aedc2b0F5f5C, value: 0, data: hex"79ba5097"});
calls[3] = Call({target: 0x5D8ba173Dc6C3c90C8f7C04C9288BeF5FDbAd06E, value: 0, data: hex"79ba5097"});
address executor = 0xdEFd1eDEE3E8c5965216bd59C866f7f5307C9b29;
bytes32 salt = hex"646563656e7472616c697a6174696f6e206973206e6f74206f7074696f6e616c";
UpgradeProposal memory upgradeProposal = UpgradeProposal({calls: calls, executor: executor, salt: salt});
return keccak256(abi.encode(upgradeProposal));
}
}
```
Now invoke:
```console
forge script ProposalIdGoLive.sol --target-contract ProposalIdGoLive --sig "computeProposalId()"
```
which will output:
```console
== Return ==
0: bytes32 0xdd9aadc3b6e3297fed40a2cf0a7e655ff5af02c9ce918ed0e86f538c1c53ce9d
```
#### Example 2 – Accept Ownership After ZIP5 Emergency Upgrade
The upgrade data for the emergency upgrade "Accept ownership after ZIP5":
```json
{
"executor": "0xECE8e30bFc92c2A8e11e6cb2e17B70868572E3f6",
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"calls": [
{
"target": "0x303a465b659cbb0ab36ee643ea362c509eeb5213",
"value": "0x00",
"data": "0x79ba5097"
},
{
"target": "0xc2ee6b6af7d616f6e27ce7f4a451aedc2b0f5f5c",
"value": "0x00",
"data": "0x79ba5097"
},
{
"target": "0xd7f9f54194c633f36ccd5f3da84ad4a1c38cb2cb",
"value": "0x00",
"data": "0x79ba5097"
},
{
"target": "0x5d8ba173dc6c3c90c8f7c04c9288bef5fdbad06e",
"value": "0x00",
"data": "0x79ba5097"
},
{
"target": "0xf553e6d903aa43420ed7e3bc2313be9286a8f987",
"value": "0x00",
"data": "0x79ba5097"
}
]
}
```
**Proposal ID Calculation**
Run the script via:
```console
./proposal_id_zip5.sh
```
which returns
```console
Encoded `UpgradeProposal` struct: 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000ece8e30bfc92c2a8e11e6cb2e17b70868572e3f60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000320000000000000000000000000303a465b659cbb0ab36ee643ea362c509eeb521300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000000000000000000000000000c2ee6b6af7d616f6e27ce7f4a451aedc2b0f5f5c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000000000000000000000000000d7f9f54194c633f36ccd5f3da84ad4a1c38cb2cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba5097000000000000000000000000000000000000000000000000000000000000000000000000000000005d8ba173dc6c3c90c8f7c04c9288bef5fdbad06e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000000000000000000000000000f553e6d903aa43420ed7e3bc2313be9286a8f98700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000
Proposal ID: 0xa34bdc028de549c0fbd0374e64eb5977e78f62331f6a55f4f2211348c4902d13
```
## EIP-712-Based Off-Chain Message Safe Hashes
Configure the parameters in [`safe_hashes.sh`](./safe_hashes.sh), then run the script:
```console
./safe_hashes.sh
```
The key parameters to configure for an emergency upgrade are:
- `SAFE_MULTISIG_ADDRESS`: Set your Safe multisig address here.
- `PROPOSAL_ID`: Set the calculated proposal ID here.
**Example Output**
```console
Safe message: 0x924182c0ae655857518786673f3026f1c75b754ffbd2716b4af6d78f04745a31
Safe message hash: 0x0b24f0f27141c3cfddcb6748516b026182fba25945dc2b328f32aa0a02229633
Domain hash: 0x63127490E98CEB540DB8DCA78EB231476F5B4061DC5139E45031491BAE94ADDF
Message hash: 0x021DEF418DA3276B5F47AB23C16FFAEA6B962872D2DDF2EBCC88310E203273ED
```
## 💸 Donation
I am a strong advocate of the open-source and free software paradigm. However, if you feel my work deserves a donation, you can send it to this address: [`0xe9Fa0c8B5d7F79DeC36D3F448B1Ac4cEdedE4e69`](https://etherscan.io/address/0xe9Fa0c8B5d7F79DeC36D3F448B1Ac4cEdedE4e69). I can pledge that I will use this money to help fix more existing challenges in the Ethereum ecosystem 🤝.