mirror of
https://github.com/Instadapp/infinite-proxy.git
synced 2024-07-29 21:47:49 +00:00
intial commit 🚀
This commit is contained in:
parent
51e654abd2
commit
5072538f22
12
README.md
12
README.md
|
@ -1 +1,11 @@
|
||||||
# infinite-proxy
|
# infinite-proxy (Infinite Extendable Proxy)
|
||||||
|
|
||||||
|
Upgradable proxy with infinite implementations enabled at once.
|
||||||
|
|
||||||
|
Read about general upgradable contacts with 1 implementation contract [here](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
- Creates a mapping from bytes4 sig to implementation's address
|
||||||
|
- Stores mapping from implementation's address to bytes4[] sigs. All the external functions we want to be callable from our contract.
|
||||||
|
- Every call (other than addition & removal of implementation & sigs) goes through fallback.
|
||||||
|
- In fallback it fetches the msg.sig, fetches the implementation from it and run the code logic on that.
|
15
events.sol
Normal file
15
events.sol
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
contract Events {
|
||||||
|
event setAdminLog(address oldAdmin_, address newAdmin_);
|
||||||
|
|
||||||
|
event setDummyImplementationLog(
|
||||||
|
address oldDummyImplementation_,
|
||||||
|
address newDummyImplementation_
|
||||||
|
);
|
||||||
|
|
||||||
|
event setImplementationLog(address implementation_, bytes4[] sigs_);
|
||||||
|
|
||||||
|
event removeImplementationLog(address implementation_);
|
||||||
|
}
|
327
proxy.sol
Normal file
327
proxy.sol
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "./events.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
|
||||||
|
* instruction `delegatecall`.
|
||||||
|
*/
|
||||||
|
contract Internals is Events {
|
||||||
|
struct AddressSlot {
|
||||||
|
address value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SigsSlot {
|
||||||
|
bytes4[] value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Storage slot with the admin of the contract.
|
||||||
|
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
|
||||||
|
* validated in the constructor.
|
||||||
|
*/
|
||||||
|
bytes32 internal constant _ADMIN_SLOT =
|
||||||
|
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Storage slot with the address of the current dummy-implementation.
|
||||||
|
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
|
||||||
|
* validated in the constructor.
|
||||||
|
*/
|
||||||
|
bytes32 internal constant _DUMMY_IMPLEMENTATION_SLOT =
|
||||||
|
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||||
|
|
||||||
|
function _getSlotImplSigsSlot(address implementation_)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes32)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
keccak256(
|
||||||
|
abi.encode("eip1967.proxy.implementation", implementation_)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getSlotSigsImplSlot(bytes4 sig_) internal pure returns (bytes32) {
|
||||||
|
return keccak256(abi.encode("eip1967.proxy.implementation", sig_));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
|
||||||
|
*/
|
||||||
|
function getAddressSlot(bytes32 slot_)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (AddressSlot storage _r)
|
||||||
|
{
|
||||||
|
assembly {
|
||||||
|
_r.slot := slot_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns an `SigsSlot` with member `value` located at `slot`.
|
||||||
|
*/
|
||||||
|
function getSigsSlot(bytes32 slot_)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (SigsSlot storage _r)
|
||||||
|
{
|
||||||
|
assembly {
|
||||||
|
_r.slot := slot_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Sets new implementation and adds mapping from implementation to sigs and sig to implementation.
|
||||||
|
*/
|
||||||
|
function _setImplementationSigs(
|
||||||
|
address implementation_,
|
||||||
|
bytes4[] memory sigs_
|
||||||
|
) internal {
|
||||||
|
require(sigs_.length != 0, "no-sigs");
|
||||||
|
bytes32 slot_ = _getSlotImplSigsSlot(implementation_);
|
||||||
|
bytes4[] memory sigsCheck_ = getSigsSlot(slot_).value;
|
||||||
|
require(sigsCheck_.length == 0, "implementation-already-exist");
|
||||||
|
for (uint256 i = 0; i < sigs_.length; i++) {
|
||||||
|
bytes32 sigSlot_ = _getSlotSigsImplSlot(sigs_[i]);
|
||||||
|
require(
|
||||||
|
getAddressSlot(sigSlot_).value == address(0),
|
||||||
|
"sig-already-exist"
|
||||||
|
);
|
||||||
|
getAddressSlot(sigSlot_).value = implementation_;
|
||||||
|
}
|
||||||
|
getSigsSlot(slot_).value = sigs_;
|
||||||
|
emit setImplementationLog(implementation_, sigs_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev removes implementation and the mappings corresponding to it.
|
||||||
|
*/
|
||||||
|
function _removeImplementationSigs(address implementation_) internal {
|
||||||
|
bytes32 slot_ = _getSlotImplSigsSlot(implementation_);
|
||||||
|
bytes4[] memory sigs_ = getSigsSlot(slot_).value;
|
||||||
|
require(sigs_.length != 0, "implementation-not-exist");
|
||||||
|
for (uint256 i = 0; i < sigs_.length; i++) {
|
||||||
|
bytes32 sigSlot_ = _getSlotSigsImplSlot(sigs_[i]);
|
||||||
|
delete getAddressSlot(sigSlot_).value;
|
||||||
|
}
|
||||||
|
delete getSigsSlot(slot_).value;
|
||||||
|
emit removeImplementationLog(implementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getImplementationSigs(address implementation_)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (bytes4[] memory)
|
||||||
|
{
|
||||||
|
bytes32 slot_ = _getSlotImplSigsSlot(implementation_);
|
||||||
|
return getSigsSlot(slot_).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getSigImplementation(bytes4 sig_)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address implementation_)
|
||||||
|
{
|
||||||
|
bytes32 slot_ = _getSlotSigsImplSlot(sig_);
|
||||||
|
return getAddressSlot(slot_).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the current admin.
|
||||||
|
*/
|
||||||
|
function _getAdmin() internal view returns (address) {
|
||||||
|
return getAddressSlot(_ADMIN_SLOT).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the current dummy-implementation.
|
||||||
|
*/
|
||||||
|
function _getDummyImplementation() internal view returns (address) {
|
||||||
|
return getAddressSlot(_DUMMY_IMPLEMENTATION_SLOT).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Stores a new address in the EIP1967 admin slot.
|
||||||
|
*/
|
||||||
|
function _setAdmin(address newAdmin_) internal {
|
||||||
|
address oldAdmin_ = _getAdmin();
|
||||||
|
require(
|
||||||
|
newAdmin_ != address(0),
|
||||||
|
"ERC1967: new admin is the zero address"
|
||||||
|
);
|
||||||
|
getAddressSlot(_ADMIN_SLOT).value = newAdmin_;
|
||||||
|
emit setAdminLog(oldAdmin_, newAdmin_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Stores a new address in the EIP1967 implementation slot.
|
||||||
|
*/
|
||||||
|
function _setDummyImplementation(address newDummyImplementation_) internal {
|
||||||
|
address oldDummyImplementation_ = _getDummyImplementation();
|
||||||
|
getAddressSlot(_DUMMY_IMPLEMENTATION_SLOT)
|
||||||
|
.value = newDummyImplementation_;
|
||||||
|
emit setDummyImplementationLog(
|
||||||
|
oldDummyImplementation_,
|
||||||
|
newDummyImplementation_
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Delegates the current call to `implementation`.
|
||||||
|
*
|
||||||
|
* This function does not return to its internall call site, it will return directly to the external caller.
|
||||||
|
*/
|
||||||
|
function _delegate(address implementation_) internal {
|
||||||
|
// solhint-disable-next-line no-inline-assembly
|
||||||
|
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())
|
||||||
|
|
||||||
|
// Call the implementation.
|
||||||
|
// out and outsize are 0 because we don't know the size yet.
|
||||||
|
let result := delegatecall(
|
||||||
|
gas(),
|
||||||
|
implementation_,
|
||||||
|
0,
|
||||||
|
calldatasize(),
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copy the returned data.
|
||||||
|
returndatacopy(0, 0, returndatasize())
|
||||||
|
|
||||||
|
switch result
|
||||||
|
// delegatecall returns 0 on error.
|
||||||
|
case 0 {
|
||||||
|
revert(0, returndatasize())
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
return(0, returndatasize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Delegates the current call to the address returned by Implementations registry.
|
||||||
|
*
|
||||||
|
* This function does not return to its internall call site, it will return directly to the external caller.
|
||||||
|
*/
|
||||||
|
function _fallback(bytes4 sig_) internal {
|
||||||
|
address implementation_ = _getSigImplementation(sig_);
|
||||||
|
require(
|
||||||
|
implementation_ != address(0),
|
||||||
|
"Liquidity: Not able to find implementation_"
|
||||||
|
);
|
||||||
|
_delegate(implementation_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract AdminStuff is Internals {
|
||||||
|
modifier onlyAdmin() {
|
||||||
|
require(msg.sender == _getAdmin(), "not-the-admin");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev sets new admin.
|
||||||
|
*/
|
||||||
|
function setAdmin(address newAdmin_) external onlyAdmin {
|
||||||
|
_setAdmin(newAdmin_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev sets new dummy-implementation.
|
||||||
|
*/
|
||||||
|
function setDummyImplementation(address newDummyImplementation_)
|
||||||
|
external
|
||||||
|
onlyAdmin
|
||||||
|
{
|
||||||
|
_setDummyImplementation(newDummyImplementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev adds new implementation address.
|
||||||
|
*/
|
||||||
|
function addImplementation(address implementation_, bytes4[] calldata sigs_)
|
||||||
|
external
|
||||||
|
onlyAdmin
|
||||||
|
{
|
||||||
|
_setImplementationSigs(implementation_, sigs_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev removes an existing implementation address.
|
||||||
|
*/
|
||||||
|
function removeImplementation(address implementation_) external onlyAdmin {
|
||||||
|
_removeImplementationSigs(implementation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(address admin_, address dummyImplementation_) {
|
||||||
|
_setAdmin(admin_);
|
||||||
|
_setDummyImplementation(dummyImplementation_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract contract Proxy is AdminStuff {
|
||||||
|
constructor(address admin_, address dummyImplementation_)
|
||||||
|
AdminStuff(admin_, dummyImplementation_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns admin's address.
|
||||||
|
*/
|
||||||
|
function getAdmin() external view returns (address) {
|
||||||
|
return _getAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns dummy-implementations's address.
|
||||||
|
*/
|
||||||
|
function getDummyImplementation() external view returns (address) {
|
||||||
|
return _getDummyImplementation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns bytes4[] sigs from implementation address If not registered then returns empty array.
|
||||||
|
*/
|
||||||
|
function getImplementationSigs(address impl_)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (bytes4[] memory)
|
||||||
|
{
|
||||||
|
return _getImplementationSigs(impl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns implementation address from bytes4 sig. If sig is not registered then returns address(0).
|
||||||
|
*/
|
||||||
|
function getSigsImplementation(bytes4 sig_)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return _getSigImplementation(sig_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Fallback function that delegates calls to the address returned by Implementations registry.
|
||||||
|
*/
|
||||||
|
fallback() external payable {
|
||||||
|
_fallback(msg.sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Fallback function that delegates calls to the address returned by Implementations registry.
|
||||||
|
*/
|
||||||
|
receive() external payable {
|
||||||
|
if (msg.sig != 0x00000000) {
|
||||||
|
_fallback(msg.sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user