Solidity 合约开发要掌握的特性和关键词记录
最近看了一些Solidity开发资料,目前latest是0.6.12,记性不好,记录一下。
1.入门扫盲
常用的关键词
变量相关 | 类型 | 备注 |
---|---|---|
uint | 无符号整数 | uint 默认为 uint256的缩写 |
mapping | 映射 | 键值对存储key/val,可嵌套 |
address | 地址类型 | 一般是两种类型:用户或者合约,自带成员函数 transfer 和 send等,更多成员函数 |
struct | 结构体 | 可以有多个属性,类似Go的struct |
enum | 枚举 | 通常由多个常量值构成的自定义类型 |
storage | 引用类型 | 永久存储在链上,类似硬盘 |
memory | 引用类型 | 临时存储,外部函数对某合约调用完时回收,类似内存 |
calldata | 引用类型 | 只读,用来存储函数传的参数,方便的在函数内引用而不占用空间。0.6.9之前只能外部调用 |
delete | 初始化为0 | Solidity没有null和undefined的概念,注意:delete数组某个索引后可能留空位置。 |
bytes | 定长字节数组 | 类似byte[],bytes能“紧打包”,将元素连续存起来,意味使用Gas更低。 |
strings | 特殊数组 | 与bytes相同,但不允许用长度或索引来访问。 |
更多请查看Solidity文档:全局变量 、保留关键字、修饰符、函数可见性 速查表
函数相关 | 类型 | 备注 |
---|---|---|
event | 事件 | 合约和区块链的通讯机制,前端可触发event方法做出反应。 |
emit | 触发事件 | 事件的调用方法 |
indexed | 事件索引 | 最多三个,特殊数据结构,前端能通过filter筛选事件,普通的是日志数据结构。 |
payable | 函数类型 | 表示可以接受ETH的函数 |
Internal | 函数类型 | 内部,类似private,继承关系可使用。 |
external | 函数类型 | 外部,类似public,不能在继承关系中使用。 |
view | 函数修饰符 | 表示函数只读不改,不会消耗Gas |
pure | 函数修饰符 | 表示函数只做计算不读不改,不会消耗Gas |
virtual | 函数类型 | 函数重写,接口默认会进行标记virtual,private不能使用该标记。 |
override | 函数类型 | 函数重写,配合父合约标记的virtual函数使用 |
abstract | 抽象合约 | 定于与现实分离,用于提高扩展性,消除代码重复 |
interface | 接口 | 不实现任何函数,只制定接口,其他合约继承后需要实现 |
library | 库 | 两种使用方法,一种是using for,一种是直接实例化,自动添加库所有方法给一个数据类型 |
is | 继承 | 可用“,”多继承,存在钻石问题,注意顺序加载:base,base-A,base-A-A这样写。 |
import | 导入 | 可以导入其他sol文件 |
constructor() | 构造函数 | 初始化合约调用一次的函数 |
modifier | 函数修饰符 | 修饰已有的函数,并且能自定义条件函数。 |
receive() | 特殊函数 | 一个合约只有一个接收以太函数,在没有payable修饰符还转账情况下自动调用。 |
fallback() | 回退函数 | externall类型,调用不存在的函数时调用,但是转账类型交易如果不加payable会报异常。 |
全局变量 | 类型 | 备注 |
---|---|---|
ether | 费用单位 | 以太单位,类似还有wei、 finney、 szabo |
Now | 时间单位 | 当前时间戳,因为是32位,会有2038问题。类似还有seconds、minutes、hours、days、weeks、years(0.5.0因闰年已移除) |
msg.sender | 全局变量 | 当前调用者的address |
msg.value | 全局变量 | 当前交易的wei数量 |
tx.gasprice | 全局变量 | 交易的 gas 价格 |
block.number | 全局变量 | 当前区块号 |
更多请查看Solidity文档:特殊变量和函数
用过的一些方法
方法名 | 描述 | 备注 |
---|---|---|
require() | 错误处理 | 不满足条件就撤销更改,0.5版新增第二个参数可以抛出message |
assert() | 错误处理 | 和 require 类似,require失败会返回剩下的Gas,assert则不会,出现严重错误时使用。 |
keccak256() | 数学和密码学函数 | 伪随机的散列函数,更多数学和密码学函数 |
uint8() | 类型强转 | uint8 c = a * uint8(b); 类似的有uint256,uint16,但是uint8不能转到uint256。 |
arr.push() | 数组成员方法 | 在尾部加入新元素,类似的还有length、pop |
生成随机数 | 0-100的随机数 | 非安全,可能被算力强的节点利用,只打包对自己有利的区块发布到链上:uint random = uint(keccak256(now, msg.sender, randNonce)) % 100 |
记录一些特别的操作
关键词 | 类型 | 备注 |
---|---|---|
5 ** 2 | 乘方操作 | x 的 y次方,5 ** 2 = 25 |
变量名含"_" | 私有变量名 | 一种习惯,区别全局变量 |
public | 函数修饰符 | 默认会提供getter方法 |
uint[] | 动态数组 | 类似的有uint[10]固定数组,Person[]结构体数组 |
return (a,b,c) | 函数参数和返回 | 批量返回函数值和接受值 跟 Go类似 |
_; | 占位符 | modifier中的_; 表示“被修饰的函数开始执行” |
0x0 | 黑洞地址 | 没有私钥的地址,常用来烧币,主动报废一些token。 |
记录常用的OpenZeppelin安全审查后的合约库
库名 | 描述 | 备注 |
---|---|---|
SafeMath | 安全数学运算 | 可防止 overflow 和 underflow 的加减乘除 |
SafeCast | 安全类型转换 | 和SafeMath类似,防止转换时的 overflow 和 underflow |
Ownable | 访问权限 | 合约的主人才可以调用哦 |
Roles | 角色控制、访问层次 | 比Ownable多了个角色概念,可以进行权限分组了。 |
ERC20 | 代币标准 | 普通代币的一些定义 |
ERC721 | 代币标准 | 用于加密收藏品,有不可分割、不可互换、整单位交易等特性。 |
Address | 地址检测 | 让我看看你是个合约,还是用户 |
具体描述参考:OpenZeppelin 7个最常使用的合约
2.省Gas秘籍
以太坊是去中心化的应用,为了防止某些用户无限循环,占用网络资源。每次执行Dapp都需要支付一定的Gas,Gas使用以太币购买。
利用view特性
view的定义是只读不改,不会真正改变链上任何数据。因此用户从外部调用一个view修饰的函数,不会产生任何Gas。
如果 一个 view 函数有进行 内部调用,虽然不属于同一个合约,但依旧会产生Gas。因为调用的函数会生成事务,要进行节点验证产生成本。所以只有外部调用view是免费的。
结构封装(Struct packing)
struct MiniMe {
uint32 a;
uint32 b;
uint c;
}
在Struct里使用合适的uint类型,并且把同样类型的变量放一起(即在 struct 中将把变量按照类型依次放置), 这样能让Solidity空间最小化。
用uint8代替uint256并不会节省空间,因为Solidity会默认使用256保留。
参考:
https://learnblockchain.cn/docs/solidity/
https://cryptozombies.io/