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

Improve TransparentUpgradeableProxy's transparency #3977

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions .changeset/many-panthers-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`TransparentUpgradeableProxy`: support value passthrough for all ifAdmin function.
7 changes: 3 additions & 4 deletions contracts/mocks/proxy/ClashingImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
pragma solidity ^0.8.0;

/**
* @dev Implementation contract with an admin() function made to clash with
* @dev TransparentUpgradeableProxy's to test correct functioning of the
* @dev Transparent Proxy feature.
* @dev Implementation contract with a payable admin() function made to clash with TransparentUpgradeableProxy's to
* test correct functioning of the Transparent Proxy feature.
*/
contract ClashingImplementation {
function admin() external pure returns (address) {
function admin() external payable returns (address) {
return 0x0000000000000000000000000000000011111142;
}

Expand Down
20 changes: 16 additions & 4 deletions contracts/proxy/transparent/TransparentUpgradeableProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function admin() external ifAdmin returns (address admin_) {
function admin() external payable ifAdmin returns (address admin_) {
_requireZeroValue();
admin_ = _getAdmin();
}

Expand All @@ -68,7 +69,8 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function implementation() external ifAdmin returns (address implementation_) {
function implementation() external payable ifAdmin returns (address implementation_) {
_requireZeroValue();
implementation_ = _implementation();
}

Expand All @@ -79,7 +81,8 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
*/
function changeAdmin(address newAdmin) external virtual ifAdmin {
function changeAdmin(address newAdmin) external payable virtual ifAdmin {
_requireZeroValue();
_changeAdmin(newAdmin);
}

Expand All @@ -88,7 +91,8 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
*/
function upgradeTo(address newImplementation) external ifAdmin {
function upgradeTo(address newImplementation) external payable ifAdmin {
_requireZeroValue();
_upgradeToAndCall(newImplementation, bytes(""), false);
}

Expand Down Expand Up @@ -117,4 +121,12 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
super._beforeFallback();
}

/**
* @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to
* emulate some proxy functions being non-payable while still allowing value to pass through.
*/
function _requireZeroValue() private {
require(msg.value == 0);
}
}
15 changes: 12 additions & 3 deletions test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,14 +330,23 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy(createProx
);
});

context('when function names clash', function () {
describe('when function names clash', function () {
it('when sender is proxy admin should run the proxy function', async function () {
const value = await this.proxy.admin.call({ from: proxyAdminAddress });
const value = await this.proxy.admin.call({ from: proxyAdminAddress, value: 0 });
expect(value).to.be.equal(proxyAdminAddress);
});

it('when sender is other should delegate to implementation', async function () {
const value = await this.proxy.admin.call({ from: anotherAccount });
const value = await this.proxy.admin.call({ from: anotherAccount, value: 0 });
expect(value).to.be.equal('0x0000000000000000000000000000000011111142');
});

it('when sender is proxy admin value should not be accepted', async function () {
await expectRevert.unspecified(this.proxy.admin.call({ from: proxyAdminAddress, value: 1 }));
});

it('when sender is other value should be accepted', async function () {
const value = await this.proxy.admin.call({ from: anotherAccount, value: 1 });
expect(value).to.be.equal('0x0000000000000000000000000000000011111142');
});
});
Expand Down