solidity0.8.0的应用案例10:可升级合约

这个案例是代理合约的实际操作,代理合约实现了逻辑和数据的分离,就可以实现在生产环境中,轻松升级合约,这就是一个如何实际升级合约的案例。

solidity0.8.0的应用案例10:可升级合约_第1张图片

 实现一个简单的可升级合约,它包含3个合约:代理合约,旧的逻辑合约,和新的逻辑合约

代理合约

我们没有在它的fallback()函数中使用内联汇编,而仅仅用了implementation.delegatecall(msg.data);。因此,回调函数没有返回值,但足够教学使用了。

它包含3个变量:

  • implementation:逻辑合约地址。
  • admin:admin地址。
  • words:字符串,可以通过逻辑合约的函数改变。

它包含3个函数

  • 构造函数:初始化admin和逻辑合约地址。
  • fallback():回调函数,将调用委托给逻辑合约。
  • upgrade():升级函数,改变逻辑合约地址,只能由admin调用。
// SPDX-License-Identifier: MIT
// wtf.academy
pragma solidity ^0.8.4;

// 简单的可升级合约,管理员可以通过升级函数更改逻辑合约地址,从而改变合约的逻辑。
// 教学演示用,不要用在生产环境
contract SimpleUpgrade {
    address public implementation; // 逻辑合约地址
    address public admin; // admin地址
    string public words; // 字符串,可以通过逻辑合约的函数改变

    // 构造函数,初始化admin和逻辑合约地址
    constructor(address _implementation){
        admin = msg.sender;
        implementation = _implementation;
    }

    // fallback函数,将调用委托给逻辑合约
    fallback() external payable {
        (bool success, bytes memory data) = implementation.delegatecall(msg.data);
    }

    // 升级函数,改变逻辑合约地址,只能由admin调用
    function upgrade(address newImplementation) external {
        require(msg.sender == admin);
        implementation = newImplementation;
    }
}

旧逻辑合约

这个逻辑合约包含3个状态变量,与保持代理合约一致,防止插槽冲突。它只有一个函数foo(),将代理合约中的words的值改为"old"

// 逻辑合约1
contract Logic1 {
    // 状态变量和proxy合约一致,防止插槽冲突
    address public implementation; 
    address public admin; 
    string public words; // 字符串,可以通过逻辑合约的函数改变

    // 改变proxy中状态变量,选择器: 0xc2985578
    function foo() public{
        words = "old";
    }
}

新逻辑合约

这个逻辑合约包含3个状态变量,与保持代理合约一致,防止插槽冲突。它只有一个函数foo(),将代理合约中的words的值改为"new"

// 逻辑合约2
contract Logic2 {
    // 状态变量和proxy合约一致,防止插槽冲突
    address public implementation; 
    address public admin; 
    string public words; // 字符串,可以通过逻辑合约的函数改变

    // 改变proxy中状态变量,选择器:0xc2985578
    function foo() public{
        words = "new";
    }
}

Remix实现

完整代码:

// SPDX-License-Identifier: MIT
// wtf.academy
pragma solidity ^0.8.4;


contract SimpleUpgrade{
    address public implementation;//逻辑合约地址
    address public admin;//合约管理人地址
    string public words;//字符串,通过逻辑合约的函数改变

    //构造函数,初始化admin和逻辑合约地址
    constructor(address _implementation) {
        implementation = _implementation;
        admin = msg.sender;
    }

    //通过fallback函数,将调用委托给逻辑合约
    fallback() external payable {
        (bool success,bytes memory data) = implementation.delegatecall(msg.data);
    }

    //升级函数,改变逻辑合约的地址,就可以升级到新的逻辑合约,而且只能管理员才能调用
    function upgrade(address newImplementation) external {
        require(msg.sender == admin,'not owner');
        implementation = newImplementation;
    }
}

contract OldLogic{
    address public implementation;
    address public admin;
    string public words;

    function foo() public {
        words = 'old';
    }
}

contract NewLogic{
    address public implementation;
    address public admin;
    string public words;

    function foo() public {
        words = 'new';
    }
}

1、部署新旧逻辑合约Logic1Logic2

solidity0.8.0的应用案例10:可升级合约_第2张图片

 

solidity0.8.0的应用案例10:可升级合约_第3张图片

 2、部署可升级合约SimpleUpgrade,将implementation地址指向把旧逻辑合约。

solidity0.8.0的应用案例10:可升级合约_第4张图片

 

 3、利用选择器0xc2985578,在代理合约中调用旧逻辑合约Logic1foo()函数,将words的值改为"old"

solidity0.8.0的应用案例10:可升级合约_第5张图片

 4、调用upgrade(),将implementation地址指向新逻辑合约Logic2

solidity0.8.0的应用案例10:可升级合约_第6张图片

5、 利用选择器0xc2985578,在代理合约中调用新逻辑合约Logic2foo()函数,将words的值改为"new"

solidity0.8.0的应用案例10:可升级合约_第7张图片

 它是一个可以改变逻辑合约的代理合约,给不可更改的智能合约增加了升级功能。但是,这个合约有选择器冲突的问题,存在安全隐患。之后我们会介绍解决这一隐患的可升级合约标准:透明代理和UUPS

你可能感兴趣的:(区块链)