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.
99 lines
3.8 KiB
99 lines
3.8 KiB
4 years ago
|
// SPDX-License-Identifier: MIT
|
||
3 years ago
|
pragma solidity >=0.8.0;
|
||
4 years ago
|
|
||
3 years ago
|
// ============ External Imports ============
|
||
|
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
||
4 years ago
|
|
||
|
/**
|
||
|
* @title UpgradeBeacon
|
||
3 years ago
|
* @notice Stores the address of an implementation contract
|
||
|
* and allows a controller to upgrade the implementation address
|
||
|
* @dev This implementation combines the gas savings of having no function selectors
|
||
4 years ago
|
* 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 {
|
||
3 years ago
|
// ============ Immutables ============
|
||
|
|
||
4 years ago
|
// The controller is capable of modifying the implementation address
|
||
|
address private immutable controller;
|
||
|
|
||
3 years ago
|
// ============ Private Storage Variables ============
|
||
|
|
||
|
// The implementation address is held in storage slot zero.
|
||
|
address private implementation;
|
||
|
|
||
|
// ============ Events ============
|
||
|
|
||
4 years ago
|
// Upgrade event is emitted each time the implementation address is set
|
||
|
// (including deployment)
|
||
|
event Upgrade(address indexed implementation);
|
||
|
|
||
3 years ago
|
// ============ Constructor ============
|
||
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @notice Validate the initial implementation and store it.
|
||
|
* Store the controller immutably.
|
||
|
* @param _initialImplementation Address of the initial implementation contract
|
||
|
* @param _controller Address of the controller who can upgrade the implementation
|
||
4 years ago
|
*/
|
||
|
constructor(address _initialImplementation, address _controller) payable {
|
||
|
_setImplementation(_initialImplementation);
|
||
|
controller = _controller;
|
||
|
}
|
||
|
|
||
3 years ago
|
// ============ External Functions ============
|
||
|
|
||
4 years ago
|
/**
|
||
3 years ago
|
* @notice For all callers except the controller, return the current implementation address.
|
||
|
* If called by the Controller, update the implementation address
|
||
|
* to the address passed in the calldata.
|
||
|
* Note: this requires inline assembly because Solidity fallback functions
|
||
|
* do not natively take arguments or return values.
|
||
4 years ago
|
*/
|
||
|
fallback() external payable {
|
||
|
if (msg.sender != controller) {
|
||
3 years ago
|
// if not called by the controller,
|
||
|
// load implementation address from storage slot zero
|
||
|
// and return it.
|
||
4 years ago
|
assembly {
|
||
|
mstore(0, sload(0))
|
||
|
return(0, 32)
|
||
|
}
|
||
|
} else {
|
||
3 years ago
|
// if called by the controller,
|
||
|
// load new implementation address from the first word of the calldata
|
||
4 years ago
|
address _newImplementation;
|
||
|
assembly {
|
||
|
_newImplementation := calldataload(0)
|
||
|
}
|
||
3 years ago
|
// set the new implementation
|
||
4 years ago
|
_setImplementation(_newImplementation);
|
||
|
}
|
||
|
}
|
||
|
|
||
3 years ago
|
// ============ Private Functions ============
|
||
|
|
||
4 years ago
|
/**
|
||
|
* @notice Perform checks on the new implementation address
|
||
|
* then upgrade the stored implementation.
|
||
3 years ago
|
* @param _newImplementation Address of the new implementation contract which will replace the old one
|
||
4 years ago
|
*/
|
||
|
function _setImplementation(address _newImplementation) private {
|
||
3 years ago
|
// 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"
|
||
|
);
|
||
|
// set the new implementation
|
||
4 years ago
|
implementation = _newImplementation;
|
||
|
emit Upgrade(_newImplementation);
|
||
|
}
|
||
|
}
|