开始ETH的学习之路把,炒币也要了解下相关原理 ( ̄▽ ̄)”
名词学习
EVM:指以太坊虚拟机,以太坊的运行环境,智能合约真正执行代码的地方,EVM的存在是为了“让全网节点按同样规则执行智能合约”。
节点:指参与以太坊网络的一台机器,区块链网络里的参与者,通常负责保存区块链数据、验证区块和交易、执行合约代码、提供RPC接口。
区块:指一批交易和相关信息打包后的数据块,区块链账本里的一页,通常会有多笔交易,上一个区块的信息,时间相关信息,区块元数据。
外部账户 EOA:即Externally Owned Account 外部账户,人控制的钱包账户,我们使用MetaMask,OKX Wallet等创建的就是EOA。
合约账户:即部署在链上的智能合约地址,没有私钥,不能主动发起交易,代码控制的账户。
Gas:在以太坊执行操作所消耗的计算量单位,比如普通ETH转账,要消耗一定的Gas,调用合约函数要消耗更多的Gas。
Gas Limit:这笔交易最多愿意消耗多少Gas,可以理解为预算上限,当Gas Limit 太低导致执行中途消耗完了,会交易失败,状态回滚,已消耗的Gas不退还
Gas Price:Gas Used * Gas 单价
Base Fee:EIP-1559后的概念,即网络规定的基础手续费单价
Priority Fee:给验证者的小费
wei和gwei:1 ETH = 10^18 wei,1 gwei = 10^9 wei,1 gwei = 0.000000001 ETH,1 ETH = 1,000,000,000 gwei
Gas消耗举个例子:
普通转账消耗 Gas Used = 2100
假设 Base Fee = 20 gwei, Priority Fee = 2 gwei
那么实际单价就是 22gwei,总费用就是 21000 * 22gwei
签名:即证明这笔交易确实是这个账户持有人授权发出的,因为EOA有私钥,所以可以对交易数据做数字签名,从而证明 发起人有这个地址对应的私钥(持有钱包)。
Nonce:账户发出的交易序号,通常表示账户已经发送过多少笔交易,作用是防止重放,保证顺序。
交易回执:交易执行完后,链上返回的“结果单据”,包括交易哈希,所在区块,是否成功,消耗Gas等
Solidity相关语法
由于已经是一名程序猿了,所以简单的语法不需要记录了,来了解下Solidity核心语法主干就行了
合约结构
1 | // SPDX-License-Identifier: MIT |
常用数据类型
- uint/uint256:无符号整数,最常用,uint其实就是uint256,不过平常会显性写成uint256
- int/int256:有符号整数,可正可负
- bool:布尔值
- address:地址类型,外部账户或者合约地址
- string:字符串
- bytes:动态字节数组 1
- bytes32:固定32字节,常用于哈希值,标识 1
常量与不可变变量
constant:编译期常量,部署后永远不变,更省gas
1
uint256 public constant MAX_SUPPLY = 1000000;
immutable:部署时确认,之后不变,运行时第一次赋值,后面不在修改
1
2
3
4
5address public immutable owner;
constructor() {
owner = msg.sender;
}
函数
函数结构拆开来看如下:
function:函数关键字set:函数名(uint256 x):参数public:可见性{ ... }:函数体returns: 返回值returns (uint256, bool)多返回值returns (uint256 value, bool ok)命名返回值pure既不读状态,也不改状态。view只读,不改状态payable:代表函数或者地址可以接受ETH
1 | function set(uint256 x) public { |
可见性
public
内外都可以调用,无论是外部账户调用还是合约内部调用
1 | function foo() public {} |
external
只能从外部调用更合适,常用于对外接口,内部合约也可以调用,只是要this.xxx一下
1 | function bar(uint256 x) external {} |
internal
只能合约内部和子合约才能调用
1 | function _calc() internal {} |
private
只能当前合约内部调用,子合约也不行
1 | function _secret() private {} |
数据位置
这个算是Solidity最关键的语法了,因为涉及钱
Storage
链上持久化存储,永久保存到链上,最贵
1 | uint256 public value; |
Memory
临时内存,只在函数执行期间存在。临时,函数执行完后消失,比storage便宜
1 | function foo() public pure returns (string memory) { |
calldata
外部函数参数只读区。只读、常用于external函数参数,更省gas
错误处理
require
条件不满足就回滚,用于输入检查和权限检查
1 | require(msg.sender == owner, "not owner"); |
revert
手动版require,不满足条件手动回滚
1 | if (amount > balance) { |
自定义错误error,结构化、更清晰、更省gas
1 | error InsufficientBalance(uint256 requested, uint256 available); |
事件
事件是链上和链下交互的重要桥梁,给前端监听、给后端索引、给区块浏览器展示、给外部系统感知链上变化
1 | event Transfer(address from, address to, uint256 amount); // 定义事件 |
复杂数据结构
mapping
就是键值对,按key存值,常用于余额、权限、白名单等,注意不能遍历,如果key不存在返回默认值
1 | mapping(address => uint256) public balances; |
array
就是数组,分为动态数组和固定长度数组
1 | uint256[3] public nums; // 固定长度为3的uint256数组 |
struct
自定义复合结构体,做对象
1 | struct User { |
enum
有限状态枚举,基本用于状态机这种
1 | enum Status { Pending, Active, Closed } |
构造函数,modifier,继承
constructor
部署时只会执行一次,通常用于初始化合约owner,参数,配置等
1 | constructor() { |
modifier
modifier可以理解成AOP切面的语法,就是用来通用前置校验,基本用于权限检查
_就是被修饰函数的函数体
1 | modifier onlyOwner() { |
继承 is
就是代码复用,逻辑扩展,没啥特殊
1 | contract Ownable { |
全局变量和特殊单位
msg.sender:当前调用者地址
msg.value:本次调用附带的ETH数量
block.timestamp:当前区块时间戳
wei、gwei、ether:ETH不同单位
特殊函数
receive
当有ETH向这个合约转账时会触发该函数
1 | receive() external payable {} |
fallback
当执行了该合约不存在的函数时会被调用
1 | fallback() external payable {} |
转账
1 | (bool success, ) = payable(to).call{value: amount}(""); |