GovernanceAdmin

Description:

Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/GovernanceAdmin.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
pragma solidity ^0.8.30;

import {GovernanceConstants, IGovernance} from "./interfaces/IGovernance.sol";
import {IMessageHandler} from "./interfaces/IMessageHandler.sol";
import {IPausable} from "./interfaces/IPausable.sol";
import {IUUPSUpgradeable} from "./interfaces/IUUPSUpgradeable.sol";

/**
 * @dev GovernanceAdmin smart contract is responsible for processing messages
 *      originated from Vara Network. It is used to change governance address,
 *      upgrade proxies and pause/unpause them.
 */
contract GovernanceAdmin is IMessageHandler, IGovernance {
    /**
     * @dev `uint8 discriminant` bit shift.
     */
    uint256 internal constant DISCRIMINANT_BIT_SHIFT = 248;
    /**
     * @dev `address proxy` bit shift.
     */
    uint256 internal constant PROXY_ADDRESS_BIT_SHIFT = 96;
    /**
     * @dev `address newImplementation` bit shift.
     */
    uint256 internal constant NEW_IMPLEMENTATION_BIT_SHIFT = 96;

    /**
     * @dev `DISCRIMINANT_SIZE` offset.
     */
    uint256 internal constant OFFSET1 = 1;
    /**
     * @dev `DISCRIMINANT_SIZE + PROXY_ADDRESS_SIZE` offset.
     */
    uint256 internal constant OFFSET2 = 21;

    bytes32 public governance;
    address public wrappedVara;
    address public messageQueue;
    address public erc20Manager;

    /**
     * @dev Initializes the GovernanceAdmin contract.
     * @param _governance The governance address (Vara Network address).
     * @param _messageQueue The message queue address.
     * @param _erc20Manager The ERC20Manager address.
     */
    constructor(bytes32 _governance, address _wrappedVara, address _messageQueue, address _erc20Manager) {
        governance = _governance;
        wrappedVara = _wrappedVara;
        messageQueue = _messageQueue;
        erc20Manager = _erc20Manager;
    }

    /**
     * @dev Handles message originated from Vara Network.
     * @param source Source of the message (`ActorId` from Vara Network).
     * @param payload Payload of the message (message from Vara Network).
     */
    function handleMessage(bytes32 source, bytes calldata payload) external {
        if (msg.sender != messageQueue) {
            revert InvalidSender();
        }

        if (source != governance) {
            revert InvalidSource();
        }

        if (!_tryParseAndApplyMessage(payload)) {
            revert InvalidPayload();
        }
    }

    /**
     * @dev Tries to parse and apply message originated from Vara Network.
     *
     *      Payload format:
     *      ```solidity
     *      uint8 discriminant;
     *      ```
     *
     *      `discriminant` can be:
     *      - `GovernanceConstants.CHANGE_GOVERNANCE = 0x00` - change governance address to `newGovernance`
     *          ```solidity
     *          bytes32 newGovernance;
     *          ```
     *
     *      - `GovernanceConstants.PAUSE_PROXY = 0x01` - pause `proxy`
     *          ```solidity
     *          address proxy;
     *          ```
     *
     *      - `GovernanceConstants.UNPAUSE_PROXY = 0x02` - unpause `proxy`
     *          ```solidity
     *          address proxy;
     *          ```
     *
     *      - `GovernanceConstants.UPGRADE_PROXY = 0x03` - upgrade `proxy` to `newImplementation` and call `data` on it
     *          ```solidity
     *          address proxy;
     *          address newImplementation;
     *          bytes data;
     *          ```
     *
     * @param payload Payload of the message (message from Vara Network).
     * @return success `true` if the message is parsed and applied, `false` otherwise.
     */
    function _tryParseAndApplyMessage(bytes calldata payload) private returns (bool) {
        if (!(payload.length > 0)) {
            return false;
        }

        uint256 discriminant;
        assembly ("memory-safe") {
            // `DISCRIMINANT_BIT_SHIFT` right bit shift is required to remove extra bits since `calldataload` returns `uint256`
            discriminant := shr(DISCRIMINANT_BIT_SHIFT, calldataload(payload.offset))
        }

        if (!(discriminant >= GovernanceConstants.CHANGE_GOVERNANCE
                    && discriminant <= GovernanceConstants.UPGRADE_PROXY)) {
            return false;
        }

        if (discriminant == GovernanceConstants.CHANGE_GOVERNANCE) {
            if (!(payload.length == GovernanceConstants.CHANGE_GOVERNANCE_SIZE)) {
                return false;
            }

            // we use offset `OFFSET1 = DISCRIMINANT_SIZE` to skip `uint8 discriminant`
            bytes32 newGovernance;
            assembly ("memory-safe") {
                newGovernance := calldataload(add(payload.offset, OFFSET1))
            }

            bytes32 previousGovernance = governance;
            governance = newGovernance;

            emit GovernanceChanged(previousGovernance, newGovernance);

            return true;
        }

        if (!(payload.length > GovernanceConstants.PROXY_ADDRESS_SIZE)) {
            return false;
        }

        // we use offset `OFFSET1 = DISCRIMINANT_SIZE` to skip `uint8 discriminant`
        address proxy;
        assembly ("memory-safe") {
            // `PROXY_ADDRESS_BIT_SHIFT` right bit shift is required to remove extra bits since `calldataload` returns `uint256`
            proxy := shr(PROXY_ADDRESS_BIT_SHIFT, calldataload(add(payload.offset, OFFSET1)))
        }

        if (!(proxy == wrappedVara || proxy == messageQueue || proxy == erc20Manager)) {
            return false;
        }

        if (discriminant >= GovernanceConstants.PAUSE_PROXY && discriminant <= GovernanceConstants.UNPAUSE_PROXY) {
            if (!(payload.length == GovernanceConstants.PAUSE_UNPAUSE_PROXY_SIZE)) {
                return false;
            }

            if (discriminant == GovernanceConstants.PAUSE_PROXY) {
                IPausable(proxy).pause();
            } else if (discriminant == GovernanceConstants.UNPAUSE_PROXY) {
                IPausable(proxy).unpause();
            }

            return true;
        }

        // `discriminant == GovernanceConstants.UPGRADE_PROXY` is guaranteed by previous checks
        if (!(payload.length >= GovernanceConstants.UPGRADE_PROXY_SIZE)) {
            return false;
        }

        // we use offset `OFFSET2 = DISCRIMINANT_SIZE + PROXY_ADDRESS_SIZE` to skip `uint8 discriminant` and `address proxy`
        address newImplementation;
        assembly ("memory-safe") {
            // `NEW_IMPLEMENTATION_BIT_SHIFT` right bit shift is required to remove extra bits since `calldataload` returns `uint256`
            newImplementation := shr(NEW_IMPLEMENTATION_BIT_SHIFT, calldataload(add(payload.offset, OFFSET2)))
        }
        // we use offset `OFFSET3 = DISCRIMINANT_SIZE + PROXY_ADDRESS_SIZE + NEW_IMPLEMENTATION_SIZE`
        // to skip `uint8 discriminant`, `address proxy` and `address newImplementation`
        // and get `bytes data`
        bytes calldata data = payload[GovernanceConstants.OFFSET3:];

        IUUPSUpgradeable(proxy).upgradeToAndCall(newImplementation, data);

        return true;
    }
}
"
    },
    "src/interfaces/IGovernance.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
pragma solidity ^0.8.30;

import {IMessageHandler} from "./IMessageHandler.sol";

/**
 * @dev Governance constants.
 */
library GovernanceConstants {
    /**
     * @dev Change governance message discriminant.
     */
    uint256 internal constant CHANGE_GOVERNANCE = 0x00;
    /**
     * @dev Pause proxy message discriminant.
     */
    uint256 internal constant PAUSE_PROXY = 0x01;
    /**
     * @dev Unpause proxy message discriminant.
     */
    uint256 internal constant UNPAUSE_PROXY = 0x02;
    /**
     * @dev Upgrade proxy message discriminant.
     */
    uint256 internal constant UPGRADE_PROXY = 0x03;

    /**
     * @dev `uint8 discriminant` size.
     */
    uint256 internal constant DISCRIMINANT_SIZE = 1;
    /**
     * @dev `bytes32 newGovernance` size.
     */
    uint256 internal constant NEW_GOVERNANCE_SIZE = 32;
    /**
     * @dev `address proxy` size.
     */
    uint256 internal constant PROXY_ADDRESS_SIZE = 20;
    /**
     * @dev `address newImplementation` size.
     */
    uint256 internal constant NEW_IMPLEMENTATION_SIZE = 20;

    /**
     * @dev `DISCRIMINANT_SIZE` offset.
     */
    uint256 internal constant OFFSET1 = 1;
    /**
     * @dev `DISCRIMINANT_SIZE + PROXY_ADDRESS_SIZE` offset.
     */
    uint256 internal constant OFFSET2 = 21;
    /**
     * @dev `DISCRIMINANT_SIZE + PROXY_ADDRESS_SIZE + NEW_IMPLEMENTATION_SIZE` offset.
     */
    uint256 internal constant OFFSET3 = 41;

    /**
     * @dev `DISCRIMINANT_SIZE + NEW_GOVERNANCE_SIZE` size.
     */
    uint256 internal constant CHANGE_GOVERNANCE_SIZE = 33;
    /**
     * @dev `DISCRIMINANT_SIZE + PROXY_ADDRESS_SIZE` size.
     */
    uint256 internal constant PAUSE_UNPAUSE_PROXY_SIZE = 21;
    /**
     * @dev `DISCRIMINANT_SIZE + PROXY_ADDRESS_SIZE + NEW_IMPLEMENTATION_SIZE` size.
     */
    uint256 internal constant UPGRADE_PROXY_SIZE = 41;
}

/**
 * @dev Interface for the Governance contract.
 */
interface IGovernance is IMessageHandler {
    /**
     * @dev Error thrown when the sender is not the message queue.
     */
    error InvalidSender();

    /**
     * @dev Error thrown when the source is not the governance.
     */
    error InvalidSource();

    /**
     * @dev Error thrown when the payload is invalid.
     */
    error InvalidPayload();

    /**
     * @dev Emitted when the governance address is changed.
     * @param previousGovernance The previous governance address.
     * @param newGovernance The new governance address.
     */
    event GovernanceChanged(bytes32 indexed previousGovernance, bytes32 indexed newGovernance);

    /**
     * @dev Returns the governance address.
     * @return governance The governance address.
     */
    function governance() external view returns (bytes32);

    /**
     * @dev Returns the WrappedVara address.
     * @return wrappedVara The WrappedVara address.
     */
    function wrappedVara() external view returns (address);

    /**
     * @dev Returns the MessageQueue address.
     * @return messageQueue The MessageQueue address.
     */
    function messageQueue() external view returns (address);

    /**
     * @dev Returns the ERC20Manager address.
     * @return erc20Manager The ERC20Manager address.
     */
    function erc20Manager() external view returns (address);
}

/**
 * @dev Type representing payload of the message that changes governance address.
 */
struct ChangeGovernanceMessage {
    bytes32 newGovernance;
}

/**
 * @dev Type representing payload of the message that pauses proxy.
 */
struct PauseProxyMessage {
    address proxy;
}

/**
 * @dev Type representing payload of the message that unpauses proxy.
 */
struct UnpauseProxyMessage {
    address proxy;
}

/**
 * @dev Type representing payload of the message that upgrades proxy.
 */
struct UpgradeProxyMessage {
    address proxy;
    address newImplementation;
    bytes data;
}

/**
 * @dev Library for packing `Governance` messages into a binary format.
 */
library GovernancePacker {
    /**
     * @dev Packs `ChangeGovernanceMessage` into a binary format.
     * @param message Message to pack.
     * @return packed Packed message.
     */
    function pack(ChangeGovernanceMessage memory message) internal pure returns (bytes memory) {
        return abi.encodePacked(uint8(GovernanceConstants.CHANGE_GOVERNANCE), message.newGovernance);
    }

    /**
     * @dev Packs `PauseProxyMessage` into a binary format.
     * @param message Message to pack.
     * @return packed Packed message.
     */
    function pack(PauseProxyMessage memory message) internal pure returns (bytes memory) {
        return abi.encodePacked(uint8(GovernanceConstants.PAUSE_PROXY), message.proxy);
    }

    /**
     * @dev Packs `UnpauseProxyMessage` into a binary format.
     * @param message Message to pack.
     * @return packed Packed message.
     */
    function pack(UnpauseProxyMessage memory message) internal pure returns (bytes memory) {
        return abi.encodePacked(uint8(GovernanceConstants.UNPAUSE_PROXY), message.proxy);
    }

    /**
     * @dev Packs `UpgradeProxyMessage` into a binary format.
     * @param message Message to pack.
     * @return packed Packed message.
     */
    function pack(UpgradeProxyMessage memory message) internal pure returns (bytes memory) {
        return abi.encodePacked(
            uint8(GovernanceConstants.UPGRADE_PROXY), message.proxy, message.newImplementation, message.data
        );
    }
}
"
    },
    "src/interfaces/IMessageHandler.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
pragma solidity ^0.8.30;

/**
 * @dev Interface for the message handler (messages from MessageQueue).
 */
interface IMessageHandler {
    /**
     * @dev Handles message originated from Vara Network.
     * @param source Source of the message (`ActorId` from Vara Network).
     * @param payload Payload of the message (message from Vara Network).
     */
    function handleMessage(bytes32 source, bytes calldata payload) external;
}
"
    },
    "src/interfaces/IPausable.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
pragma solidity ^0.8.30;

/**
 * @dev Interface for the pausable contracts.
 */
interface IPausable {
    /**
     * @dev Pauses the contract.
     */
    function pause() external;

    /**
     * @dev Unpauses the contract.
     */
    function unpause() external;
}
"
    },
    "src/interfaces/IUUPSUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
pragma solidity ^0.8.30;

/**
 * @dev Interface for the UUPSUpgradeable contract.
 */
interface IUUPSUpgradeable {
    /**
     * @dev Upgrades the implementation of the contract.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable;
}
"
    }
  },
  "settings": {
    "remappings": [
      "src/=src/",
      "test/=test/",
      "script/=script/",
      "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
      "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
      "forge-std/=lib/forge-std/src/",
      "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
      "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/",
      "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "none",
      "appendCBOR": false
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "prague",
    "viaIR": true,
    "debug": {
      "revertStrings": "strip"
    }
  }
}}

Tags:
Proxy, Upgradeable, Factory|addr:0x3681a3e25f5652389b8f52504d517e96352830c3|verified:true|block:23633483|tx:0xb2c33794588992c71a205dd7330528e5b1a04a0e6c47174e85cae0a881b34a61|first_check:1761244835

Submitted on: 2025-10-23 20:40:38

Comments

Log in to comment.

No comments yet.