Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple ImmutableBeaconProxy implementation #19

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/contracts/transparent-proxy/ImmutableBeaconProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IBeacon} from './interfaces/IBeacon.sol';
import {Proxy} from './Proxy.sol';
import {Address} from '../oz-common/Address.sol';

/**
* @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
* The beacon address is immutable. The purpose of it, is to be able to access this proxy via delegatecall

* !!! IMPORTANT CONSIDERATION !!!
* We expect that that implementation will not have any storage associated,
* because it will not be accessible via delegatecall, and will not work as expected
kyzia551 marked this conversation as resolved.
Show resolved Hide resolved
*/
contract ImmutableBeaconProxy is Proxy {
address internal immutable _beacon;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
address internal immutable _beacon;
address public immutable BEACON;


constructor(address beacon) {
require(Address.isContract(beacon), 'beacon is not a contract');
kyzia551 marked this conversation as resolved.
Show resolved Hide resolved
require(
Address.isContract(IBeacon(beacon).implementation()),
'beacon implementation is not a contract'
);

// there is no initialization call, because we expect that implementation should have no storage
_beacon = beacon;
}

/**
* @dev Returns the current implementation address of the associated beacon.
*/
function _implementation() internal view virtual override returns (address) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why internal?

return IBeacon(_beacon).implementation();
}
}
16 changes: 16 additions & 0 deletions src/contracts/transparent-proxy/interfaces/IBeacon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
43 changes: 43 additions & 0 deletions test/ImmutableBeaconProxy.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import 'forge-std/Test.sol';
import {ImmutableBeaconProxy, IBeacon} from '../src/contracts/transparent-proxy/ImmutableBeaconProxy.sol';

contract ImplementationMock {}

contract BeaconMock is IBeacon {
address public implementation;

constructor(address newImplementation) {
implementation = newImplementation;
}
}

contract ImmutableBeaconProxyMock is ImmutableBeaconProxy {
constructor(address beacon) ImmutableBeaconProxy(beacon) {}

function implementation() public view returns (address) {
return _implementation();
}
}

contract ImmutableBeaconProxyTest is Test {
function testResolvesImplementationCorrectly() public {
address implementation = address(new ImplementationMock());
address beacon = address(new BeaconMock(implementation));

assertEq(implementation, (new ImmutableBeaconProxyMock(beacon)).implementation());
}

function testBeaconNotAContract() public {
vm.expectRevert(bytes('beacon is not a contract'));
new ImmutableBeaconProxy(address(1));
}

function testImplementationNotAContract() public {
address beacon = address(new BeaconMock(address(1)));

vm.expectRevert(bytes('beacon implementation is not a contract'));
new ImmutableBeaconProxy(beacon);
}
}