Solidity学习笔记
Solidity编译器。Solidity代码。
以太坊
共识协议有
- pow(工作量证明)
- pos(权益证明)
结点类型有
- 普通节点:备份区块链数据
- 矿工:创建区块链
加密算法使用的是ECC(椭圆曲线加密算法)
通信协议使用的是Whisper
存储平台使用的是Swarm
geth是以太坊,whipster和swarm节点的一个实现
solidity流程
文件组成
Solidity文件,扩展名为.sol,由4个结构组成
- 预编译指令
- 注释
- 导入
- 合约/库/接口
使用下面指令指定编译器版本
pragma Solidity <version number>
注释形式有
- 单行注释 :用双斜线表示//
- 多行注释 :用/*和*/表示
- 以太坊自然规范:即三个斜杠///表示单行注释,多行注释用/**表示注释开始,*/表示注释结束
导入其它合约使用
import "otherContract"
合约使用关键字contract,库使用关键字library,接口使用关键字interface
合约
合约结构包含
- 状态变量
- 结构定义:使用struct
- 修改器定义:使用关键字modifier,修改器与函数绑定。修改器中的下划线_表示执行目标函数。payable是由Solidity提供的修改器,当应用于函数时允许函数接受以太币
- 事件声明:使用关键字event来定义
- 枚举定义
- 函数定义:函数支持的限定符有external, payable(k只能接受以太币), view(常量函数别名),pure(既不能读取也不能写入,不能访问状态变量),其修饰符默认是public
状态变量修饰符有
- internal:在没有显示指定情况下,默认是internal
- private
- public
- constant
数据类型支持的有
- bool
- uint/int:从8位到256位,没有指定位数时,表示的是256位
- bytes
- address
- mapping
- enum
- struct
- string
合约创建
有两种方式
- 使用new创建
contract Helloworld {
}
contract client {
function createHelloworld() public returns (Helloworld address) {
return new Helloworld()
}
}
- 使用已经部署的合约地址
contract client {
function useHelloworld(address addr) public {
Helloworld hw = Helloworld(addr)
}
}
构造函数
在用new创建合约时,会调用构造函数。构造函数只会调用一次,也只能有一个构造函数,不能重载。
contract Helloworld {
construct([参数]) {
}
}
继承
单继承:使用形式为子合约 is 父合约
多继承:使用形式为子合约 is 父合约1,父合约2,...
在调用继承体系中的函数时,可以直接使用父合约.函数
,直接调用其直接父合约使用super.函数
(在多继承中时,按照父合约声明顺序调用)
需要重载的函数使用virtual
修饰符,子合约重载父合约函数使用override
修饰符
重载时支持修改函数的可见性(从不严格转为严格的,不能从严格的转不严格的)有
external
转public
nonpayable
转view
和pure
view
转pure
payable
不能转
修改器跟函数一样支持重载,也是使用virtual
和override
对于有参数的构造函数,有两种方式
- 直接在继承声明时指定父合约的参数
- 在子合约构造函数中,显示指定父合约参数
否则只能将合约声明为抽象的
抽象合约
合约前使用abstract
,即合约中函数没有实现
接口
关键字为interface
,约束有
- 不能包含实现的函数
- 不能继承合约,可以继承其它接口
- 所有函数必须是
external
- 不能声明构造函数
- 不能声明状态变量
- 不能声明修改器
数据类型
值类型有
- bool
- uint/int
- address:分为payable(包含transfer和send)和不能支付的,
- byte:是bytes1的别名,字节类型还支持1-32,即bytes1-bytes32
- enum
- 定长浮点型:fixed和ufixedMxN,其中M表示所占的大小,需要为8的位数,范围为8-256,N表示小数点的位数,范围为0-80
- 定长数组:即固定数组
int[5] age
- 函数类型:对于external和public函数,其包含两个属性.address(合约地址)和.selector(abi函数选择器)
- 用户自定义值类型:如type aliasName as built-in-type
- 合约类型:可以与地址作相互转换
引用类型有
- 动态数组:
int[] age
。动态数组可以使用内联初始化或者使用new运算符初始化,内联形式为int[] age = [1, 2,3]
,new初始化形式为int[] age = new int[](5)
。solidity中有特殊数组类型,即字节数组bytes
和字符串数组string
- 数组切片
- 结构
- 映射
数据存储位置
存储位置支持有
- 存储:局部复杂类型变量和状态变量默认存储位置是存储
- 内存:函数参数包含返回值默认存储位置是内存
- 调用数据:外部函数参数只读,不包含返回值
- 堆栈
字面量
字符串字面量用单引号或者双引号
十六进制字面量以前缀hex作为关键字,如hex "1A2B3F"
全局变量和函数
区块
blockhash(uint blockNumber) returns (bytes32)
:当 blockNumber是 256 个最新区块之一时,给定区块的哈希值;否则返回零blobhash(uint index) returns (bytes32)
:与当前事务关联的索引第index个 Blob 的版本化哈希。版本化哈希由代表版本(当前0x01)的单个字节组成,后跟 KZG 承诺 (EIP-4844) 的 SHA256 哈希的最后 31 个字节。如果不存在具有给定索引的 blob,则返回零。block.basefee (uint)
:当前区块的基本费用(EIP-3198 和 EIP-1559)block.blobbasefee (uint)
:当前区块的 blob 基本费用(EIP-7516 和 EIP-4844)block.chainid (uint)
:当前链 IDblock.coinbase (address payable)
:当前区块矿工地址block.difficulty (uint)
:当前区块难度(EVM < Paris)block.prevrandao (uint)
:信标链提供的随机数 (EVM >= Paris)block.gaslimit (uint)
:当前块 gas限制block.number (uint)
:当前区块编号block.timestamp (uint)
:当前区块时间戳(自 Unix 纪元以来的秒数)
消息
msg.data (bytes calldata)
:完整的调用数据,与创建交易相关的函数及其参数信息msg.sender (address)
:调用函数的地址msg.sig (bytes4)
:函数标识符使用散列函数签名后的前四个字节msg.value (uint)
:随消息发送的以太币数量,以wei为单位
交易
tx.gasprice (uint)
:交易的 gas 价格tx.origin (address)
:交易的发送者(全调用链)
加密
keccak256(bytes memory) returns (bytes32)
:计算输入的 Keccak-256 哈希值,其基于SH3算法sha256(bytes memory) returns (bytes32)
:计算输入的 SHA-256哈希值,基基于SH2算法
地址
<address>.balance (uint256)
:是address类型的内置全局变量,用于查询某个地址的ETH余额,单位为wei
全局函数有
<address payable>.transfer(uint256 amount)
:安全转换函数,向目标地址发送指定数量的ETH,若转帐失败,自动回滚当前交易。要求目标地址是address payable
类型<address payable>.send(uint256 amount) returns (bool)
:低级转账函数,但是转账失败,不会自动 回滚<address>.call(bytes memory) returns (bool, bytes memory)
:通用低级调用函数,向目标地址发送一个自定义数据的调用请求<address>.delegatecall(bytes memory) returns (bool, bytes memory)
:委托调用函数,与call类似,但是使用当前合约的上下文<address>.staticcall(bytes memory) returns (bool, bytes memory)
:只读调用函数,禁止修改任何状态
合约
合约全局变量有
this
:表示当前合约的类型,可以显示转换成地址address contractAddress = address(this);
合约全局函数有
selfdestruct(address payable recipient)
:销毁当前合约,并且把剩余的以太币余额转移到参数指定地址
库
库里的内部函数是以复制的形式到调用它的合约中,内部类型可以传递,内存类型以引用传递
有以下限制
- 无状态变量
- 不能继承和被继承
- 不能接收以太币
- 不能销毁一个库
使用场景
- 节省gas
- 给数据类型添加成员函数
ABI
有两种形式,一种是参数二进制编码,一种是json形式
参数编码(二进制形式)
首先是4字节的函数签名,然后是对函数参数的编码
对于静态类型和动态类型的编码方式不一样,动态类型有
- string
- bytes
- 定长数组
- 变长数组
- 元组(T1,…,Tk)
元组编码形式为
enc(X) = head(X(1)) … head(X(k)) tail(X(1)) … tail(X(k))
对于静态类型,head(X(i)) = enc(X(i)), tail(X(i)) = “”
对于动态类型,head(X(i)) = enc(len( head(X(1)) … head(X(k)) tail(X(1)) …tail(X(i-1)) )), tail(X(i)) = enc(X(i))
更多推荐
所有评论(0)