Skip to content
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
*/
contract ImmutableBeaconProxy is Proxy {
address internal immutable _beacon;
Copy link
Copy Markdown
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');
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
Copy Markdown
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);
}
}