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.
177 lines
7.0 KiB
177 lines
7.0 KiB
4 years ago
|
// SPDX-License-Identifier: MIT
|
||
|
pragma solidity >=0.6.11;
|
||
|
|
||
3 years ago
|
// ============ External Imports ============
|
||
|
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
||
4 years ago
|
|
||
|
/**
|
||
|
* @title UpgradeBeaconProxy
|
||
|
* @notice
|
||
3 years ago
|
* Proxy contract which delegates all logic, including initialization,
|
||
|
* to an implementation contract.
|
||
|
* The implementation contract is stored within an Upgrade Beacon contract;
|
||
4 years ago
|
* the implementation contract can be changed by performing an upgrade on the Upgrade Beacon contract.
|
||
|
* The Upgrade Beacon contract for this Proxy is immutably specified at deployment.
|
||
3 years ago
|
* @dev This implementation combines the gas savings of keeping the UpgradeBeacon address outside of contract storage
|
||
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 safety checks that the UpgradeBeacon and implementation are contracts at time of deployment
|
||
|
* found in OpenZeppelin's implementation:
|
||
|
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/beacon/BeaconProxy.sol
|
||
|
*/
|
||
|
contract UpgradeBeaconProxy {
|
||
3 years ago
|
// ============ Immutables ============
|
||
|
|
||
4 years ago
|
// Upgrade Beacon address is immutable (therefore not kept in contract storage)
|
||
|
address private immutable upgradeBeacon;
|
||
|
|
||
3 years ago
|
// ============ Constructor ============
|
||
|
|
||
4 years ago
|
/**
|
||
|
* @notice Validate that the Upgrade Beacon is a contract, then set its
|
||
|
* address immutably within this contract.
|
||
|
* Validate that the implementation is also a contract,
|
||
|
* Then call the initialization function defined at the implementation.
|
||
|
* The deployment will revert and pass along the
|
||
|
* revert reason if the initialization function reverts.
|
||
3 years ago
|
* @param _upgradeBeacon Address of the Upgrade Beacon to be stored immutably in the contract
|
||
|
* @param _initializationCalldata Calldata supplied when calling the initialization function
|
||
4 years ago
|
*/
|
||
|
constructor(address _upgradeBeacon, bytes memory _initializationCalldata)
|
||
|
payable
|
||
|
{
|
||
|
// Validate the Upgrade Beacon is a contract
|
||
|
require(Address.isContract(_upgradeBeacon), "beacon !contract");
|
||
|
// set the Upgrade Beacon
|
||
|
upgradeBeacon = _upgradeBeacon;
|
||
|
// Validate the implementation is a contract
|
||
4 years ago
|
address _implementation = _getImplementation(_upgradeBeacon);
|
||
4 years ago
|
require(
|
||
4 years ago
|
Address.isContract(_implementation),
|
||
4 years ago
|
"beacon implementation !contract"
|
||
|
);
|
||
|
// Call the initialization function on the implementation
|
||
|
if (_initializationCalldata.length > 0) {
|
||
4 years ago
|
_initialize(_implementation, _initializationCalldata);
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
3 years ago
|
// ============ External Functions ============
|
||
|
|
||
4 years ago
|
/**
|
||
|
* @notice Forwards all calls with data to _fallback()
|
||
|
* No public functions are declared on the contract, so all calls hit fallback
|
||
|
*/
|
||
|
fallback() external payable {
|
||
|
_fallback();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Forwards all calls with no data to _fallback()
|
||
|
*/
|
||
|
receive() external payable {
|
||
|
_fallback();
|
||
|
}
|
||
|
|
||
3 years ago
|
// ============ Private Functions ============
|
||
|
|
||
4 years ago
|
/**
|
||
|
* @notice Call the initialization function on the implementation
|
||
|
* Used at deployment to initialize the proxy
|
||
|
* based on the logic for initialization defined at the implementation
|
||
4 years ago
|
* @param _implementation - Contract to which the initalization is delegated
|
||
4 years ago
|
* @param _initializationCalldata - Calldata supplied when calling the initialization function
|
||
|
*/
|
||
|
function _initialize(
|
||
4 years ago
|
address _implementation,
|
||
4 years ago
|
bytes memory _initializationCalldata
|
||
|
) private {
|
||
|
// Delegatecall into the implementation, supplying initialization calldata.
|
||
4 years ago
|
(bool _ok, ) = _implementation.delegatecall(_initializationCalldata);
|
||
4 years ago
|
// Revert and include revert data if delegatecall to implementation reverts.
|
||
4 years ago
|
if (!_ok) {
|
||
4 years ago
|
assembly {
|
||
|
returndatacopy(0, 0, returndatasize())
|
||
|
revert(0, returndatasize())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Delegates function calls to the implementation contract returned by the Upgrade Beacon
|
||
|
*/
|
||
|
function _fallback() private {
|
||
4 years ago
|
_delegate(_getImplementation());
|
||
4 years ago
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Delegate function execution to the implementation contract
|
||
3 years ago
|
* @dev This is a low level function that doesn't return to its internal
|
||
4 years ago
|
* call site. It will return whatever is returned by the implementation to the
|
||
|
* external caller, reverting and returning the revert data if implementation
|
||
|
* reverts.
|
||
4 years ago
|
* @param _implementation - Address to which the function execution is delegated
|
||
4 years ago
|
*/
|
||
4 years ago
|
function _delegate(address _implementation) private {
|
||
4 years ago
|
assembly {
|
||
|
// Copy msg.data. We take full control of memory in this inline assembly
|
||
|
// block because it will not return to Solidity code. We overwrite the
|
||
|
// Solidity scratch pad at memory position 0.
|
||
|
calldatacopy(0, 0, calldatasize())
|
||
|
// Delegatecall to the implementation, supplying calldata and gas.
|
||
|
// Out and outsize are set to zero - instead, use the return buffer.
|
||
|
let result := delegatecall(
|
||
|
gas(),
|
||
4 years ago
|
_implementation,
|
||
4 years ago
|
0,
|
||
|
calldatasize(),
|
||
|
0,
|
||
|
0
|
||
|
)
|
||
|
// Copy the returned data from the return buffer.
|
||
|
returndatacopy(0, 0, returndatasize())
|
||
|
switch result
|
||
3 years ago
|
// Delegatecall returns 0 on error.
|
||
|
case 0 {
|
||
|
revert(0, returndatasize())
|
||
|
}
|
||
|
default {
|
||
|
return(0, returndatasize())
|
||
|
}
|
||
4 years ago
|
}
|
||
|
}
|
||
4 years ago
|
|
||
|
/**
|
||
|
* @notice Call the Upgrade Beacon to get the current implementation contract address
|
||
3 years ago
|
* @return _implementation Address of the current implementation.
|
||
4 years ago
|
*/
|
||
4 years ago
|
function _getImplementation()
|
||
|
private
|
||
|
view
|
||
|
returns (address _implementation)
|
||
|
{
|
||
|
_implementation = _getImplementation(upgradeBeacon);
|
||
4 years ago
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Call the Upgrade Beacon to get the current implementation contract address
|
||
3 years ago
|
* @dev _upgradeBeacon is passed as a parameter so that
|
||
|
* we can also use this function in the constructor,
|
||
|
* where we can't access immutable variables.
|
||
|
* @param _upgradeBeacon Address of the UpgradeBeacon storing the current implementation
|
||
|
* @return _implementation Address of the current implementation.
|
||
4 years ago
|
*/
|
||
4 years ago
|
function _getImplementation(address _upgradeBeacon)
|
||
4 years ago
|
private
|
||
|
view
|
||
4 years ago
|
returns (address _implementation)
|
||
4 years ago
|
{
|
||
|
// Get the current implementation address from the upgrade beacon.
|
||
4 years ago
|
(bool _ok, bytes memory _returnData) = _upgradeBeacon.staticcall("");
|
||
4 years ago
|
// Revert and pass along revert message if call to upgrade beacon reverts.
|
||
4 years ago
|
require(_ok, string(_returnData));
|
||
4 years ago
|
// Set the implementation to the address returned from the upgrade beacon.
|
||
4 years ago
|
_implementation = abi.decode(_returnData, (address));
|
||
4 years ago
|
}
|
||
4 years ago
|
}
|