lesson0 : Academy 学习

第一章 入门

一、hello world

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract HelloWeb3{
    string public helloString = "Hello Web3!";
}

二、solidity类型

solidity中的变量类型

  1. 数值类型(Value Type):包括布尔型,整数型等等,这类变量赋值时候直接传递数值。
  2. 引用类型(Reference Type):包括数组和结构体,这类变量占空间大,赋值时候直接传递地址(类似指针)。
  3. 映射类型(Mapping Type): Solidity里的哈希表。
  4. 函数类型(Function Type):Solidity文档里把函数归到数值类型,但我觉得他跟其他类型差别很大,所以单独分一类。

数值类型

布尔型

布尔型是二值变量,取值为true或false。

整型

整型是solidity中的整数,最常用的包括

    // 整型
    int public _int = -1; // 整数,包括负数
    uint public _uint = 1; // 正整数
    uint256 public _number = 20220330; // 256位正整数

常用的整型运算符包括:

比较运算符(返回布尔值): <=, <, ==, !=, >=, >
算数运算符: +, -, 一元运算 -, +, *, /, %(取余),**(幂)

地址类型

地址类型(address)存储一个 20 字节的值(以太坊地址的大小)。地址类型也有成员变量,并作为所有合约的基础。有普通的地址和可以转账ETH的地址(payable)。payable的地址拥有balance和transfer()两个成员,方便查询ETH余额以及转账。

address payable addr;
addr.transfer(1);

表示合约向addr转账1wei

定长字节数组

字节数组bytes分两种,一种定长(byte, bytes8, bytes32),另一种不定长。定长属于数值类型,不定长属于引用类型

 // 固定长度的字节数组
    bytes32 public byte32Type = "MiniSolidity";
    bytes1 public oneByte = byte32Type[0];
枚举 enum

枚举(enum)是solidity中用户定义的数据类型。它主要用于为uint分配名称,使程序易于阅读和维护。它与C语言中的enum类似,使用名称来代替从0开始的uint:

    // 用enum将uint 0, 1, 2表示为Buy, Hold, Sell
    enum ActionSet { Buy, Hold, Sell }
    // 创建enum变量 action
    ActionSet action = ActionSet.Sell;

它可以显式的和uint相互转换,并会检查转换的正整数是否在枚举的长度内,不然会报错:

    // 打印上面的action变量
    function enumToUint() external view returns(uint){
        return uint(action);//输出2
    }

enum的一个比较冷门的变量,几乎没什么人用。

引用类型

数组(array)和结构体(struct)

数组
  • 固定长度数组(静态数组):
	// 固定长度为2的静态数组定义
	uint[2] fixedArray;
	//定长数组实例化
	fixedArray = [4, 6];
  • 可变长度数组(动态数组):
	//声明
	uint[] dynamicArray;
	//初始化,这里实例化一个长度为2的数组,值为0。
	dynamicArray = new uint[](2);
	//bytes比较特殊,是数组,但是不用加[]
	bytes array7;

创建数组的规则
memory 修饰的动态数组,可以用new来创建,但必须声明长度,并且长度不可变。

函数类型

访问控制修饰符
  1. public: 内部外部均可见。(也可用于修饰状态变量,public变量会自动生成 getter函数,用于查询数值).
  2. private: 只能从本合约内部访问,继承的合约也不能用(也可用于修饰状态变量)。
  3. external: 只能从合约外部访问(但是可以用this.f()来调用,f是函数名)
  4. internal: 只能从合约内部访问,继承的合约可以用(也可用于修饰状态变量)。
Pure和View

使用 pureview 关键字的函数是不改写链上状态的,不消耗gas

以下语句被视为修改链上状态:
写入状态变量。
释放事件。
创建其他合同。
使用selfdestruct.
通过调用发送以太币。
调用任何未标记view或pure的函数。
使用低级调用(low-level calls)。
使用包含某些操作码的内联汇编。

pure
不能读取也不能写入存储在链上的状态变量,如:

    // pure: 纯纯牛马 只修改入参或方法本身的变量,与全局变量无关
    function addPure(uint256 _number) external pure returns(uint256 new_number){
        return new_number = _number+1;
    }

带有pure的函数也不能引用修改状态的方法

如果要在pure中进行修改合约状态,那么则会报错:(让你使用view或不加修饰)
image

view
比pure的权限要高一级,能读取但不能修改合约状态,如:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract HelloWeb3{
    uint hello = 100;
     // view: 能读取全局变量hello (看客)
    function addPure(uint256 _number) external view returns(uint256 new_number){
        _number = hello+1;
        return _number;
    }
}
internal 与 external

在部署的时候,无法直接调用合约的internal函数,必须使用external函数进行调用。

我们定义一个internal的minus()函数,每次调用使得number变量减1。由于是internal,只能由合约内部调用,而外部不能。因此,我们必须再定义一个external的minusCall()函数,来间接调用内部的minus()。 Example:
image

payable

当一个函数被 payable 修饰,表示调用这个函数时,可以附加发送一些ETH(当然也可以不发)。
没有加payable的函数,则没有方法接受 ETH, 附加ETH调用会出错。
image

函数返回

方法声明中使用 returns 进行声明返回的变量类型及变量名
方法体中使用 return 返回变量

    // 返回多个变量
    function returnMultiple() public pure returns(uint256, bool, uint256[3] memory){
        return(1, true, [uint256(1),2,5]);
    }
命名式返回

指在方法返回声明中添加需要返回的变量名,则在方法中末尾自动将这些变量名进行返回,不需要在方法体中显式声明
如上可改写为下方法,自动将三个变量return

    // 命名式返回
    function returnNamed() public pure returns(uint256 _number, bool _bool, uint256[3] memory _array){
        _number = 2;
        _bool = false;
        _array = [uint256(3),2,1];
    }
解构式赋值

可以在读取方法返回值时,部分读取或全部读取方法返回值。如:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract HelloWeb3{
    //解构式赋值方式1,将函数返回值赋值给方法变量
    function getValue1() public pure{
        uint256 _number;
        bool _bool;
        uint256[3] memory _array;
        (_number, _bool, _array) = returnNamed();
    }
    //解构式赋值2,将函数返回值赋值给方法变量,省略不需要的返回值
    function getValue2() public pure{
        bool _bool;
        (, _bool, ) = returnNamed();
    }
    function returnNamed() public pure returns(uint256 _number, bool _bool, uint256[3] memory _array){
        return(1, true, [uint256(1),2,5]);
    }
}

变量数据存储和作用域

storage/memory/calldata

数组(array),结构体(struct)和映射(mapping),这类引用类型变量占空间大,赋值时候直接传递地址(类似指针)。由于这类变量比较复杂,占用存储空间大,我们在使用时必须要声明数据存储的位置。

storage,memory和calldata

solidity数据存储位置有三类:storage,memory和calldata。不同存储位置的gas成本不同。
storage类型的数据存在链上,类似计算机的硬盘,消耗gas多
memory和calldata类型的临时存在内存里,消耗gas少。大致用法:

1. storage:合约里的状态变量默认都是storage,存储在链上。

2. memory:函数里的参数和临时变量一般用memory,存储在内存中,不上链。

3. calldata:和memory类似,存储在内存中,不上链。(只读)。与memory的不同点在于calldata变量不能修改(immutable),一般用于函数的参数。例子:

    function fCalldata(uint[] calldata _x) public pure returns(uint[] calldata){
        //参数为calldata数组,不能被修改
        // _x[0] = 0 //这样修改会报错
        return(_x);
    }

数据位置和赋值规则

1、合约的 storage 类型变量赋值给方法中的 storage 变量时,时引用赋值,如果改动,则都会改动。如下代码,调用modify方法时,会更改x 和 a 数组的第二个数:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract HelloWeb3{

     event Log(uint[]);

    uint[] public x = [1,2,3];

    function modify() public {
        uint[] storage a = x;
        x[1] = 1;
        emit Log(x);
        emit Log(a);
    }

}
日志打印

日志打印:
如果想要实现类似打印的功能,在合约中创建一个event,起名为 Log
在想要打印日志的地方调用事件 emit Log(...)如:

event log(int);
emit log();

image

2、storagememory 相互赋值时,会创建独立副本,修改其中一个不会影响另一个。


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract HelloWeb3{
     event Log(uint[]);
    uint[]  public  x = [1,2,3];
    function modify() public {
        uint[] memory a = x;
        a[1] = 1;
        emit Log(x);
        emit Log(a);
    }
}

image

3、memory赋值给memory,会创建引用,改变新变量会影响原变量。
image

4、变量赋值给storage,会创建独立的复本,修改其中一个不会影响另一个。

变量作用域

1、全局变量:solidity预留的关键字,可以不声明直接使用。如下:

    function global() external{
        address sender = msg.sender;
        uint blockNum = block.number;
        bytes memory data = msg.data;
    }

2、状态变量:声明在合约内,函数外。(类似于java的全局变量)gas消耗高。存储在链上。
3、局部变量:晟敏在方法中,存储在内存中,不上链,gas消耗低。

一些预置的全局变量:链接

方法 说明 类型
blockhash(uint blockNumber) 给定区块的哈希值 – 只适用于256最近区块, 不包含当前区块。 bytes32
block.coinbase 当前区块矿工的地址 address payable
block.gaslimit 当前区块的gaslimit uint
block.number 当前区块的number uint
block.timestamp 当前区块的时间戳,为unix纪元以来的秒 uint
gasleft() 剩余 gas uint256
msg.data 完整call data bytes calldata
msg.sender 消息发送者 (当前 caller) address payable
msg.sig calldata的前四个字节 (function identifier) bytes4
msg.value 当前交易发送的wei值 uint
posted @ 2023-04-26 15:35  Furaooooo  阅读(39)  评论(0)    收藏  举报