EIP1967可升级合约详解

server/2024/12/15 4:54:48/

在这里插入图片描述
可升级代理合约方案:用户访问proxy合约,实际方法由logic合约实现。数据存储在proxy合约中

在这里插入图片描述

在这里插入图片描述
部署Proxy示例地址:https://testnet.bscscan.com/address/0xcb301306aa03115d40052eec804cc7458d03f1c2

// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;abstract contract Proxy {// 内联汇编实现委托调用function _delegate(address implementation) internal virtual {assembly {calldatacopy(0, 0, calldatasize())let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)returndatacopy(0, 0, returndatasize())switch result//  0(表示委托调用失败)case 0 {revert(0, returndatasize())}default {return(0, returndatasize())}}}// 用于指定实际的实现合约地址,需要在继承的子类合约中被重写function _implementation() internal view virtual returns (address);// 调用前钩子及委托调用触发function _fallback() internal virtual {_beforeFallback();_delegate(_implementation());}// 当合约接收到一个没有匹配到明确定义函数的调用时会被触发的函数fallback() external payable virtual {_fallback();}// 在合约接收到以太币(payable)且没有 fallback 函数或者没有匹配到其他合适函数时被触发的特殊函数receive() external payable virtual {_fallback();}// 可以在子类中重写此钩子函数来添加一些前置的自定义逻辑function _beforeFallback() internal virtual {}
}abstract contract ERC1967Upgrade {// 合约升级回滚测试相关bytes32 private constant _ROLLBACK_SLOT = bytes32(uint256(keccak256("eip1967.proxy.rollback")) - 1);// 用于存储实现合约的地址bytes32 internal constant _IMPLEMENTATION_SLOT = bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1);event Upgraded(address indexed implementation);// 获取实现合约地址function _getImplementation() internal view returns (address) {return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;}// 设置实现合约地址,私有函数function _setImplementation(address newImplementation) private {require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;}// 执行合约升级function _upgradeTo(address newImplementation) internal {_setImplementation(newImplementation);emit Upgraded(newImplementation);}// 实现了在升级后立即执行一些初始化或者其他必要的函数调用操作的功能function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {_upgradeTo(newImplementation);if (data.length > 0 || forceCall) {Address.functionDelegateCall(newImplementation, data);}}// 安全升级并调用及回滚测试function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {address oldImplementation = _getImplementation();_setImplementation(newImplementation);if (data.length > 0 || forceCall) {Address.functionDelegateCall(newImplementation, data);}StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);// 是否进行回滚测试相关操作,防止重放?if (!rollbackTesting.value) {rollbackTesting.value = true;// 在新实现合约上调用 upgradeTo 函数Address.functionDelegateCall(newImplementation, abi.encodeWithSignature("upgradeTo(address)", oldImplementation));rollbackTesting.value = false;require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");// 如果验证通过,则再次调用 _upgradeTo 函数重新设置新的实现合约地址_upgradeTo(newImplementation);}}// 用于存储管理员地址bytes32 internal constant _ADMIN_SLOT = bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1);event AdminChanged(address previousAdmin, address newAdmin);function _getAdmin() internal view returns (address) {return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;}function _changeAdmin(address newAdmin) internal {emit AdminChanged(_getAdmin(), newAdmin);require(newAdmin != address(0), "ERC1967: new admin is the zero address");StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;}// 用于存储信标(Beacon)相关的地址信息bytes32 internal constant _BEACON_SLOT = bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1);event BeaconUpgraded(address indexed beacon);function _getBeacon() internal view returns (address) {return StorageSlot.getAddressSlot(_BEACON_SLOT).value;}// 升级信标并可选调用function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");require(Address.isContract(IBeacon(newBeacon).implementation()), "ERC1967: beacon implementation is not a contract");StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;emit BeaconUpgraded(newBeacon);if (data.length > 0 || forceCall) {Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);}}
}contract ERC1967Proxy is Proxy, ERC1967Upgrade {constructor(address _logic) payable {// 检查 _IMPLEMENTATION_SLOT 的值是否符合预期的计算结果(基于 EIP1967 标准中对实现合约存储槽的定义进行验证),确保存储槽定义的一致性和正确性assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));_upgradeToAndCall(_logic, "", false);}// 重写获取实现合约地址function _implementation() internal view virtual override returns (address impl) {return ERC1967Upgrade._getImplementation();}
}
// 用于规范信标合约应该具备的功能
interface IBeacon {function implementation() external view returns (address);
}library Address {// 判断是否为合约地址function isContract(address account) internal view returns (bool) {uint256 size;assembly {size := extcodesize(account)}return size > 0;}function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {return functionDelegateCall(target, data, "Address: low-level delegate call failed");}function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {require(isContract(target), "Address: delegate call to non-contract");(bool success, bytes memory returndata) = target.delegatecall(data);return verifyCallResult(success, returndata, errorMessage);}function verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) internal pure returns (bytes memory) {if (success) {return returndata;} else {if (returndata.length > 0) {assembly {let returndata_size := mload(returndata)revert(add(32, returndata), returndata_size)}} else {revert(errorMessage);}}}
}library StorageSlot {struct AddressSlot {address value;}struct BooleanSlot {bool value;}struct Bytes32Slot {bytes32 value;}struct Uint256Slot {uint256 value;}function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {assembly {r.slot := slot}}function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {assembly {r.slot := slot}}function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {assembly {r.slot := slot}}function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {assembly {r.slot := slot}}
}

http://www.ppmy.cn/server/150265.html

相关文章

QT笔记- QSystemTrayIcon系统托盘功能完整示例

1. 创建托盘对象 // 创建托盘图标QSystemTrayIcon * trayIcon new QSystemTrayIcon(this);QIcon icon("://icon/test.png");trayIcon->setIcon(icon);trayIcon->show();trayIcon->connect(trayIcon, &QSystemTrayIcon::activated,this, &MainWindo…

快速上手Neo4j图关系数据库

参考视频: 【IT老齐589】快速上手Neo4j网状关系图库 1 Neo4j简介 Neo4j是一个图数据库,是知识图谱的基础 在Neo4j中,数据的基本构建块包括: 节点(Nodes)关系(Relationships)属性(Properties)标签(Labels) 1.1 节点(Nodes) 节点…

抖音后端实习一面总结

置之死地而后生 抖音后端开发实习一面 自我介绍 你参加了PAT比赛?介绍一下? 平时有刷题吗?有的,那来做一下算法题目吧,单词拆分(动态规划1h过去了...) TCP有哪些状态?每种状态代表…

【3】数据分析基础(Numpy的计算)

在学习了N维数组的概念、常用属性以及如何创建一个N维数组后,我们来继续学习N维数组的计算。 我们将会从2个方向学习N维数组的计算: 1. 数组和数的计算 2.相同形状数组的计算 1. 数组和数的计算当数组和数字进行计算的时候,NumPy会将该数字的…

【开源免费】基于SpringBoot+Vue.JS渔具租赁系统(JAVA毕业设计)

本文项目编号 T 005 ,文末自助获取源码 \color{red}{T005,文末自助获取源码} T005,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 渔…

基于多视角深度学习技术的乳腺X线分类:图神经网络与Transformer架构的研究|文献速递-生成式模型与transformer在医学影像中的应用速递

Title 题目 Mammography classification with multi-view deep learning techniques:Investigating graph and transformer-based architectures 基于多视角深度学习技术的乳腺X线分类:图神经网络与Transformer架构的研究 01 文献速递介绍 乳腺X线检查是乳腺癌…

【C++】继承和派生(超级详细版)

文章目录 继承概念定义格式单继承和多继承继承权限 派生派生类的构成派生类的默认成员函数①构造函数②拷贝构造函数③赋值运算符重载函数④析构函数 派生类的特殊成员函数①友元函数②静态函数 派生类的内存大小 派生类和基类的关系复杂的菱形继承及菱形虚继承 继承是面向对象…

大模型:把GPT搬回家 - chatGPT的本地化API -Node.js调用

chatGPT拒绝了中国大陆和中国香港的访问,包括api的调用。这使得我们无法使用目前来讲确实YYLX的生产工具,仔细想一下其实还是可以曲线解决的,本文的介绍仅供学习参考。 用Node.jschatGPT提供的API,就可以在自己本地或者自己的服务…