0%

solidity | 数组

这一章讲一下 solidity 的数组。

声明 T[K]

  • T 元素类型
  • K 数组长度「可选」
    • uint [10] tens
    • uint [] us
    • uint [] public c = [1,2,3] // public 数组状态会自动生成一个对应变量的函数
  • 使用 new 关键字
    • uint [] c = new uint[](7); // storage
    • 这种方式必须要指定长度

通过下标访问

  • tens[0] = 10

多维数组

solidity 多维数组和其他数组不一样的是

- solidity:uint[2][3] 它是列前行后,即是 3 行 2 列的数组
- python: [2][3] 是 2 行 3 列
  • uint[][5] a // 5 个数组都是变长数组

数组成员

  • .length
    • 数组长度
    • storage 的变长数组,可以通过 .length 赋值调整数组长度
    • memory 数组一旦创建,无法改变长度
  • push()
    • 添加新元素,返回新长度
    • 仅针对变长数组
    • memory 数组不支持

案例

基本功能

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract Test {

constructor() {
}

uint[2][3] T =[[1,2],[3,4],[5,6]];
uint[][] B;
// new的方式第一个参数(列)必须指定。
uint[2][] twoArray2 = new uint[2][](4);

// error, 其它方式不支持
// uint[][2] twoArray2 = new uint[][2](4);

function getList(uint i)public view returns(uint[2] memory){
return T[i];
}

function getLength() public view returns(uint){
return T.length;
}

function getListLength(uint i)public view returns(uint){
return T[i].length;
}

function getElement(uint i,uint j) public view returns(uint){
return T[i][j];
}

function initB() public {
B = [[1,2],[2,3]];
}

function getB() public view returns(uint[][] memory){
return B;
}

function add() public returns(uint[][] memory) {
B.push([1,2]);
B.push([1,2,3]);
return B;
}
}
  • getList
    • getList(1)
      • [3,4]
  • getLength
    • 3
  • getListLength
    • getListLength(1)
      • 2
  • getElement
    • getElement(2,1)
      • 6
    • getElement(0,1)
      • 2
  • initB
    • 这个是变长初始化,如果要给定具体的数值,那么,必须都是定长的,下面这个是错误的
    • B = [[1],[2,3]]
  • 综合状态
    • initB() -> getB()
      • [[1,2],[2,3]]
  • add
    • add() -> getB()
      • [[1,2],[1,2,3]]
  • 综合操作
    • initB() -> add() -> getB()
      • [[1,2],[2,3],[1,2],[1,2,3]]

上面总结了定长、变长等数组的初始化以及返回。

互动

ganache 的启动方式是

  • ganache-cli --fork.url wss://bsc-mainnet.nodereal.io/ws/v1/63ef*** --miner.blockTime 1

必须要加 miner.blockTime 让其自动出块,否则进行写区块操作的时候,会出现

transaction can't be replaced, mining has already started

python 代码

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
from web3 import Web3

from Constant.Strategy.StrategyConstant import StrategyType

abi = """[
{
"inputs": [],
"name": "add",
"outputs": [
{
"internalType": "uint256[][]",
"name": "",
"type": "uint256[][]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "initB",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "getB",
"outputs": [
{
"internalType": "uint256[][]",
"name": "",
"type": "uint256[][]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "i",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "j",
"type": "uint256"
}
],
"name": "getElement",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "i",
"type": "uint256"
}
],
"name": "getList",
"outputs": [
{
"internalType": "uint256[2]",
"name": "",
"type": "uint256[2]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "i",
"type": "uint256"
}
],
"name": "getListLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]"""

private = "***"
address = "***"


class Test:

def __init__(self, strategyEngine):
self.web3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
self.strategyEngine = strategyEngine
self.test = self.web3.eth.contract(
address=Web3.toChecksumAddress("0xe109458c765fb5dc0f3fbf60e0e07656981b2a2f"), abi=abi)

def add(self):
# add
tx_dic = self.test.functions.add().buildTransaction(
{
'gas': 4000000,
}
)
tx_dic["nonce"] = self.web3.eth.getTransactionCount(Web3.toChecksumAddress(address))
tx_dic['gasPrice'] = self.web3.toWei(5, 'gwei')
sign_tx = self.web3.eth.account.signTransaction(tx_dic, private_key=private)
txn_hash = self.web3.eth.sendRawTransaction(sign_tx.rawTransaction)
tx = Web3.toHex(txn_hash)
## get
data = self.web3.eth.get_transaction_receipt(tx)
print(data)

def getB(self):
data = self.test.functions.getB().call()
print(data)


if __name__ == '__main__':
l = Test(None)
l.add()
l.getB()

上面的输出

  • add
    • data
      • 就是 tx 获取,没什么可以用的
      • 可能是目前我还不知道怎么读取
  • getB
    • data
      • 就是 2 维数组

其他

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

// 合约调用 pancakeswap
// 合约转出任意代币


contract MultiTrade {

constructor() {
}

function get(uint index,uint[][] calldata test) public view returns(uint[] calldata){
return test[index];
}
}

进行浏览器部署后

  • get
    • 1,[[1,2],[3,4,5],[7]]
    • 返回 [3,4,5]

数组间的传值

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
pragma solidity ^0.8.4;

contract Test {

uint[] aStorage = [1,2,3];

function getStorage() public view returns(uint[] memory) {
return aStorage;
}

function storage2Memory() public view {
uint[] memory b = aStorage; // 值传递
b[0] = 100;
}

function memory2Storage() public {
uint[3] memory m = [uint(10), 20, 30];
aStorage = m; // 值传递
m[0] = 77;
}

function storage2Storage() public {
// storage定义的局部变量不能使用数组字面量进行赋值
// uint[] storage s = [uint(22),33,44]; // error
uint[] storage s = aStorage; // 引用传递
s[0] = 99;
}

function memory2Memory() public pure returns(uint8[3] memory) {
uint8[3] memory m1 = [11,12,13];
uint8[3] memory m2 = m1; // 引用传递
m2[0] = 20;
return m1;
}
}

分别进行运行,「下面函数,每运行一次,都要重新部署一次,用来隔离参数,防止被污染」

  • getStorage()
    • [1,2,3]
  • storage2Memory() -> getStorage()
    • [1,2,3] # 不变
  • memory2Storage() -> getStorage()
    • [10,20,30] # 变化
  • storage2Storage() -> getStorage()
    • [99,2,3] # 变化
  • memory2Memory() -> getStorage()
    • [1,2,3] # 不变

字节数组

字节数组 bytes 类似 byte[] ,不过 bytes 作为外部函数的参数空间更小,所以,优先使用 bytes

  • bytes bs;
  • bytes bs = "123bh";

字符串

字符串也是数组

  • string st0;
  • string st1 = "等链学院"
  • 没有 .length 属性
    • 通过 bytes(s).length 获得字节数组
    • bytes(s)[k] 获取下标 Kutf-8 的编码
  • bytes 存储任意长度的字节数据 「utf-8 编码」
  • string 用来存储 utf-8 编码的字符串数据
1
2
3
4
5
6
7
8
9
10
11
pragma solidity ^0.4.18;

contract Test{

string public st1 = "犀牛";
bytes public st2 = "犀牛";

function get_len() constant returns(uint){
return bytes(st1).length;
}
}
  • get_len
    • 获得的是 6 ,因为,一个汉字有 3 个字节
  • 如果要获取下标,那么,获取的也是编码

stringutils

这个是第三方库,用来处理字符串。

使用前需要把字符串转化成一个个切片,再进行使用

主要功能有

  • 字符长度
  • 匹配字符串
    • find()
    • startsWith()
    • endsWith()
  • 字符串拼接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pragma solidity ^0.4.18;

import "github.com/Arachnid/solidity-stringutils/strings.sol";

contract Test{

using strings for *; // 意思是 strings 适用于所有的类型

string public st1 = "犀牛";


function get_len() constant returns(uint len){
len = st1.toSlice().len();
}
}

这里说一下几点要注意的

  • 我是 remix 编写的代码
    • import "github.com/Arachnid/solidity-stringutils/strings.sol";
    • 会在线上目录中,创建相应的文件夹,会把 github 中的 strings.sol 文件内容 copy 到这个文件中
    • 但是,github 线上的这个文件是一个路径,而不是 strings.sol 的内容,于是,我把 strings.sol 的内容直接 copyremix 创建的文件中
    • 就可以正常运行了
  • 如果是本地
    • 建议 copy 上面第三方库的合约内容到本地文件,然后引用

上面的图是你引入 importremix 自己创建的。

因为,该库 github 的文件内容是一个引用「现在可能已经修改了」,所以,我们需要自己把该库的 strings.sol 内容 copyremixstring.sol 里面。

请我喝杯咖啡吧~