solidity学习日记
下面是我在cryptozombies学习的过程:
https://cryptozombies.io/
./zombiefactory.sol
1 | pragma solidity >=0.5.0 <0.6.0; |
zombiefeeding.sol
1 | pragma solidity >=0.5.0 <0.6.0; |
Solidity 是一种用于编写以太坊虚拟机(EVM)智能合约的编程语言。
1 | // SPDX-License-Identifier: MIT |
第 3-4 行是合约部分。第 3 行创建合约(contract),并声明合约名为 HelloWeb3。第 4 行是合约内容,声明了一个 string(字符串)变量 _string,并赋值为 “Hello Web3!”。
值类型:
- 值类型(Value Type) :包括布尔型,整数型等等,这类变量赋值时候直接传递数值。
- 引用类型(Reference Type) :包括数组和结构体,这类变量占空间大,赋值时候直接传递地址(类似指针)。
- 映射类型(Mapping Type) : Solidity中存储键值对的数据结构,可以理解为哈希表
1. 布尔型:
布尔值的运算符包括:
!(逻辑非)&&(逻辑与,”and”)||(逻辑或,”or”)==(等于)!=(不等于)
1 | // 布尔运算 |
复制代码
在上述代码中:变量 _bool 的取值是 true;_bool1 是 _bool 的非,为 false;_bool && _bool1 为 false;_bool || _bool1 为 true;_bool == _bool1 为 false;_bool != _bool1 为 true。
值得注意的是: && 和 || 运算符遵循短路规则,这意味着,假如存在 f(x) || g(y) 的表达式,如果 f(x) 是 true,g(y) 不会被计算,即使它和 f(x) 的结果是相反的。假如存在f(x) && g(y) 的表达式,如果 f(x) 是 false,g(y) 不会被计算。 所谓“短路规则”,一般出现在逻辑与(&&)和逻辑或(||)中。 当逻辑与(&&)的第一个条件为false时,就不会再去判断第二个条件; 当逻辑或(||)的第一个条件为true时,就不会再去判断第二个条件,这就是短路规则。
2. 地址类型
地址类型(address)有两类:
- 普通地址(address): 存储一个 20 字节的值(以太坊地址的大小)。
- payable address: 比普通地址多了
transfer和send两个成员方法,用于接收转账。
1 | // 地址 |
3枚举 enum
枚举(enum)是 Solidity 中用户定义的数据类型。它主要用于为 uint 分配名称,使程序易于阅读和维护。它与 C 语言 中的 enum 类似,使用名称来代替从 0 开始的 uint:
1 | // 用enum将uint 0, 1, 2表示为Buy, Hold, Sell |
枚举可以显式地和 uint 相互转换,并会检查转换的无符号整数是否在枚举的长度内,否则会报错:
1 | // enum可以和uint显式的转换 |
enum 是一个比较冷门的数据类型,几乎没什么人用。
函数
1 | function <function name>([parameter types[, ...]]) {internal|external|public|private} [pure|view|payable] [virtual|override] [<modifiers>] |
看着有一些复杂,让我们从前往后逐个解释(方括号中的是可写可不 写的关键字):
-
function:声明函数时的固定用法。要编写函数,就需要以function关键字开头。 -
<function name>:函数名。 -
([parameter types[, ...]]):圆括号内写入函数的参数,即输入到函数的变量类型和名称。 -
{internal|external|public|private}:函数可见性说明符,共有4种。public:内部和外部均可见。private:只能从本合约内部访问,继承的合约也不能使用。external:只能从合约外部访问(但内部可以通过this.f()来调用,f是函数名)。internal: 只能从合约内部访问,继承的合约可以用。
注意 1:合约中定义的函数需要明确指定可见性,它们没有默认值。
注意 2:public|private|internal也可用于修饰状态变量(定义可参考WTF Solidity 第5讲的相关内容)。public变量会自动生成同名的getter函数,用于查询数值。未标明可见性类型的状态变量,默认为internal。
-
[pure|view|payable]:决定函数权限/功能的关键字。payable(可支付的)很好理解,带着它的函数,运行的时候可以给合约转入 ETH。pure和view的介绍见下一节。 -
[virtual|override]: 方法是否可以被重写,或者是否是重写方法。virtual用在父合约上,标识的方法可以被子合约重写。override用在自合约上,表名方法重写了父合约的方法。 -
<modifiers>: 自定义的修饰器,可以有0个或多个修饰器。 -
[returns ()]:函数返回的变量类型和名称。 -
<function body>: 函数体。
到底什么是 Pure 和View?
刚开始学习 solidity 时,pure 和 view 关键字可能令人费解,因为其他编程语言中没有类似的关键字。solidity 引入这两个关键字主要是因为 以太坊交易需要支付气费(gas fee)。合约的状态变量存储在链上,gas fee 很贵,如果计算不改变链上状态,就可以不用付 gas。包含 pure 和 view 关键字的函数是不改写链上状态的,因此用户直接调用它们是不需要付 gas 的(注意,合约中非 pure/view 函数调用 pure/view 函数时需要付gas)。
在以太坊中,以下语句被视为修改链上状态:
- 写入状态变量。
- 释放事件。
- 创建其他合约。
- 使用
selfdestruct. - 通过调用发送以太币。
- 调用任何未标记
view或pure的函数。 - 使用低级调用(low-level calls)。
- 使用包含某些操作码的内联汇编。
代码
1. pure 和 view
我们在合约里定义一个状态变量 number,初始化为 5。
1 | // SPDX-License-Identifier: MIT |
定义一个 add() 函数,每次调用会让 number 增加 1。
1 | // 默认function |
如果 add() 函数被标记为 pure,比如 function add() external pure,就会报错。因为 pure 是不配读取合约里的状态变量的,更不配改写。那 pure 函数能做些什么?举个例子,你可以给函数传递一个参数 _number,然后让他返回 _number + 1,这个操作不会读取或写入状态变量。
1 | // pure: 纯纯牛马 |
2. internal v.s. external
1 | // internal: 内部函数 |
我们定义一个 internal 的 minus() 函数,每次调用使得 number 变量减少 1。由于 internal 函数只能由合约内部调用,我们必须再定义一个 external 的 minusCall() 函数,通过它间接调用内部的 minus() 函数。
3. payable
1 | // payable: 递钱,能给合约支付eth的函数 |
我们定义一个 external payable 的 minusPayable() 函数,间接的调用 minus(),并且返回合约里的 ETH 余额(this 关键字可以让我们引用合约地址)。我们可以在调用 minusPayable() 时往合约里转入1个 ETH。
返回值:return 和 returns
Solidity 中与函数输出相关的有两个关键字:return和returns。它们的区别在于:
returns:跟在函数名后面,用于声明返回的变量类型及变量名。return:用于函数主体中,返回指定的变量。
1 | // 返回多个变量 |
在上述代码中,我们利用 returns 关键字声明了有多个返回值的 returnMultiple() 函数,然后我们在函数主体中使用 return(1, true, [uint256(1),2,5]) 确定了返回值。
这里uint256[3]声明了一个长度为3且类型为uint256的数组作为返回值。因为[1,2,3]会默认为uint8(3),因此[uint256(1),2,5]中首个元素必须强转uint256来声明该数组内的元素皆为此类型。数组类型返回值默认必须用memory修饰
以太单位
Solidity中不存在小数点,以0代替为小数点,来确保交易的精确度,并且防止精度的损失,利用以太单位可以避免误算的问题,方便程序员在合约中处理货币交易。
wei: 1gwei: 1e9 = 1000000000ether: 1e18 = 1000000000000000000
时间单位
可以在合约中规定一个操作必须在一周内完成,或者某个事件在一个月后发生。这样就能让合约的执行可以更加精确,不会因为技术上的误差而影响合约的结果。因此,时间单位在Solidity中是一个重要的概念,有助于提高合约的可读性和可维护性。
seconds: 1minutes: 60 seconds = 60hours: 60 minutes = 3600days: 24 hours = 86400weeks: 7 days = 604800




