The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hyperlane-monorepo/solidity/optics-core/contracts/upgrade/UpgradeBeacon.sol

93 lines
3.4 KiB

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;
import "@openzeppelin/contracts/utils/Address.sol";
/**
* @title UpgradeBeacon
*
* @notice
* This contract stores the address of an implementation contract
* and allows the controller to change the implementation address
*
* This implementation combines the gas savings of having no function selectors
* found in 0age's implementation:
* https://github.com/dharma-eng/dharma-smart-wallet/blob/master/contracts/proxies/smart-wallet/UpgradeBeaconProxyV1.sol
* With the added niceties of a safety check that each implementation is a contract
* and an Upgrade event emitted each time the implementation is changed
* found in OpenZeppelin's implementation:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/beacon/BeaconProxy.sol
*/
contract UpgradeBeacon {
// The implementation address is held in storage slot zero.
address private implementation;
// The controller is capable of modifying the implementation address
address private immutable controller;
// Upgrade event is emitted each time the implementation address is set
// (including deployment)
event Upgrade(address indexed implementation);
/**
* @notice Validate that the initial implementation is a contract,
* then store it in the contract.
* Validate that the controller is also a contract,
* Then store it immutably in this contract.
*
* @param _initialImplementation - Address of the initial implementation contract
* @param _controller - Address of the controller to be stored immutably in the contract
*/
constructor(address _initialImplementation, address _controller) payable {
_setImplementation(_initialImplementation);
controller = _controller;
}
/**
* @notice In the fallback function, allow only the controller to update the
* implementation address - for all other callers, return the current address.
* Note that this requires inline assembly, as Solidity fallback functions do
* not natively take arguments or return values.
*/
fallback() external payable {
// Return implementation address for all callers other than the controller.
if (msg.sender != controller) {
// Load implementation from storage slot zero into memory and return it.
assembly {
mstore(0, sload(0))
return(0, 32)
}
} else {
// Load new implementation from the first word of the calldata
address _newImplementation;
assembly {
_newImplementation := calldataload(0)
}
_setImplementation(_newImplementation);
}
}
/**
* @notice Perform checks on the new implementation address
* then upgrade the stored implementation.
*
* @param _newImplementation - Address of the new implementation contract which will replace the old one
*/
function _setImplementation(address _newImplementation) private {
// Require that the new implementation is different from the current one
require(implementation != _newImplementation, "!upgrade");
// Require that the new implementation is a contract
require(
Address.isContract(_newImplementation),
"implementation !contract"
);
implementation = _newImplementation;
emit Upgrade(_newImplementation);
}
}