TransparentUpgradeableProxy.sol наследует от ERC1967Proxy.sol. В конструкторе этого контракта разворачивается ProxyAdmin и устанавливается как адрес неизменяемого admin (первая переменная в контракте).
contract TransparentUpgradeableProxy is ERC1967Proxy {
address private immutable _admin;
error ProxyDeniedAdminAccess();
constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
_admin = address(new ProxyAdmin(initialOwner));
// Set the storage value and emit an event for ERC-1967 compatibility
ERC1967Utils.changeAdmin(_proxyAdmin());
}
function _proxyAdmin() internal view virtual returns (address) {
return _admin;
}
function _fallback() internal virtual override {
if (msg.sender == _proxyAdmin()) {
if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
revert ProxyDeniedAdminAccess();
} else {
_dispatchUpgradeToAndCall();
}
} else {
super._fallback();
}
}
function _dispatchUpgradeToAndCall() private {
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
ERC1967Utils.upgradeToAndCall(newImplementation, data);
}
}
Рассмотрим случай, когда отправителем msg.sender является _proxyAdmin. В этом случае вызов направляется в _dispatchUpgradeToAndCall(), но _fallback() сначала проверяет, что предоставленный селектор функций является селектором функций для upgradeToAndCall.
«Селектор» здесь не является "настоящим" селектором, поскольку Transparent Upgradeable Proxy не имеет публичных функций. Однако, чтобы позволить ProxyAdmin сделать вызов интерфейса (вызов высокого уровня), он должен принять от ProxyAdmin кодированные ABI calldata для upgradeToAndCall().
Напомн, что ProxyAdmin делает интерфейсный вызов upgradeToAndCall в прокси, хотя у прокси нет публичных функций, кроме fallback (код ProxyAdmin показан далее):
contract ProxyAdmin is Ownable {
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
constructor(address initialOwner) Ownable(initialOwner) {}
function upgradeAndCall(
ITransparentUpgradeableProxy proxy,
address implementation,
bytes memory data
) public payable virtual onlyOwner {
@> proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}Выше представлено видео, демонстрирующее все три блока кода рядом друг с другом и то, как различные контракты в цепочке наследования (Proxy, ERC1967Proxy и TransparentUpgradeableProxy) взаимодействуют друг с другом.
В следующем посте поговорим о том, почему функция называется upgradeToAndCall(), а не просто upgradeTo().
#proxy #transparent