Web3.0学习树|5.Solidity基础 Ⅱ
November 25th, 2022

这是Web3.0学习的第二章Solidity基础,文内如出现知识性和理解性错误还请各位斧正,原创文章,完全开源,若能得转载不胜荣幸,推特会分享Coding学习,生活日常和NBA等内容。**

目录

  • Solidity基础函数

  • 数组和结构体

  • 错误和警告

  • 变量/常量/Immutable

Solidity基础函数

"函数"或者"方法"指的是独立模块,在我们调用的时候会执行某些指令,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 许可证未提供标识符

变量/常量/Immutable

变量是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费并保障数据安全。

Subscribe to 链上巫师
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from 链上巫师

Skeleton

Skeleton

Skeleton