基础智能合约第二讲(智能合约结构)

255 阅读8分钟

介绍

在solidity中,合约有点类似面向对象语言中的类,每个合约中包含状态变量、函数变量、函数、函数修饰器、事件、结构、和枚举类的声明,合约也可以继承其他的合约。大家可能对类和类中的函数的概念没有什么了解,我简单给大家举个例子。一个类可以比作是汽车,汽车里面的油就是变量,然后油门、刹车等就是函数,我们踩油门相当于调用类中的函数,汽车动起来,油减少,相当于变量值改变了。

我们来根据上面的描述写一个汽车的合约。先使用remix 创建一个CarContract1.sol文件,然后设定一个CarContract1名字的合约。汽车有了,还要有一个油箱,设定一个变量_gasoline,作为油箱。然后我们再给汽车加一个油门,写一个startUp函数作为油门。现在有了油箱但是不知道有多少油,再加gatGasoline函数作为一个仪表盘。咱们只有油箱没有油汽车也跑不了,在加一个加油的接口,给汽车加油,使用addGasoline函数进行加油。下面就是我们完整的小汽车的代码。

pragma solidity ^0.6.0;

contract CarContract1 {
    uint256 _gasoline;
    
    function startUp() public {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline = _gasoline + gasoline;
    }
    
}

1、 状态变量

小汽车合约中的_gasoline就是我们定义的状态变量,类型是 uint256 类型。 该变量是存储在链上的,也就是说他的数据是被保存起来的,每次改动都会记录下来。因此我们在进行调用 addGasoline 函数时,会给这个小汽车加油成功,_gasoline 的值会变化,同样的我们调用 startUp 函数时,_gasoline 的值也会变化。

2、 函数

在CarContract1小汽车中,startUp()、getGasoline()、addGasoline(uint256 gasoline) 都是函数。这些函数有的是没有参数的,又叫无参函数,比如:startUp()、getGasoline()。有的是有参数的,就叫有参函数,比如:addGasoline(uint256 gasoline)。这些函数,有的有返回值,有的没有返回值,根据具体场景来定,一般call操作都是有返回值的,call操作不会改变合约状态。只有send操作,才会进行改变合约的状态。

3、 函数变量

我们都知道加不同的型号汽油会有不一样的效果,我们来给汽车换下不同的型号汽油,在汽车上我们放置一个桶名字是_bucket,用来装另一个型号的汽油。如果我们自己的两个容器里面有一个是空的,我们可以直接进行转换汽油。但是我们自己的两个容器中都有油的时候,两个容器很明显不能进行交换汽油,这个时候我们需要向隔壁的老李借一个桶 __tempBucket,这样三个容器就能进行转换油箱里面的汽油和桶里面的汽油进行对换了,换完以后把桶还回去。

我们进行在进行造一个新的小汽车名字是CarContract2,增加一个桶,设定变量为_bucket,作为桶。还需要记录当前汽车的油的型号。设定变量 _gasolineType 为当前油类型,默认是 1类型。设定一个函数 changeGasolineType,进行交换汽油类型,在设定一个函数进行查看当前汽车的类型 getGasolineType 。至此我们小汽车升级成功。

pragma solidity ^0.6.0;

contract CarContract2 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    
    function startUp() public {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasolineType() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
}

上面的小汽车2代正式出炉,我来给大家讲下做了那些升级,首先我们的changeGasolineType内部定义了 __tempBucket 变量,该变量就是函数变量,是临时创建的并且不会被记录在链上的变量,也就是我们用完就还给隔壁老李了,还回去的时候桶是空的。

4、 函数修饰器

我们的小汽车还是很简单,我们在给他加一点东西,规定小汽车要想启动必须关闭车门。

下面我们再一次修改我们的小汽车,加一个_doorStatus状态变量作为我们的车门状态。再加连个函数getDoorStatus()、changeDoorStatus(),用来控制开门/关门并且查看门的状态。并且加入一个whenDoorClose()作为我们的判断器。

pragma solidity ^0.6.0;

contract CarContract3 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    
    bool _doorStatus;
    
    modifier whenDoorClose() {
        require(_doorStatus, "door is not close");
        _;
        
    }
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
}

上面我们的3代小汽车已经完成了,whenDoorClose() 就是我们定义的函数修饰器 使用modifier 来定义的。

5、 事件

每次都到没有油了才去加油,我们加一个功能,当行驶时油量低于5的时候我们要进行预警。

我们加入一个 gasolineAlarm 事件,该事件有一个参数,当前的油量。这样我们在启动的函数中加入这个事件的调用,判断本次使用后的油量是否小于等于5,是的话进行调用该事件

pragma solidity ^0.6.0;

contract CarContract4 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    
    bool _doorStatus;
    
    modifier whenDoorClose() {
        require(_doorStatus, "door is not close");
        _;
        
    }
    
    event gasolineAlarm(uint256 gasoline);
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
        if (_gasoline <= 5) {
            emit gasolineAlarm(_gasoline);
        }
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
}

我们已经更新到第四代小汽车了,四代小汽车的gasolineAlarm 就是我们定义的事件,事件是会在虚拟机上记录一条日志的,我么可以通过查询日志的方式得到事件内容。

6、 结构

我们的汽车感觉成熟了,这个时候我们要给我们的汽车打上一些特性,比如颜色,比如车轮数,比如车门数等等。

我们在小汽车里面加入CarInfo结构体,里面可以定义color颜色,wheelNum 车轮数等等,然后我们加入设置和获取的函数:setCarInfo()、getCarInfo(), 这样我们的小汽车就有了一些参数了。

pragma solidity ^0.6.0;

contract CarContract5 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    bool _doorStatus;
    
    struct CarInfo {
        string color;
        uint8 wheelNum;
    }
    
    CarInfo _carInfo;
    
    modifier whenDoorClose() {
        require(_doorStatus, "door is not close");
        _;
        
    }
    
    event gasolineAlarm(uint256 gasoline);
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
        if (_gasoline <= 5) {
            emit gasolineAlarm(_gasoline);
        }
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
    
    function setCarInfo(string memory color, uint8 wheelNum) public {
        _carInfo.color = color;
        _carInfo.wheelNum = wheelNum;
        
        //_carInfo = CarInfo(color, wheelNum);

    }
    
    function getCarInfo() public view returns(string memory color, int wheelNum) {
        color = _carInfo.color;
        wheelNum = _carInfo.wheelNum;
    }
}

我们的5代小汽车加入了CarInfo就是结构体,结构体中不能进行设置初值,我们能把一类的属性等进行分类的放在结构体中,可以充当我们的数据模型。

7、 枚举类

我们的小汽车想要开门,需要打开车锁,车锁是一种状态,开/关。

我们加入枚举类DoorSwitch,定义两个状态open,close 。在定义whenDoorSwitch函数修饰器,进行判断。

pragma solidity ^0.6.0;

contract CarContract6 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    bool _doorStatus;
    
    enum DoorSwitch{ open, close }
    
    DoorSwitch _doorSwitch;
    
    struct CarInfo {
        string color;
        uint8 wheelNum;
    }
    
    CarInfo _carInfo;
    
    modifier whenDoorClose() {
        require(_doorStatus, "door is not close");
        _;
        
    }
    
    modifier whenDoorSwitch() {
        if (!_doorStatus) {
            require(_doorSwitch == DoorSwitch.open, "door switch is close");
        }
        _;
    }
    
    event gasolineAlarm(uint256 gasoline);
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
        if (_gasoline <= 5) {
            emit gasolineAlarm(_gasoline);
        }
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
    
    function setCarInfo(string memory color, uint8 wheelNum) public {
        _carInfo.color = color;
        _carInfo.wheelNum = wheelNum;
        
        //_carInfo = CarInfo(color, wheelNum);
    }
    
    function getCarInfo() public view returns(string memory color, int wheelNum) {
        color = _carInfo.color;
        wheelNum = _carInfo.wheelNum;
    }
    
    function setDoorSwitch(DoorSwitch doorSwitch) public {
        _doorSwitch = doorSwitch; 
    }
}

我们已经更新到6代小汽车了,在6代小汽车中我们加入了DoorSwitch车门的开关,使用的就是枚举定义的,在实际项目中枚举定义的话,一般使用在状态和类型的定义上,方便进行管理。

到此我们的小汽车已经完成了,经历了6代的更新,相信大家对于本节课程有空了一定的了解了。智能合约包含的状态变量、函数、函数变量、函数修饰器、事件、结构、枚举类都已经在制作和升级小汽车中使用了。

8、作业

大家关于本次课程都有一定的了解了,那么我们光看不练假把式,是否学会了,需要大家自己检验下。本次课程作业为开放型题目,大家完成一个独立设计的合约,要求使用到本讲所有的知识。

提示:给大家一点idea,大家不仅仅可以设计飞机、大炮、也可以设计一只猫,一只狗等

9、 结束语

感谢小伙伴们阅读本文。有对conflux感兴趣的小伙伴可以添加我的微信(15832013094)或是龙老师微信(ye_pj2013),会拉大家进入conflux成神学院群。conflux成神学院所有课程永久免费,并且还有可能获得一定的外快,加入我们大家一起共同学习进步。