- Proposal ID Calculation for ZKsync
executeEmergencyUpgrades - EIP-712-Based Off-Chain Message Safe Hashes
- πΈ Donation
Note
The original gist can be found here.
The function executeEmergencyUpgrade will be invoked in the following contract:
ProtocolUpgradeHandler:0x8f7a9912416e8AdC4D9c21FAe1415D3318A11897
/// @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:
keccak256(abi.encode(_proposal));Note
Ensure that forge and cast are installed locally. For installation instructions, refer to this guide.
Adjust the executor, salt, and calls parameters either in ProposalIdGoLive.sol or proposal_id_go_live.sh and invoke
forge script ProposalIdGoLive.sol --target-contract ProposalIdGoLive --sig "computeProposalId()"or
./proposal_id_go_live.shThe proposal ID, given by the ZKsync Era UI is 0xdd9aadc3b6e3297fed40a2cf0a7e655ff5af02c9ce918ed0e86f538c1c53ce9d. So we need to verify that one.
From the docs here, we know:
bytes32 salt = 0x646563656e7472616c697a6174696f6e206973206e6f74206f7074696f6e616cThe executor in our case is 0xdEFd1eDEE3E8c5965216bd59C866f7f5307C9b29, the EmergencyUpgradeBoard contract.
Proposal ID Calculation
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.34;
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:
forge script ProposalIdGoLive.sol --target-contract ProposalIdGoLive --sig "computeProposalId()"which will output:
== Return ==
0: bytes32 0xdd9aadc3b6e3297fed40a2cf0a7e655ff5af02c9ce918ed0e86f538c1c53ce9dThe upgrade data for the emergency upgrade "Accept ownership after ZIP5":
{
"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:
./proposal_id_zip5.shwhich returns
Encoded `UpgradeProposal` struct: 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000ece8e30bfc92c2a8e11e6cb2e17b70868572e3f60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000320000000000000000000000000303a465b659cbb0ab36ee643ea362c509eeb521300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000000000000000000000000000c2ee6b6af7d616f6e27ce7f4a451aedc2b0f5f5c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000000000000000000000000000d7f9f54194c633f36ccd5f3da84ad4a1c38cb2cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba5097000000000000000000000000000000000000000000000000000000000000000000000000000000005d8ba173dc6c3c90c8f7c04c9288bef5fdbad06e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000000000000000000000000000f553e6d903aa43420ed7e3bc2313be9286a8f98700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000479ba509700000000000000000000000000000000000000000000000000000000
Proposal ID: 0xa34bdc028de549c0fbd0374e64eb5977e78f62331f6a55f4f2211348c4902d13Configure the parameters in safe_hashes.sh, then run the script:
./safe_hashes.shThe 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
Safe message: 0x924182c0ae655857518786673f3026f1c75b754ffbd2716b4af6d78f04745a31
Safe message hash: 0x0b24f0f27141c3cfddcb6748516b026182fba25945dc2b328f32aa0a02229633
Domain hash: 0x63127490E98CEB540DB8DCA78EB231476F5B4061DC5139E45031491BAE94ADDF
Message hash: 0x021DEF418DA3276B5F47AB23C16FFAEA6B962872D2DDF2EBCC88310E203273EDI 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. I can pledge that I will use this money to help fix more existing challenges in the Ethereum ecosystem π€.