从TwitterScan项目看代理合约的使用

前言

在【关于可变合约的二三事】相关的文章中,我们讨论了代理合约的使用方式,但文中的最后,也提到了,给出的代理合约例子是有问题的,不能用在真实项目中,哪真实项目中是怎么使用代理合约的呢?本文就以最近比较火热的项目TwitterScan作为分析对象,讨论其使用代理合约的方式。

在讨论前,有必要提一下,网上很多代理合约的文章都脱离了真实项目,即你了解完后,依旧看不懂真实项目的合约,比较难受。最好的学习方式还是专业机构的文档+真实在跑的项目,这样才能验证你自己的理解是否正确。

TwitterScan简介

因为有些朋友可能不知道TwitterScan是什么项目,这里简单介绍一下。

TwitterScan官网:https://twitterscan.com/

b3978b9951a89d917e533882c3605632.png

一个Web3数据分析工具,可以追踪链上数据(即各种Token和链的交易数据)还可以追踪Twitter Web3圈KOL社交发言等链下数据。

嗯,这些都不是重点,重点是,这个项目在短短几个月内,获利536.68个ETH(即获利近500w RMB),而且,技术难度也不会太高。

这里,插一下我目前的一个观点:web3工具类的项目,开发难度不会特别大,但起来的项目收益都很高,所以开发者同行们,可以多多关注一下这个领域的工具

简单代理合约的问题

基于【关于可变合约的二三事】一文,我们知道代理合约会通过delegatecall函数来实现委托调用,其目的是将项目数据放在代理合约中,将项目玩法逻辑放在逻辑合约中,当项目需要更新时,直接替换代理合约关联的逻辑合约,便可以无痛切换成新的玩法且数据不会丢失。当然,逻辑合约出现了bug也可以利用这种方式修改一下。

那代理合约出现bug了呢?抱歉,救不了,要救只能通过社交更新法,即告诉大家,旧的合约有问题,一起来玩新的合约吧,我们官方不承认旧合约里的数据了,嗯,这就是所谓的社交更新,你要让大家配合你玩。

为了稳妥起见,多数项目都不会自己去开发代理合约,而是直接使用专业机构提供的代理合约模型,比如openzeppelin提供了不同模式的代理合约玩法,TwitterScan项目便使用openzeppelin提供的透明代理合约。

在看TwitterScan的合约前,有必要讨论一下简单代理合约的问题。

slot clash

solidity语言有个比较隐晦的特性,那便是slot的分布,很多人看solidity的语法与JavaScript类似,就模仿JavaScript的形式写代码,从而在这里踩坑。

为了方便你理解,这里写给出一个简单的Proxy合约和逻辑合约:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

contract Proxy {
    address public implementation; 
    address public admin;
    uint256 public x = 0;

    constructor() {
        admin = msg.sender;
    }

    modifier onlyAdmin {
        require(msg.sender == admin);
        _;
    }


    function setImplementation(address _implementation) external onlyAdmin{
        implementation = _implementation;
    }


    fallback() external payable {
        _delegate();
    }

    receive() external payable {
        _delegate();
    }
    
    
    function _delegate() internal {
        assembly {
    
            let _implementation := sload(0)

            calldatacopy(0, 0, calldatasize())


            let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)

            returndatacopy(0, 0, returndatasize())

            switch result
     
            case 0 {
                revert(0, returndatasize())
            }
  
            default {
                return(0, returndatasize())
            }
        }
    }

}


contract LogicsContract {
	  // 与代理合约中变量的顺序要一致
    address public implementation;
    address public admin;
    uint256 public x = 0;

    function add_x(uint256 _x) external returns(uint256) {
        x += _x;
        return x;
    }

}

上面的代码是从【关于可变合约的二三事】中直接复制过来的,看到LogicsContract中的注释,solidity要求你,逻辑合约要与代理合约的变量顺序是一直的,究其原因,便是slot。

在solidity中,每个合约中的变量都是按顺序放置的,比如LogicsContract合约中的implementation变量在第0个slot,admin变量则在第1个slot,它刚好与proxy合约中的implementation变量对应上,如果你将顺序调整一下,比如调整成如下形式:

contract LogicsContract {
    // x => 0 slot
    uint256 public x = 0;
    // implementation => 1 slot
    address public implementation;
    // admin => 2 slot
    address public admin;

    function add_x(uint256 _x) external returns(uint256) {
        x += _x;
        return x;
    } 
}

如果调整成上面的顺序,那么x变量就在第0个slot了,而proxy中,第0个slot是implementation变量,这样当我们调用add_x函数时,在LogicsContract中,处理的是第0个slot,即x变量,但映射到Proxy中,则变成改变的是i

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒编程-二两

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值