// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /** * @dev ERC-1967 Transparent Upgradeable Proxy * Implementation storage slot: * bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) */ contract TransparentUpgradeableProxy { bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; event Upgraded(address indexed implementation); event AdminChanged(address previousAdmin, address newAdmin); modifier ifAdmin() { if (msg.sender == _getAdmin()) { _; } else { _fallback(); } } constructor(address _logic, address _admin, bytes memory _data) payable { _setAdmin(_admin); _upgradeTo(_logic); if (_data.length > 0) { (bool success, ) = _logic.delegatecall(_data); require(success, "Initialization failed"); } } function admin() external ifAdmin returns (address) { return _getAdmin(); } function implementation() external ifAdmin returns (address) { return _getImplementation(); } function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot be zero address"); emit AdminChanged(_getAdmin(), newAdmin); _setAdmin(newAdmin); } function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { _upgradeTo(newImplementation); (bool success, ) = newImplementation.delegatecall(data); require(success, "Upgrade call failed"); } function _fallback() internal { address impl = _getImplementation(); require(impl != address(0), "Implementation not set"); assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } fallback() external payable { _fallback(); } receive() external payable { _fallback(); } function _getImplementation() internal view returns (address impl) { bytes32 slot = _IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } function _setImplementation(address newImplementation) internal { bytes32 slot = _IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } function _getAdmin() internal view returns (address adm) { bytes32 slot = _ADMIN_SLOT; assembly { adm := sload(slot) } } function _setAdmin(address newAdmin) internal { bytes32 slot = _ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } function _upgradeTo(address newImplementation) internal { require(newImplementation.code.length > 0, "Implementation must be a contract"); _setImplementation(newImplementation); emit Upgraded(newImplementation); } }