可升级代理合约方案:用户访问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}}
}