文章目录
-
- 参考网址
- 修饰符
- mapping
- 数组
- struct 结构体
- modifier
- Events 事件 indexed
- Constructor 构造方法
- 继承 Inheritance
- 调用父类合约
- payable
- 发送以太坊 Sending Ether (transfer, send, call)
- fallback 回调函数,receive,function()
- call
- delegatecall
- function-selector
- 调用其他合约 Calling Other Contract
- 合约中创建合约
- try catch
- Keccak256
- 验证签名 Verifying Signature
- solidity版本变化
参考网址
此处主要是针对链接中,增加中文理解说明,有需要可查看原文
(文中参考代码主要摘自该链接)https://solidity-by-example.org/
修饰符
初始变量修饰符 constants-immutable
- constants 常量,硬编码,不可修改
- immutable 不可变, 可在构造函数中赋值,之后不能修改
- public 外部可访问,public变量会默认有个getter
- private/default 私有,外部访问不了,子类可访问
数据位置 storage-memory-calldata
- storage 全局变量都属于storage,存储在区块中,方法中使用该声明,会对区块信息修改
- memory 存储在内存中,仅调用函数体内有效,可修改值
- calldata 主要针对external类方法的参数,和memory的区别是不可以修改该变量
方法修饰符 view-pure
- view 不会更改任何状态(会读取区块内变量)
- pure 不会更改任何状态且不读取区块内变量
错误 require-revert-assert
- require 执行的前置条件,如果这个不满足,无法往下执行, 且有提示语的话,会出现在浏览器和estimateGas提示
- revert 在某个if判断,可调用revrt,功能和require类似
- assert 断言某个条件是否为真
常用是require/revert, 可以将错误信息返出去,便于知道错误在哪
assert存在意义在哪不确定,可以使用require替代
另外针对assert的区别。 0.8.x测试没发现和require有什么区别,但是在低版本区别很大
例:0.4.x eth-usdt,可以去查看他的源码
如果一个交易gasLimit设置的100w,实际消耗大概是5w,代码最后面增加(require,revert)/ assert 导致出错
如果使用require/revert, 实际gasUsed = gasPrice * 5w,然后回退了
如果使用assert, 实际gasUsed = gasPrice * 100w, 会把gas消耗完…!
方法修饰符 public-private-internal-external
- public 公开的,本合约也可以调用,外部也可以调用
- private 私有的, 仅本合约可以调用
- internal 内部的, 当前合约和子合约可以调用
- external 公开的,当前合约自身不可调用
mapping
key=>value 格式
mapping(address => uint) public myMap;
mapping(address => mapping(uint => bool)) public nested;
myMap[_addr] = _i;
myMap[_addr];
delete myMap[_addr];
数组
pragma solidity ^0.8.7;
contract Array {
// 初始化数组的几种方式
uint[] public arr;//可变长度数组,初始长度0
uint[] public arr2 = [1, 2, 3];//可变长度数组,初始长度3,有对应值
// 固定长度数组,所有元素是默认值,当前例子为 0, 这个数组不可以push/pop改变长度
uint[10] public myFixedSizeArr;
//通过下标获取数组元素
function get(uint i) public view returns (uint) {
return arr[i];
}
//可返回整个数组,这种方法需要避免长度很长可增长的数组
//查询方法也受gasLimit限制,查询过多内容时会超限制
function getArr() public view returns (uint[] memory) {
return arr;
}
//数量很长的分页/区间查询
function getArr1(uint256 pageNo, uint256 pageSize)public view returns(uint256[]memory list) {
uint len = arr.length;
uint start = pageNo * pageSize;
if(len == 0 || start >= len){
return new uint[](0);
}
uint end = start + pageSize;
if(end > len){
end = len;
}
uint arrLen = end - start;
list = new uint[](arrLen);
uint index;
for(;start < end ; start ++){
list[index++] = start;
}
}
function push(uint i) public {
//追加到数组,数组长度加1
arr.push(i);
}
function pop() public {
//移除数组的最后一个元素
arr.pop();
}
//返回数组长度
function getLength() public view returns (uint) {
return arr.length;
}
function remove(uint index) public {
//delete 操作不对修改数组长度,只是把索引位置的值重置为默认值,当前例子为0
delete arr[index];
}
//如果想移除一个值,且改变数组长度,
//可以先替换值, 在pop
//注: 该方式会导致数组值不是原来的插入顺序
function remove2(uint index)public{
arr[index] = arr[arr.length-1];
arr.pop();
}
function examples() external {
// 在内存中创建数组,只能创建固定大小
uint[] memory a = new uint[](5);
}
}
struct 结构体
通过struct将相关数据放一起
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Todos {
struct Todo {
string text;
bool completed;
}
// An array of 'Todo' structs
Todo[] public todos;//public声明, 外部可以通过 todos[index],输入index会返回整个结构体
function create(string memory _text) public {
// 3种初始化结构的方式
// 像方法一样顺序传参
todos.push(Todo(_text, false));
// key value mapping
todos.push(Todo({text: _text, completed: false}));
// 初始化一个空的结构体,并针对每个字段赋值, 如果结构体中存在数组/其他结构体时,这种方式合适
Todo memory todo;
todo.text = _text;
//未显视赋值的变量为类型初始值
// todo.completed initialized to false
todos.push(todo);
}
// Solidity automatically created a getter for 'todos' so
// you don't actually need this function.
function get(uint _index) public view returns (string memory text, bool completed) {
Todo storage todo = todos[_index];
return (todo.text, todo.completed);
}
//如果是低版本的,会要求在文件头部声明 pragma experimental ABIEncoderV2;
function getObject(uint _index)public view returns(Todo memory){
return todos[_index];
}
// update text
function update(uint _index, string memory _text) public {
Todo storage todo = todos[_index];
todo.text = _text;
}
// update completed
function toggleCompleted(uint _index) public {
Todo storage todo = todos[_index];
todo.completed = !todo.completed;
}
}
modifier
可在函数调用前做一定处理,以及执行之后,再做一定处理
主要功能
- 限制访问
- 验证输入
- 防止重入
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
//下划线是一个特殊字符,仅在modifier中, 标志执行方法的其他代码
_;
}
//判断输入的地址不是0地址
modifier validAddress(address _addr) {
require(_addr != address(0), "Not valid address");
_;
}
//防止重入,调用函数前先把状态改了, 函数执行完后, 再把状态改回来
modifier noReentrancy() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
Events 事件 indexed
简单理解就是日志,便于知道区块内部执行了什么
event Transfer(address indexed from, address indexed to, uint value);
日志定义里面有个indexed修饰符, 最多允许3个参数使用该修饰符
event的数据,在transaction中logs中存在两个不同位置,
indexed 修饰的 在topic中 而其他的在data中
"logs": [
{
"address": "0x55d398326f99059ff775485246999027b3197955",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000eb2d2f1b8c558a40207669291fda468e50c12345",
"0x000000000000000000000000bcdc55ce32a3d875d440c6fec0354919ab812345"
],
"data": "0x00000000000000000000000000000000000000000000001b1ae4d6e2ef500000"
}
]
logs中说明
address 表示该事件是哪个合约地址的(注意是实际发出的地址, 比如A合约调用B代币, address是B代币的地址,而不是A合约的地址)
topics 是个数组,topics[0] 表示事件名,后面的就是顺序取事件中的indexed修饰的参数
以下针对Transfer事件的说明
topics[0]对应的内容是 keccak256(bytes('Transfer(address,address,uint256)'))
topics[1]对应的内容是事件中的 from
topics[2]对应的内容是事件中的 to
data 是非indexed修饰的参数,顺序取
去掉前面的0x后, 每64位表示一个参数,解析的时候对应参数类型解析即可
Constructor 构造方法
继承多父类是从左到右的顺序
参考下面代码中注释, 链接中描述顺序是Y-X-child 是错的
// SPDX-License-Identifier: MIT
pragma solidity ^0.8

本文详细介绍了Solidity中的修饰符(如常量、不可变、访问权限)、数据存储位置(storage、memory、calldata)、方法修饰符(view、pure、require-revert-assert)以及事件和继承机制。还涵盖了如何发送与接收以太坊、构造函数、调用其他合约和创建合约等内容,适合深入理解智能合约开发。

642

被折叠的 条评论
为什么被折叠?



