Sonic Blaze Testnet

Contract Diff Checker

Contract Name:
InitialProxyImplementationWithOwner

Contract Source Code:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

library AddressUtil {
    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

/**
 * @title Contract for facilitating ownership by a single address, using the Openzeppelin ownable upgradable storage slot.
 * @dev Implementation is a modified version of Openzeppelin and Synthetix Ownable implementations.
 */
interface IOwnable {
    /*///////////////////////////////////////////////////////////////
                                ERRORS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Thrown when an address tries to accept ownership but has not been nominated.
     * @param addr The address that is trying to accept ownership.
     */
    error NotNominated(address addr);

    /**
     * @notice Thrown when an address tries to renounce pending ownership but not owner or pending owner.
     * @param addr The address that is trying to renounce pending ownership.
     */
    error NotNominatedOrOwner(address addr);

    /**
     * @notice Thrown when an address is zero.
     */
    error ZeroAddress();

    /**
     * @notice Thrown when no change is made.
     */
    error NoChange();

    /**
     * @notice Thrown when an address is not the owner.
     */
    error Unauthorized(address addr);

    /*///////////////////////////////////////////////////////////////
                                EVENTS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Emitted when an address has been nominated.
     * @param newOwner The address that has been nominated.
     */
    event OwnerNominated(address newOwner);

    /**
     * @notice Emitted when the owner of the contract has changed.
     * @param oldOwner The previous owner of the contract.
     * @param newOwner The new owner of the contract.
     */
    event OwnerChanged(address oldOwner, address newOwner);

    /**
     * @notice Emitted when the nominated pending owner renounces themselves as nominated.
     * @param pendingOwner The pending owner that is renounced.
     */
    event PendingOwnerRenounced(address pendingOwner);

    /*///////////////////////////////////////////////////////////////
                        VIEW FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the current owner of the contract.
     */
    function owner() external view returns (address);

    /**
     * @notice Returns the current pending owner of the contract.
     * @dev Only one address can be pending at a time.
     */
    function pendingOwner() external view returns (address);

    /*///////////////////////////////////////////////////////////////
                        MUTATIVE FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Allows a pending owner address to accept ownership of the contract.
     * @dev Reverts if the caller has not been nominated.
     */
    function acceptOwnership() external;

    /**
     * @notice Allows the current owner to nominate a new owner.
     * @dev The pending owner will have to call `acceptOwnership` in a separate transaction in order to finalize the action and become the new contract owner.
     * @param newOwner The address that is to become nominated.
     */
    function transferOwnership(address newOwner) external;

    /**
     * @notice Allows a pending owner to reject the nomination.
     */
    function renouncePendingOwnership() external;

    /**
     * @notice Allows the current owner of the contract to renounce the ownership and pending ownership completely.
     */
    function renounceOwnership() external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import { IOwnable } from "src/ownership/IOwnable.sol";

import { OwnableStorage } from "src/ownership/OwnableStorage.sol";

/**
 * @title Contract for facilitating ownership by a single address, using the Openzeppelin ownable upgradable storage slot.
 * @dev Implementation is a modified version of Openzeppelin and Synthetix Ownable implementations.
 */
abstract contract Ownable is IOwnable {
    /*///////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    ///////////////////////////////////////////////////////////////*/

    constructor(address initialOwner) {
        if (initialOwner == address(0)) revert ZeroAddress();
        OwnableStorage._setOwner(initialOwner);
    }

    /*///////////////////////////////////////////////////////////////
                            MODIFIERS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Reverts if the caller is not the owner.
     */
    modifier onlyOwner() {
        _onlyOwner();

        _;
    }

    /*///////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the current owner of the contract.
     */
    function owner() external view virtual returns (address) {
        return _owner();
    }

    /**
     * @notice Returns the current pending owner of the contract.
     * @dev Only one address can be pending at a time.
     */
    function pendingOwner() external view virtual returns (address) {
        return _pendingOwner();
    }

    /*///////////////////////////////////////////////////////////////
                            INTERNAL FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the current owner of the contract.
     */
    function _owner() internal view virtual returns (address) {
        return OwnableStorage._getOwner();
    }

    /**
     * @notice Returns the current pending owner of the contract.
     * @dev Only one address can be nominated at a time.
     */
    function _pendingOwner() internal view virtual returns (address) {
        return OwnableStorage._getPendingOwner();
    }

    /**
     * @notice Reverts if the caller is not the owner.
     */
    function _onlyOwner() internal view virtual {
        if (msg.sender != _owner()) {
            revert Unauthorized(msg.sender);
        }
    }

    /*///////////////////////////////////////////////////////////////
                        MUTATIVE FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Allows a pending owner to accept ownership of the contract.
     * @dev Reverts if the caller is not pending owner.
     */
    function acceptOwnership() public virtual {
        address currentPendingOwner = _pendingOwner();

        if (msg.sender != currentPendingOwner) {
            revert NotNominated(msg.sender);
        }

        emit OwnerChanged(_owner(), currentPendingOwner);
        OwnableStorage._setOwner(currentPendingOwner);
        OwnableStorage._setPendingOwner(address(0));
    }

    /**
     * @notice Allows the current owner to nominate a new owner.
     * @dev The pending owner will have to call `acceptOwnership` in a separate transaction in order to finalize the action and become the new contract owner.
     * @param newOwner The address that is to become nominated.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert ZeroAddress();
        }

        if (newOwner == _pendingOwner()) {
            revert NoChange();
        }

        emit OwnerNominated(newOwner);
        OwnableStorage._setPendingOwner(newOwner);
    }

    /**
     * @notice Allows a pending owner or owner to reject the nomination.
     */
    function renouncePendingOwnership() external virtual {
        address pendingOwner = _pendingOwner();

        if (pendingOwner != msg.sender && msg.sender != _owner()) {
            revert NotNominatedOrOwner(msg.sender);
        }

        emit PendingOwnerRenounced(pendingOwner);
        OwnableStorage._setPendingOwner(address(0));
    }

    /**
     * @notice Allows the current owner of the contract to renounce the ownership and pending ownership completely.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit PendingOwnerRenounced(_pendingOwner());
        OwnableStorage._setPendingOwner(address(0));

        emit OwnerChanged(_owner(), address(0));
        OwnableStorage._setOwner(address(0));
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

/**
 * @title Ownable Storage
 * @dev This library provides storage and functions for managing the ownership of a contract, using the Openzeppelin ownable upgradable storage slot.
 * Implementation is a modified version of Openzeppelin and Synthetix Ownable implementations.
 */
library OwnableStorage {
    // Storage slot copied from the Openzeppelin ownable upgradable contract.
    // https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/OwnableUpgradeable.sol
    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant _SLOT_OWNABLE_STORAGE = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; //#gitleaks:allow

    struct Storage {
        address _owner;
        address _pendingOwner;
    }

    /**
     * @notice Loads the storage data for the Ownable contract
     * @return store The storage data for the Ownable contract
     */
    function getStorage() internal pure returns (Storage storage store) {
        bytes32 s = _SLOT_OWNABLE_STORAGE;
        assembly {
            store.slot := s
        }
    }

    /**
     * @notice Returns the current owner of the contract
     * @return The address of the owner
     */
    function _getOwner() internal view returns (address) {
        return getStorage()._owner;
    }

    /**
     * @notice Returns the pending owner of the contract.
     * @return The address of the pending owner.
     */
    function _getPendingOwner() internal view returns (address) {
        return getStorage()._pendingOwner;
    }

    /**
     * @notice Sets the owner in ownable storage.
     * @param _newOwner The new owner of the contract.
     */
    function _setOwner(address _newOwner) internal {
        getStorage()._owner = _newOwner;
    }

    /**
     * @notice Sets the pending owner.
     * @param _newOwner The new pending owner of the contract.
     */
    function _setPendingOwner(address _newOwner) internal {
        getStorage()._pendingOwner = _newOwner;
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

/**
 * @title Contract to be used as the implementation of a Universal Upgradeable Proxy Standard (UUPS) proxy.
 * Important: A UUPS proxy requires its upgradeability functions to be in the implementation as opposed to the proxy.
 * This means that if the proxy is upgraded to an implementation that does not support this interface, it will no longer be upgradeable.
 * Copied from Synthetix
 * https://github.com/Synthetixio/synthetix-v3/blob/main/utils/core-contracts/contracts/interfaces/IUUPSImplementation.sol
 */
interface IUUPSImplementation {
    /**
     * @notice Thrown when an incoming implementation will not be able to receive future upgrades.
     */
    error ImplementationIsSterile(address implementation);

    /**
     * @notice Thrown intentionally when testing future upgradeability of an implementation.
     */
    error UpgradeSimulationFailed();

    /**
     * @notice Thrown when the address is zero.
     */
    error NullAddress();

    /**
     * @notice Thrown when the address is not a contract.
     */
    error NotAContract(address implementation);

    /**
     * @notice Thrown when the implementation is the same as the current implementation.
     */
    error SameImplementation();

    /**
     * @notice Emitted when the implementation of the proxy has been upgraded.
     * @param self The address of the proxy whose implementation was upgraded.
     * @param implementation The address of the proxy's new implementation.
     */
    event Upgraded(address indexed self, address implementation);

    /**
     * @notice Allows the proxy to be upgraded to a new implementation.
     * @param newImplementation The address of the proxy's new implementation.
     * @dev Will revert if `newImplementation` is not upgradeable.
     * @dev The implementation of this function needs to be protected by some sort of access control such as `onlyOwner`.
     */
    function upgradeTo(address newImplementation) external;

    /**
     * @notice Function used to determine if a new implementation will be able to receive future upgrades in `upgradeTo`.
     * @param newImplementation The address of the new implementation being tested for future upgradeability.
     * @dev This function will always revert, but will revert with different error messages. The function `upgradeTo` uses this error to determine the future upgradeability of the implementation in question.
     */
    function simulateUpgradeTo(address newImplementation) external;

    /**
     * @notice Retrieves the current implementation of the proxy.
     * @return The address of the current implementation.
     */
    function getImplementation() external view returns (address);
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import { UUPSImplementation } from "src/proxy/UUPSImplementation.sol";
import { Ownable } from "src/ownership/Ownable.sol";

/**
 * @dev The below contract is only used as an intermediary implementation contract
 * when initializing the proxy to make sure that, when deployed using CREATE2,
 * we always get the same contract address since the bytecode is deterministic.
 */
contract InitialProxyImplementationWithOwner is UUPSImplementation, Ownable {
    constructor() Ownable(address(this)) { }

    function upgradeTo(address newImplementation) public override {
        _onlyOwner();

        // If the current implementation is the same as the new one, we return blank to avoid the NoChange Error event.
        // This is helpful when trying to build the package for the first time, so that we can smoothly upgrade the proxy impl
        if (newImplementation == this.getImplementation()) {
            emit Upgraded(address(this), newImplementation);
            return;
        }

        _upgradeTo(newImplementation);
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

/**
 * @dev Modified Synthetix proxy storage contract.
 * https://github.com/Synthetixio/synthetix-v3/blob/main/utils/core-contracts/contracts/proxy/ProxyStorage.sol
 */
contract ProxyStorage {
    // keccak256(abi.encode("io.synthetix.core-contracts.Proxy"));
    bytes32 private constant _SLOT_PROXY_STORAGE = 0x5a648c35a2f5512218b4683cf10e03f5b7c9dc7346e1bf77d304ae97f60f592b; //#gitleaks:allow

    struct ProxyStore {
        address implementation;
        bool simulatingUpgrade;
    }

    function _proxyStore() internal pure returns (ProxyStore storage store) {
        bytes32 s = _SLOT_PROXY_STORAGE;
        assembly {
            store.slot := s
        }
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import { IUUPSImplementation } from "src/proxy/IUUPSImplementation.sol";
import { AddressUtil } from "src/libraries/AddressUtil.sol";
import { ProxyStorage } from "src/proxy/ProxyStorage.sol";

/**
 * @dev Modified Synthetix UUPS Implementation contract
 * https://github.com/Synthetixio/synthetix-v3/blob/main/utils/core-contracts/contracts/proxy/UUPSImplementation.sol
 */
abstract contract UUPSImplementation is IUUPSImplementation, ProxyStorage {
    /**
     * @notice Function used to determine if a new implementation will be able to receive future upgrades in `upgradeTo`.
     * @param newImplementation The address of the new implementation being tested for future upgradeability.
     * @dev This function will always revert, but will revert with different error messages. The function `upgradeTo` uses this error to determine the future upgradeability of the implementation in question.
     */
    function simulateUpgradeTo(address newImplementation) public override {
        if (newImplementation == address(0)) {
            revert NullAddress();
        }

        ProxyStore storage store = _proxyStore();
        store.simulatingUpgrade = true;
        address currentImplementation = store.implementation;
        store.implementation = newImplementation;

        // slither-disable-start controlled-delegatecall
        // solhint-disable-next-line avoid-low-level-calls
        (bool rollbackSuccessful,) = newImplementation.delegatecall(abi.encodeCall(this.upgradeTo, (currentImplementation)));
        // slither-disable-end controlled-delegatecall

        if (!rollbackSuccessful || _proxyStore().implementation != currentImplementation) {
            revert UpgradeSimulationFailed();
        }

        store.simulatingUpgrade = false;

        // solhint-disable-next-line reason-string,gas-custom-errors
        revert();
    }

    /**
     * @notice Retrieves the current implementation of the proxy.
     * @return The address of the current implementation.
     */
    function getImplementation() external view override returns (address) {
        return _proxyStore().implementation;
    }

    /**
     * @notice Allows the proxy to be upgraded to a new implementation.
     * @param newImplementation The address of the new implementation.
     * @dev Will revert if `newImplementation` is not upgradeable.
     * @dev The implementation of this function needs to be protected by some sort of access control such as `onlyOwner`.
     */
    function _upgradeTo(address newImplementation) internal virtual {
        if (newImplementation == address(0)) {
            revert NullAddress();
        }

        if (!AddressUtil.isContract(newImplementation)) {
            revert NotAContract(newImplementation);
        }

        ProxyStore storage store = _proxyStore();

        if (newImplementation == store.implementation) {
            revert SameImplementation();
        }

        if (!store.simulatingUpgrade && _implementationIsSterile(newImplementation)) {
            revert ImplementationIsSterile(newImplementation);
        }

        store.implementation = newImplementation;

        emit Upgraded(address(this), newImplementation);
    }

    /**
     * @notice Checks if the candidate implementation is sterile.
     * @param candidateImplementation The address of the candidate implementation.
     * @return True if the candidate implementation is sterile, false otherwise.
     */
    function _implementationIsSterile(address candidateImplementation) internal virtual returns (bool) {
        (bool simulationReverted, bytes memory simulationResponse) =
        // solhint-disable-next-line avoid-low-level-calls
         address(this).delegatecall(abi.encodeCall(this.simulateUpgradeTo, (candidateImplementation)));

        return !simulationReverted
            && keccak256(abi.encodePacked(simulationResponse)) == keccak256(abi.encodePacked(UpgradeSimulationFailed.selector));
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):