Solidity总结

Solidity总结

在学习Solidity过程中遇到的问题和一些经验总结。

参考学习网站

Solidity语法

函数

view 和 pure

img

  • view可以读取但是不能修改。
  • 在本地执行不消耗gas。

img

  • pure不可以读取也不能修改。
  • 在本地执行不消耗gas。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pragma solidity >=0.4.0 <0.6.0;

contract User {

// 状态变量
uint public user_age = 12;

// view修饰的条件(只读取状态,但不修改状态)
// 本地运行,不消耗gas
function get_age() public view returns(uint){
return user_age;
}

// pure修饰的条件(不读取且不修改任何状态)
// 本地运行,不消耗gas
function get_pure() public pure returns(string memory){
return "hello";
}
}

所以view和pure两个关键字都是为了更为严格的保护数据,即保证函数不去修改state value的值。

同时,pure更加的严格,保证了连state value读也不读。

internal 和 external

  • 内部(internal)函数只能在当前合约内被调用(在当前的代码块内,包括内部库函数,和继承的函数中),访问函数直接用函数名 func
  • 外部(external)函数由地址和函数方法签名两部分组成,可作为外部函数调用的参数,或返回值,访问函数用 this.func

变量

主要数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
uint8;
uint256;
int8;
int256;
address;

int256 public minInt = type(int256).min;
int256 public maxInt = type(int256).max;
address public addr = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;

/*
In Solidity, the data type byte represent a sequence of bytes.
Solidity presents two type of bytes types :

- fixed-sized byte arrays
- dynamically-sized byte arrays.

The term bytes in Solidity represents a dynamic array of bytes.
It’s a shorthand for byte[] .
*/
bytes1 a = 0xb5;
bytes1 b = 0x56;

// Default values
// Unassigned variables have a default value
bool public defaultBoo; // false
uint256 public defaultUint; // 0
int256 public defaultInt; // 0
address public defaultAddr; // 0x0000000000000000000000000000000000000000

位置

  • 本地
    • 在函数体中定义。
    • 不存储在链上。
  • 链上
    • 在函数体外定义。
    • 在链上存储。
  • 全局
    • 提供关于区块链的信息。
1
2
3
// Here are some global variables
uint256 timestamp = block.timestamp; // Current block timestamp
address sender = msg.sender; // address of the caller

常量

常量是不能修改的变量。

它们的值是硬编码的,使用常量可以节省gas成本。

1
2
3
4
// coding convention to uppercase constant variables
address public constant MY_ADDRESS =
0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc;
uint256 public constant MY_UINT = 123;

不可变常量

不可变变量就像常量。不可变变量的值可以在构造函数内部设置,但之后不能修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Immutable {
// coding convention to uppercase constant variables
address public immutable MY_ADDRESS;
uint256 public immutable MY_UINT;

constructor(uint256 _myUint) {
MY_ADDRESS = msg.sender;
MY_UINT = _myUint;
}
}

读写花费

对于状态变量(state value)来说:

  • 读不花费。
  • 写花费。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract SimpleStorage {
// State variable to store a number
uint256 public num;

// You need to send a transaction to write to a state variable.
function set(uint256 _num) public {
num = _num;
}

// You can read from a state variable without sending a transaction.
function get() public view returns (uint256) {
return num;
}
}

以太币和wei

交易需要花掉以太币,以太币和wei的换算关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract EtherUnits {
uint256 public oneWei = 1 wei;
// 1 wei is equal to 1
bool public isOneWei = (oneWei == 1);

uint256 public oneGwei = 1 gwei;
// 1 gwei is equal to 10^9 wei
bool public isOneGwei = (oneGwei == 1e9);

uint256 public oneEther = 1 ether;
// 1 ether is equal to 10^18 wei
bool public isOneEther = (oneEther == 1e18);
}

Gas

You pay gas spent * gas price amount of ether, where

  • gas is a unit of computation
  • gas spent is the total amount of gas used in a transaction
  • gas price is how much ether you are willing to pay per gas

Transactions with higher gas price have higher priority to be included in a block.

Unspent gas will be refunded.

There are 2 upper bounds to the amount of gas you can spend

  • gas limit (max amount of gas you’re willing to use for your transaction, set by you)
  • block gas limit (max amount of gas allowed in a block, set by the network)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Gas {
uint256 public i = 0;

// Using up all of the gas that you send causes your transaction to fail.
// State changes are undone.
// Gas spent are not refunded.
function forever() public {
// Here we run a loop until all of the gas are spent
// and the transaction fails
while (true) {
i += 1;
}
}
}

数据结构

Mapping

Maps are created with the syntax mapping(keyType => valueType).

The keyType can be any built-in value type, bytes, string, or any contract.

valueType can be any type including another mapping or an array.

Mappings are not iterable.(不可遍历)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Mapping {
// Mapping from address to uint
mapping(address => uint256) public myMap;

function get(address _addr) public view returns (uint256) {
// Mapping always returns a value.
// If the value was never set, it will return the default value.
return myMap[_addr];
}

function set(address _addr, uint256 _i) public {
// Update the value at this address
myMap[_addr] = _i;
}

function remove(address _addr) public {
// Reset the value to the default value.
delete myMap[_addr];
}
}

contract NestedMapping {
// Nested mapping (mapping from address to another mapping)
mapping(address => mapping(uint256 => bool)) public nested;

function get(address _addr1, uint256 _i) public view returns (bool) {
// You can get values from a nested mapping
// even when it is not initialized
return nested[_addr1][_i];
}

function set(address _addr1, uint256 _i, bool _boo) public {
nested[_addr1][_i] = _boo;
}

function remove(address _addr1, uint256 _i) public {
delete nested[_addr1][_i];
}
}

Array

Array can have a compile-time fixed size or a dynamic size.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Array {
// Several ways to initialize an array
uint256[] public arr;
uint256[] public arr2 = [1, 2, 3];
// Fixed sized array, all elements initialize to 0
uint256[10] public myFixedSizeArr;

function get(uint256 i) public view returns (uint256) {
return arr[i];
}

// Solidity can return the entire array.
// But this function should be avoided for
// arrays that can grow indefinitely in length.
function getArr() public view returns (uint256[] memory) {
return arr;
}

function push(uint256 i) public {
// Append to array
// This will increase the array length by 1.
arr.push(i);
}

function pop() public {
// Remove last element from array
// This will decrease the array length by 1
arr.pop();
}

function getLength() public view returns (uint256) {
return arr.length;
}

function remove(uint256 index) public {
// Delete does not change the array length.
// It resets the value at index to it's default value,
// in this case 0
delete arr[index];
}

function examples() external {
// create array in memory, only fixed size can be created
uint256[] memory a = new uint256[](5);
}
}

例子1:将数组中脚标处的某个元素删除。(将数组整体前移)

1
2
3
4
5
6
7
8
function remove(uint256 _index) public {
require(_index < arr.length, "index out of bound");

for (uint256 i = _index; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
arr.pop();
}

例子2:将数组中脚标处的某个元素删除。(将数组看做一个set,删除后不保证数组的顺序)

1
2
3
4
5
6
function remove(uint256 index) public {
// Move the last element into the place to delete
arr[index] = arr[arr.length - 1];
// Remove the last element
arr.pop();
}

Enum

Solidity supports enumerables and they are useful to model choice and keep track of state.

Enums can be declared outside of a contract.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Enum {
// Enum representing shipping status
enum Status {
Pending,
Shipped,
Accepted,
Rejected,
Canceled
}

// Default value is the first element listed in
// definition of the type, in this case "Pending"
Status public status;

// Returns uint
// Pending - 0
// Shipped - 1
// Accepted - 2
// Rejected - 3
// Canceled - 4
function get() public view returns (Status) {
return status;
}

// Update status by passing uint into input
function set(Status _status) public {
status = _status;
}

// You can update to a specific enum like this
function cancel() public {
status = Status.Canceled;
}

// delete resets the enum to its first value, 0
function reset() public {
delete status;
}
}

Struct

You can define your own type by creating a struct.

They are useful for grouping together related data.

Structs can be declared outside of a contract and imported in another contract.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Todos {
struct Todo {
string text;
bool completed;
}

// An array of 'Todo' structs
Todo[] public todos;

function create(string calldata _text) public {
// 3 ways to initialize a struct
// - calling it like a function
todos.push(Todo(_text, false));

// key value mapping
todos.push(Todo({text: _text, completed: false}));

// initialize an empty struct and then update it
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(uint256 _index)
public
view
returns (string memory text, bool completed)
{
Todo storage todo = todos[_index];
return (todo.text, todo.completed);
}

// update text
function updateText(uint256 _index, string calldata _text) public {
Todo storage todo = todos[_index];
todo.text = _text;
}

// update completed
function toggleCompleted(uint256 _index) public {
Todo storage todo = todos[_index];
todo.completed = !todo.completed;
}
}

数据位置

Variables are declared as either storage, memory or calldata to explicitly specify the location of the data.

  • storage - variable is a state variable (store on blockchain)(链上,修改需要花gas)
  • memory - variable is in memory and it exists while a function is being called
  • calldata - special data location that contains function arguments(只读)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract DataLocations {
uint256[] public arr;
mapping(uint256 => address) map;

struct MyStruct {
uint256 foo;
}

mapping(uint256 => MyStruct) myStructs;

function f() public {
// call _f with state variables
_f(arr, map, myStructs[1]);

// get a struct from a mapping
MyStruct storage myStruct = myStructs[1];
// create a struct in memory
MyStruct memory myMemStruct = MyStruct(0);
}

function _f(
uint256[] storage _arr,
mapping(uint256 => address) storage _map,
MyStruct storage _myStruct
) internal {
// do something with storage variables
}

// You can return memory variables
function g(uint256[] memory _arr) public returns (uint256[] memory) {
// do something with memory array
}

function h(uint256[] calldata _arr) external {
// do something with calldata array
}
}
  • storage,memory,calldate三个属性区分变量存储的位置,可以显式的定义。对数据的位置进行了区分。
作者

Dicemy

发布于

2024-08-19

更新于

2024-09-07

许可协议

评论