智能合约并不是现实中常见的合同,而是存在区块链上,能够被触发执行的一段程序代码,这些代码实现了某种预约的规则,是存在于区块链执行环境中的“自治代理”。智能合约须要被触发,代码才会执行,不被触发他的状态将会始终保持一个状态,而且部署后的智能合约将不可被修改。智能合约语言的语法和js脚本语言很像,所以有过js开发经验的小伙伴们学习起来会很快。程序员
咱们都知道“预先善其事、必先利其器”的道理,现实中织布是须要织布机才能完成织布,一样的咱们的智能合约学习也是要有编程工具的使用的。咱们本套课程都将以 remix 为编程工具进行讲解课程。remix 就是咱们手里的织布机,能织出什么布就看咱们这些使用 remix 的程序员了。
地址为(http://remix.ethereum.org/)能够直接在浏览器中开发,很方便,只要有一个 google chrome 谷歌浏览器就能够开发了。chrome
remix 有几个主要的经常使用面板,分别是文件面板、编译器、运行器、以及占用最大一部分的文本编辑器组成。编程
文件面板:咱们能够在这个面板进行建立文件、选择文件等管理文件的操做。
编译器:咱们能够将sol文件进行编译,编译不经过将不能执行合约,而且能够获得code.json以及abi.json。咱们能够将他们在支持sol语言的公链上运行。
运行器:能够将sol智能合约部署在eth链上,而且能对合约的方法进行调用等操做。
文本编辑器:咱们的代码就是写在这个位置的,后面基本大部分时间你将面对的是这个页面。json
下面的智能合约是咱们第一个合约程序,所以咱们命名为 “FirstContract.sol” 文件名数组
pragma solidity ^0.6.0; // first contract contract FirstContract { // first method function firstMethod() public pure returns (string memory) { return 'I am first method !!!'; } }
上面代码不少小伙伴应该不是很懂什么意思,不懂不要紧,我来给你们一行一行讲解。
pragma solidity ^0.6.0;
这一行是说明使用solidity 0.6.0版本写的,能够运行在0.6.0到0.7.0之间的版本上。
contract FirstContract {
这一句是定义一个叫 FirstContract 名称的合约。
function firstMethod() public pure returns (string memory){
这一行是定义一个方法叫作 firstMethod, 该方法有返回值,类型是 string 类型的。浏览器
return 'I am first method !!!';
这一行是这个方法将会返回 “I am first method !!!”。app
看起来可能仍是会有小伙伴们有不明白的地方,可是咱们先只教这么多,关于什么是string,string 就是字符串的意思,字符串你就能够看成是任意的abcde这些字母等还有标点符号写在了单引号或者双引号中。这就是字符串最通俗易懂的解释了,小伙伴们,你们能够动手试试自定义一些字符串让他返回。编辑器
在solidity中,合约有点相似面向对象语言中的类,每一个合约中包含状态变量、函数、函数变量、函数修饰器、事件、结构、和枚举类的声明,合约也能够继承其余的合约。你们可能对类和类中的结构的概念没有什么了解,我简单给你们举个例子。一个类能够比做是汽车,汽车里面的油就是变量,而后油门、刹车等就是函数,咱们踩油门至关于调用类中的函数,汽车动起来,油减小,至关于变量值改变了。函数
咱们来根据上面的描述写一个汽车的合约。先使用remix 建立一个CarContract1.sol文件,而后设定一个CarContract1名字的合约。汽车有了,还要有一个油箱,设定一个变量_gasoline,做为油箱。而后咱们再给汽车加一个油门,写一个startUp函数做为油门。如今有了油箱可是不知道有多少油,再加gatGasoline函数做为一个仪表盘。我们只有油箱没有油汽车也跑不了,在加一个加油的接口,给汽车加油,使用addGasoline函数进行加油。下面就是咱们完整的小汽车的代码。工具
CarContract1.sol
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; } }
小汽车合约中的_gasoline就是咱们定义的状态变量,类型是 uint256 类型。 该变量是存储在链上的,也就是说他的数据是被保存起来的,每次改动都会记录下来。所以咱们在进行调用 addGasoline 函数时,会给这个小汽车加油成功,_gasoline 的值会变化,一样的咱们调用 startUp 函数时,_gasoline 的值也会变化。
在CarContract1小汽车中,startUp()、getGasoline()、addGasoline(uint256 gasoline) 都是函数。这些函数有的是没有参数的,又叫无参函数,好比:startUp()、getGasoline()。有的是有参数的,就叫有参函数,好比:addGasoline(uint256 gasoline)。这些函数,有的有返回值,有的没有返回值,根据具体场景来定,通常call操做都是有返回值的,call操做不会改变合约状态。只有send操做,才会进行改变合约的状态。
三、 函数变量
咱们都知道加不一样的型号汽油会有不同的效果,咱们来给汽车换下不一样的型号汽油,在汽车上咱们放置一个桶名字是_bucket,用来装另外一个型号的汽油。若是咱们本身的两个容器里面有一个是空的,咱们能够直接进行转换汽油。可是咱们本身的两个容器中都有油的时候,两个容器很明显不能进行交换汽油,这个时候咱们须要向隔壁的老李借一个桶 __tempBucket,这样三个容器就能进行转换油箱里面的汽油和桶里面的汽油进行对换了,换完之后把桶还回去。
咱们进行在进行造一个新的小汽车名字是CarContract2,增长一个桶,设定变量为_bucket,做为桶。还须要记录当前汽车的油的型号。设定变量 _gasolineType 为当前油类型,默认是 1类型。设定一个函数 changeGasolineType,进行交换汽油类型,在设定一个函数进行查看当前汽车的类型 getGasolineType 。至此咱们小汽车升级成功。
CarContract2.sol
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 变量,该变量就是函数变量,是临时建立的而且不会被记录在链上的变量,也就是咱们用完就还给隔壁老李了,还回去的时候桶是空的。
咱们的小汽车仍是很简单,咱们在给他加一点东西,规定小汽车要想启动必须关闭车门。
下面咱们再一次修改咱们的小汽车,加一个_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的时候咱们要进行预警。
咱们加入一个 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 就是咱们定义的事件,事件是会在虚拟机上记录一条日志的,我么能够经过查询日志的方式获得事件内容。
咱们的汽车感受成熟了,这个时候咱们要给咱们的汽车打上一些特性,好比颜色,好比车轮数,好比车门数等等。
咱们在小汽车里面加入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就是结构体,结构体中不能进行设置初值,咱们能把一类的属性等进行分类的放在结构体中,能够充当咱们的数据模型。
咱们的小汽车想要开门,须要打开车锁,车锁是一种状态,开/关。
咱们加入枚举类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代的更新,相信你们对于本节课程有空了必定的了解了。智能合约包含的状态变量、函数、函数变量、函数修饰器、事件、结构、枚举类都已经在制做和升级小汽车中使用了。
在solidity中有专门的数据类型,什么是数据类型呢,咱们能够认为数字是类型,是不是类型,地址是类型等。在solidity中存在如下经常使用的类型,uint/int、bool、 address、 mapping、bytes、string、fixed/ufixed 经常使用的类型。
pragma solidity ^0.6.0; contract Test { uint v1 = 1; int v2 = -2; bool v3 = true; address v4 = 0x8a5fa31F2bf83812ECd8E5Ef1878dD12bBaDb40C; mapping(uint => uint) v5; bytes v6 = "0x123"; string v7 = "asd"; fixed v8 = 1.3; ufixed v9 = 1.2; }
uint/int 类型都是整型,也就是都是整数。一、二、三、4相似的数没有小数点。区别是uint是没有符号的。看代码中的 v1 就是无符号整数,v2就是有符号整数。默认是0
bool 中文是布尔类型,表示是/否的类型。v3就是bool类型,只有两个值 true/false,默认是false
address是地址类型,存储地址的,帐户地址,合约地址等,均可以。
映射类型示例中的代码不是很全,我来解释下,实际上他是一个key-value模型,也就是一个key对应一个value。好比咱们说小明,咱们就是到小明这我的。就是这个道理。
定长数组,就是说他的长度是固定的,不能改变,设定是多少就是多少。
字符串类型,能够放字符串,长度是255位二级制数。
浮点类型,就是带有小数点的,1.二、1.四、1.9这类数值。也是分为有无符号项。
在solidity中可使用如下控制结构,有 if,else,while,for,break,continue,return,? : 咱们来以此介绍这些结构。
你们确定好奇什么是if else语句。他就是咱们说的若是 否者。也就是说若是我怎么样否者我怎么样。注意else只能与if一同使用,你们一块儿来看下面代码:
pragma solidity ^0.6.0; contract Test { uint256 temp; function test1(bool doorSwitch) public { if (doorSwitch) { temp = 1; } } function test2(bool doorSwitch) public { if (doorSwitch) { temp = 1; } temp = 2; } function test3(bool doorSwitch) public { if (doorSwitch) { temp = 1; } else { temp = 2; } } function getTemp() public view returns(uint256){ return temp; } }
上面代码中咱们定义了三个测试方法,以及一个获取temp值的方法。第一个测试方法表示若是咱们传进去的doorSwitch是true,门是开的,那么temp就等于1,否者doorSwitch等于false的话temp值不变。第二个测试方法表示若是咱们传进去的doorSwitch是true,门是开的,那么temp就先等于1,而后等于2,否者doorSwitch等于false的话temp直接等于2。第三个测试方法表示若是咱们传进去的doorSwitch是true,门是开的,那么temp就等于1,否者doorSwitch等于false的话temp等于2。
while 语句是循环语句,表示知足条件就一直循环,通常我么会和break来使用,当达到某种状况进行跳出循环,可让循环体自动结束循环。
pragma solidity ^0.6.0; contract Test { function test1() public { int a = 1; while(true) { if (a == 5) { break; } a++; } } function test2() public { int a = 1; while(a != 5) { a++; } } }
上面两个函数内部都有一个循环,实际上两个循环的效果是同样的,有一些细微的差异,须要根据具体业务场景分析了,不建议使用while 一不当心死循环就很差玩了。
for循环概念直接上代码你们好理解些。
pragma solidity ^0.6.0; contract Test { function test1() public { for(uint8 i = 0; i < 10; i ++) { ... } } }
上面就是一个经常使用的for循环使用方式,循环10次 “...” 的内容。
continue语句是跳过本次循环进入下一次循环,来看代码:
pragma solidity ^0.6.0; contract Test { function test1() public { for(uint8 i = 0; i < 10; i ++) { if (i == 3) { continue; } ... } } }
上面的代码是若是i = 3后跳过此次循环。
return 是返回的命令当遇到return时就是表示结束了。看代码:
pragma solidity ^0.6.0; contract Test { function test1() public { for(uint8 i = 0; i < 10; i ++) { if (i == 3) { return; } ... } } }
上面的代码能够看出,运行到i = 3时,程序直接结束。
到此咱们的控制语句基本学习结束。这些概念每一个语言都差很少,因此很重要。多练小伙伴!
学习完上面的课程你们对于智能合约基本已经学习好了,可是仍是缺少使用,只是知道有啥东西,何时用不知道,这个时候须要你们进行锻炼了,多写一些小东西练习。