一个简单的合约申明
pragma solidity ^0.4.21
语言的版本
contract SimpleStorage{
}
类似于类的概念
注意: constant 是定义恒常数,不能改变成员变量。但是,这个版本中,只是警示用,实际程序可以修改的。returns(uint) 定义返回类型。
最新版本中,constant变为view,但是仍然可以改参数;pure是加强版的view,意思是函数中不能修改,也不能读取成员变量,例如:用于加密库之类。
合约的执行
个人简单的理解:
当部署在区块链中时,会申明出这个合约中的方法,然后大家就可以通过执行这些方法来进行相应的交易。
每一个函数执行都是有一定的费用的,简单理解为小费,单位gas,在以太坊的ether是最小的虚拟货币单位,会有价格波动,所以用gas表示函数执行所拥有的小费。比如说,一个函数固定执行一次会有2146gas,但是gas的price是随ether的价格变动而变动的。这样,每一个函数执行的费用就不会随着货币价值的变化而需要修改相应的数量了。
类型
- uint/int ,目前不存在浮点数类型
- address,类似于一个objecct类型,默认值为0x0(包含钱包地址,智能合约的地址等)。.balance地址上的金额,.transfer转钱
一些单位跟常用变量
uint salary = 1 ether; //金额的最小单位wei,本质上就是等于整型的1
uint constant dur = 30 days ;// 时间单位,这里的constant是有效的,申明常变量,但是修饰函数,是没用的
uint lastPday = now;//
块的概念,相当于静态变量(全局变量)。在区块链中,就是挖到的交易块
当执行某个函数时,包含调用用户信息等。
.sender 谁调用了这个函数
.gas 这个函数附带的gas消费
调用函数,给合约塞钱
- 关键字 payable 只有添加这个关键字,才能在执行这个函数时,给这个合约充钱。
- this.balance 这里的this就是指代合约的地址,return this.balance 就是返回执行之后合约上的钱。
- 通过分析2,可以推测函数的执行,虽然是用户点击执行,但是真正执行环境是contract。
给一个地址转钱
注意: 当执行函数时,是执行多少语句就要花费多少gas,如果说遍历很大的数据时,就很贵了。所以如果这个函数出现了异常或者没有按照理想的情况执行,这些gas是拿不回来的。执行revert()函数,能够终止当前函数,并不消耗gas。而throw异常的话,执行到throw前的gas就拿不回来了。
固定执行的用户
变量作用域与js很像
会有变量提升
计算顺序,注意点
在solidity中,除法是做整除,没有小数点。so
当计算a(b-c)/d时,如果计算顺序是a((b-c)/d),会导致误差变大。
构造函数
在合约发布的时候,就执行。
这里的意思是,保存发布这个合约的人。
注意: 构造函数与合约名要一致。
assert && require
assert函数用于确定运行中的代码满足某要求。
require函数用于要求输入的起始条件。
数组
可固定,也可以动态数组。
uint[2] a;//固定长度
uint[] a;//动态长度,这时长度为0,所以无法用a[0]=1进行赋值
a.push(1);//用于增加元素,之后,就可以用a[0]进行获取
delete a[i];
a[i] = a[a.length-1];
a.length -=1;//删除数组中的值,并且把最后一个数放到删除的位置上,长度减一
struct结构
这个就类似于C语言里面的,构造类型。
Employee[] employees;//相当于一个新的类型
Employee(employee,salary,now);//这就新建了一个Employee类型对象了
可视度,函数默认为public
但是一个函数的输入或者输出是自己建的struct,那么函数可视度需要为private。
数据存储
storage 是在区块链上,永久存在
memory 临时空间,当函数运行之后,就会释放
calldata 也是临时空间,类似memory
这里的存储类似于JS,对于一个对象来说,存储的是内存地址。
注意点: 由于函数返回的是memory上的数据。
这里的_findEmployee函数返回相应的状态变量的拷贝。因为函数返回是memory,而状态变量是storage。
这时当修改employee上的值时,存在storage上的状态变量是不会改变的。
为了减少gas的消耗,mapping
就是一个hash数据结构。
只有四种类型做key
由于不能进行遍历,所以下面的语句报错。
解决方法:将uint totalSalary变为状态变量,然后在每次增加或者移除一个employee时,便操作这个totalSalary的值。
命名参数
对于输出也可以直接进行赋值
可视度
internal像protect。
external就是只能是外部用户或者其他合约来调用,而当前合约其他内部函数,是无法调用的。为了能够使用,可以用this.fn1();这样就相当于外部调用,代价会贵一点。
因为在区块链上所有的数据都是公开的。合约的成员变量都是肉眼可见的。
继承
Parent is owned 继承
抽象合约
抽象合约不能部署在区块链上,继承抽象合约的合约需要重新定义其中的函数,类似于重写。
INTERFACE
必须实现继承的interface上的函数,否则因为还有抽象函数在里面,合约不能发布。
多继承
super.func1();//用于绑定上级继承的函数func1
如果多继承的执行函数出现环,就无法进行线性化。
modifier,使得代码工程化
address owner = 0xdfsdfdff;
modifier onlyOwner{
require(msg.sender == owner)
-;//相当于替代修饰的函数剩下的语句,也可以加参数
}
function removeEm(address employ) onlyowner{
}//这样修饰之后就相当于加了一个输入限制
modifier参数来自于当前函数的传入参数或者全局的状态变量。
这里的modifier中的a=1;执行在修饰的函数return之前。
SAFE math
在solidity中,运算是比较危险的。
因为这个数字大部分都跟钱有关系,所以要避免数据的溢出等异常。
但是这样做的话,很麻烦。所以我们加入一些第三方库,来帮我们确保safe math。
例如:
- zeppelin-solidity
import './SafeMath.sol';//进行导入第三方的library
如果要自己编写一个第三方库:
当引入第三方库后,就可以直接使用里面定义的方法了。但是这样也会很麻烦,相当于每次进行操作时,都会调用这个函数。
利用一个语法,using SafeMath for uint8; 这样每当调用SafeMath中的sub函数时,直接用a.sub(100)替代SafeMath.sub(a,100),因为a是uint8类型,可以直接穿透到参数里面。