pyth-crosschain/target-chains/ethereum/contracts/wormhole/Governance.sol

166 lines
4.9 KiB
Solidity

// contracts/Governance.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./Structs.sol";
import "./GovernanceStructs.sol";
import "./Messages.sol";
import "./Setters.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
abstract contract Governance is
GovernanceStructs,
Messages,
Setters,
ERC1967Upgrade
{
event ContractUpgraded(
address indexed oldContract,
address indexed newContract
);
event GuardianSetAdded(uint32 indexed index);
// "Core" (left padded)
bytes32 constant module =
0x00000000000000000000000000000000000000000000000000000000436f7265;
function submitContractUpgrade(bytes memory _vm) public {
Structs.VM memory vm = parseVM(_vm);
(bool isValid, string memory reason) = verifyGovernanceVM(vm);
require(isValid, reason);
GovernanceStructs.ContractUpgrade memory upgrade = parseContractUpgrade(
vm.payload
);
require(upgrade.module == module, "Invalid Module");
require(upgrade.chain == chainId(), "Invalid Chain");
setGovernanceActionConsumed(vm.hash);
upgradeImplementation(upgrade.newContract);
}
function submitSetMessageFee(bytes memory _vm) public {
Structs.VM memory vm = parseVM(_vm);
(bool isValid, string memory reason) = verifyGovernanceVM(vm);
require(isValid, reason);
GovernanceStructs.SetMessageFee memory upgrade = parseSetMessageFee(
vm.payload
);
require(upgrade.module == module, "Invalid Module");
require(upgrade.chain == chainId(), "Invalid Chain");
setGovernanceActionConsumed(vm.hash);
setMessageFee(upgrade.messageFee);
}
function submitNewGuardianSet(bytes memory _vm) public {
Structs.VM memory vm = parseVM(_vm);
(bool isValid, string memory reason) = verifyGovernanceVM(vm);
require(isValid, reason);
GovernanceStructs.GuardianSetUpgrade
memory upgrade = parseGuardianSetUpgrade(vm.payload);
require(upgrade.module == module, "invalid Module");
require(
upgrade.chain == chainId() || upgrade.chain == 0,
"invalid Chain"
);
require(
upgrade.newGuardianSet.keys.length > 0,
"new guardian set is empty"
);
require(
upgrade.newGuardianSetIndex == getCurrentGuardianSetIndex() + 1,
"index must increase in steps of 1"
);
setGovernanceActionConsumed(vm.hash);
expireGuardianSet(getCurrentGuardianSetIndex());
storeGuardianSet(upgrade.newGuardianSet, upgrade.newGuardianSetIndex);
updateGuardianSetIndex(upgrade.newGuardianSetIndex);
}
function submitTransferFees(bytes memory _vm) public {
Structs.VM memory vm = parseVM(_vm);
(bool isValid, string memory reason) = verifyGovernanceVM(vm);
require(isValid, reason);
GovernanceStructs.TransferFees memory transfer = parseTransferFees(
vm.payload
);
require(transfer.module == module, "invalid Module");
require(
transfer.chain == chainId() || transfer.chain == 0,
"invalid Chain"
);
setGovernanceActionConsumed(vm.hash);
address payable recipient = payable(
address(uint160(uint256(transfer.recipient)))
);
recipient.transfer(transfer.amount);
}
function upgradeImplementation(address newImplementation) internal {
address currentImplementation = _getImplementation();
_upgradeTo(newImplementation);
// Call initialize function of the new implementation
(bool success, bytes memory reason) = newImplementation.delegatecall(
abi.encodeWithSignature("initialize()")
);
require(success, string(reason));
emit ContractUpgraded(currentImplementation, newImplementation);
}
function verifyGovernanceVM(
Structs.VM memory vm
) internal view returns (bool, string memory) {
// validate vm
(bool isValid, string memory reason) = verifyVM(vm);
if (!isValid) {
return (false, reason);
}
// only current guardianset can sign governance packets
if (vm.guardianSetIndex != getCurrentGuardianSetIndex()) {
return (false, "not signed by current guardian set");
}
// verify source
if (uint16(vm.emitterChainId) != governanceChainId()) {
return (false, "wrong governance chain");
}
if (vm.emitterAddress != governanceContract()) {
return (false, "wrong governance contract");
}
// prevent re-entry
if (governanceActionIsConsumed(vm.hash)) {
return (false, "governance action already consumed");
}
return (true, "");
}
}