TransparentUpgradeableProxy double admin source

Hi, I am researching TransparentUpgradeableProxy and I see it uses immutable variable which is being set to ProxyAdmin address and when upgrading to new implementation this variable is checked to ensure ProxyAdmin initiated request to TransparentUpgradeableProxy for upgrade. That is clear, what is not clear is why TransparentUpgradeableProxy in its constructor is using:

 ERC1967Utils.changeAdmin(_proxyAdmin());

why we are setting to some storage slot same ProxyAdmin address. I searched and I cannot find where this storage slot with ProxyAdmin address is being read. Only "useful" variable is immutable variable which is being used is inside TransparentUpgradeableProxy:

address private immutable _admin;

:1234: Code to reproduce

contract TransparentUpgradeableProxy is ERC1967Proxy {
    // An immutable address for the admin to avoid unnecessary SLOADs before each call
    // at the expense of removing the ability to change the admin once it's set.
    // This is acceptable if the admin is always a ProxyAdmin instance or similar contract
    // with its own ability to transfer the permissions to another account.
    address private immutable _admin;

    /**
     * @dev The proxy caller is the current admin, and can't fallback to the proxy target.
     */
    error ProxyDeniedAdminAccess();

    /**
     * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
     * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
     * {ERC1967Proxy-constructor}.
     */
    constructor(
        address _logic,
        address initialOwner,
        bytes memory _data
    ) payable ERC1967Proxy(_logic, _data) {
        _admin = address(new FijaProxyAdmin(initialOwner));
        // Set the storage value and emit an event for ERC-1967 compatibility
        ERC1967Utils.changeAdmin(_proxyAdmin());
    }

    /**
     * @dev Returns the admin of this proxy.
     */
    function _proxyAdmin() internal virtual returns (address) {
        return _admin;
    }

:laptop: Environment

1 Like

Good question! You're diving deep into the OpenZeppelin's TransparentUpgradeableProxy, as you said above, The TransparentUpgradeableProxy uses two separate ways to store the admin address, I think there are two main reasons for this:

  • Gas Optimization, The immutable _admin is embedded in bytecode, accessing them costs 0 gas, this optimization is critical for proxy contracts, where admin checks happen frequently.
  • Backward Compatibility with ERC-1967, some tools like block explorers, wallets, and upgrade systems expect the admin to be in that slot(ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103).