代理合约的變量
- 邏輯合約地址
- admin地址
- 字符串: 可透過邏輯合約的函數改變
代理合约的函數
- constructor: Initialize邏輯合約地址與admin地址
- fallback: delegatecall邏輯合約,admin不能調用
- upgrade: 改變邏輯合約地址,只能由admin調用
透過權限管理避免function selector collision
// 透明可升级合约的教学代码,不要用于生产。
contract TransparentProxy {
address implementation; // logic合约地址
address admin; // 管理员
string public words; // 字符串,可以通过逻辑合约的函数改变
// 构造函数,初始化admin和逻辑合约地址
constructor(address _implementation){
admin = msg.sender;
implementation = _implementation;
}
// fallback函数,将调用委托给逻辑合约
// 不能被admin调用,避免选择器冲突引发意外
fallback() external payable {
require(msg.sender != admin);
(bool success, bytes memory data) = implementation.delegatecall(msg.data);
}
// 升级函数,改变逻辑合约地址,只能由admin调用
function upgrade(address newImplementation) external {
if (msg.sender != admin) revert();
implementation = newImplementation;
}
}
邏輯合約的變量(變數位置需與代理合约的位置一樣,防止storage collision)
- 邏輯合約地址
- admin地址
- 字符串: 可透過邏輯合約的函數改變
// 旧逻辑合约
contract Logic1 {
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器: 0xc2985578
function foo() public{
words = "old";
}
}
// 新逻辑合约
contract Logic2 {
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器:0xc2985578
function foo() public{
words = "new";
}
}
- UUPS => 升級函數放在邏輯合約(較複雜)
- Transparent Proxy => 升級函數放在代理合約(耗gas)
- 解決function selector collision