这是Web3.0学习的第二章Solidity基础,文内如出现知识性和理解性错误还请各位斧正,原创文章,完全开源,若能得转载不胜荣幸,推特会分享Coding学习,生活日常和NBA等内容。**
Solidity基础函数
数组和结构体
错误和警告
变量/常量/Immutable
"函数"或者"方法"指的是独立模块,在我们调用的时候会执行某些指令,Solidity的函数性质和Java一样,函数通过"function"关键字表示。
为了查看一个函数的实际运行结果,我们需要把合约部署在一个测试环境上,把它部署在本地网络,或者叫javascript VM(现在称其为Remix VM),在部署之前,我们需要看看它有没有正确编译。
Remix VM表示合约将被部署到本地的Remix虚拟机上,Remix VM是本地测试用的区块链,在上面可以快速模拟交易,不需要等待测试网的流程。
运行Remix VM的时候,我们有很多账户可以部署,每个账户中都有100个以太币,这些账户和Metamask中的账户类似,区别是,这些是在Remix VM中的测试以太币。
在部署合约的交易中,可以设置Gas limit,同时可以选择要部署的合约
在测试环境中,合约也有一个地址,每个合约都有一个地址,就像每个钱包账户都有地址一样。
部署一个合约其实就是发送一个交易(任何时候你改变链上的任何东西,包括制定一个新的合约,它都会发生在一个交易中),部署一个合约就修改了区块链,让链上拥有这个合约
参数的访问级别如果没有特别指定的话默认是internal.
将参数的访问级别设置为public后的合约:
favoriteNumbre按钮,类似一个函数,显示变量的值。
这些按钮都是被因代码语句而被Solidity自动赋予的视图功能。
权限声明:
函数分为不同的可见性,用户使用不同的关键字进行声明:
public:在外部和内部都可见,任何与合约交互的人,都可以看到favoriteNumber中存的值(public会创建storage(存储)和state(状态)变量的getter函数)给favoriteNumber加上public,实际上就是给favoriteNumber创建了getter函数,我们其实创建了一个函数,返回favoriteNumber的值,上图中的蓝色按钮就是一个返回favoriteNumber值的函数。
带有public的变量favoriteNumber,可以看作是一个返回uint256的view函数
private:表示只有这个合约可以调用这个函数,对于Storage来说,它不是说只有这个合约才能读取它的值。private表示这个合约是唯一可以调用favoriteNumber函数的合约。private表示只对合约内部可见。
external:表示只对合约外部可见,表示合约外的账户可以调用这个函数。
internal:表示只有这个合约或者继承它的合约可以调用。
智能合约的函数越复杂,消耗的计算量就越多,我们花掉的Gas费就越多(The more "stuff" in your function the more gas it costs)
查询数据的合约函数也有不同的声明方式:
view可以读取变量,但不能修改
pure不可以读也不可以修改
Scope(作用域)
favoriteNumber是在Global scope(全局作用域)中,表示所有在花括号中的任何函数都可以获取它。
当你创建一个变量的时候,它只有在这个作用域(花括号)才可见
因为something函数不在store函数中,所以something函数无法获取testVar变量。
这就是作用域的原理,看变量是否在这个花括号中被创建,然后判断是否可以被别的函数获取
标识函数的调用不需要消耗Gas,Solidity中有两个可以把普通函数转换为标识函数的关键字,这两个关键字是view和pure
如果一个函数是view函数,意味着我们只会读取这个合约的状态
例如:retrive函数只读取favoriteNumber的值
view函数和pure函数不允许修改任何状态,但是pure函数也不允许读取区块链数据(所以我们也不能读取favoriteNumber的值)
通过pure函数,你想做的事情可能是这样的,在pure函数中,返回1+1的结果(类似于这样的东西,可能是常用的方法,或者某个不需要读取数据的算法)。
为什么我们调用view或者pure函数,是不需要支付Gas的?
因为只是读区块链数据,记住,只有更改状态的时候才支付Gas。
但是如果一个要改变区块链状态的函数调用了类似retrive这种view或者pure函数才会消耗Gas
假设:store不是view函数,它在某处调用了retrive,那它就要支付retrive的Gas。
关键字return表示,我们调用函数后会得到什么类型的数据。
数组是一种存储同类元素的有序集合,通过uint[] public arr;来进行定义,在定义时可以预先指定数组大小,如uint[10] public myFixedSizeArr;。
需要注意的是,我们可以在内存中创建数组,但是必须固定大小,如uint[] memory a = new uint
数组类型有一些基本操作方法,如下:
//定义数组类型 uint[7] public arr; //添加数据 arr.push(7); //删除最后一个数据 arr.pop(); //删除某个索引值数据 delete arr[1]; //获取数组长度 uint len = arr.length;
示例:
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.8;
contract SimpleStorage {
uint256 public favoriteNumber;
function store(uint256 _favoriteNumber) public {
favoriteNumber = _favoriteNumber;
uint256 testVar = 5;
}
//创建一个函数来返回favoriteNumber,模拟被自动创建的getter函数
//view,pure
function retrieve() public view returns(uint256){
return favoriteNumber;
}
function add() public pure returns(uint256){
return(1+1);
}
}
之前我们的合约允许我们存储一个喜欢的号码,但是如果我们想存储一系列最喜欢的数字,或者我们想存储一大群不同的人,每个人都有不同的所喜欢的数字,该如何去存储呢?
方法引入:
每当你有一个变量列表在一个对象内部时,在Solidity中,它们会自动编入索引。
创建列表的更好方法是使用结构类型,称为Struct,Struct是一种存储列表或一系列对象的方式
Struct
struct是结构类型,对于复杂业务,我们经常需要定义自己的结构,将关联的数据组合起来,可以在合约内进行定义。
各类操作:
contract Struct {
struct Data {
string id;
string hash;
}
Data public data;
//添加数据
function create(string calldata _id) public {
data = Data{id: _id, hash:"111222"};
}
//更新数据
function updata(string _id) public {
//查询数据
string id = data.id;
//更新
data.hash = "222333"
}
}
也可以分离出一个文件定义所有需要的结构类型,由合约按需导入
// 'StructDeclaration.sol'
struct Data {
string id;
string hash;
}
// 'Struct.sol'
import "./StructDeclaration.sol"
contract Struct {
Data public data;
}
示例代码:
//创建一种新的类型
//有点像uint256/boolean/string
struct People {
uint256 favoriteNumber;
string name;
}
// uint256[] public favoriteNumberList;
//People[3] public people;//静态数组(指定数组大小,该数组中只能放下三个人)
People[] public people;//动态数组(数组的大小没有给出,我们不给它一个尺寸,这意味着它可以是任何尺寸,并且数组的大小可以随着我们加减人而增长和缩小)
//0: 2,Patrick | 1: 7,Jon |
function addPerson(string memory _name, uint256 _favoriteNumber) public{
People memory newPerson = People({favoriteNumber: _favoriteNumber, name: _name});
people.push(newPerson);
//上面那两句的效果相当于下面这一句
//people.push(People(_favoriteNumber, _name));
}
错误(红色)
预期得到";"但是实际得到了"}"意味着你的代码没有编译。这些错误意味着你的代码没有编译,它不像预期的那样工作
警告(黄色)
警告不会阻止您的代码工作,但检查它们通常是个好主意,因为它们通常会给出很有见地的信息。
SPDX 许可证未提供标识符
变量是Solidity中可改变值的一种数据结构,分为以下三种:
local变量
state变量
global变量
其中,local变量定义在方法中,而不会存储在链上,如string var = "Hello";
state变量在方法之外定义,会存储在链上,通过string public var;定义变量,写入值时会发送交易,而读取值则不会;
global变量则是提供了链信息的全局变量,如当前区块时间戳变量,uint timestamp = block.timestamp;
合约调用者地址变量,address sender = msg.sender;等。
变量可以通过不同关键字进行声明,表示不同的存储位置。
storage,会存储在链上
memory,在内存中,只有方法被调用的时候才存在
calldata,作为调用方法传入参数时存在
而常量是一种不可以改变值的变量,使用常量可以节约Gas费用,我们可以通过string public constant MY_CONSTANT = "0707";来进行定义。immutable则是一种特殊的类型,它的值可以在constructor中初始化,但不可以再次改变。灵活使用这几种类型可以有效节省Gas费并保障数据安全。