23-03-05

First Post:

Last Update:

2023-03-05

salt关键字

在 Solidity 中,salt 是一个关键字,它用于支持 create2 功能。自 0.8.0 版本起,可以通过指定 salt 选项来使用新的关键字

相关文章/问答->Salted contract creations / create2what does keyword “salt” mean in solidity?What is the benefit of using create2() to create a smart 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
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract D {
uint public x;
constructor(uint a) {
x = a;
}
}

contract C {
function createDSalted(bytes32 salt, uint arg) public {
// This complicated expression just tells you how the address
// can be pre-computed. It is just there for illustration.
// You actually only need ``new D{salt: salt}(arg)``.
//这些复杂的表达只是告诉我们地址是如何被提前计算的,仅作展示
//事实上只需要``(new D){salt: salt}(arg)``
address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(abi.encodePacked(
type(D).creationCode,
abi.encode(arg)
))
)))));

D d = new D{salt: salt}(arg);
require(address(d) == predictedAddress);
}
}

例子

在一个合约中创建合约其实有助于管理合约(不借助数据库)

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
pragma solidity ^0.8.17;
//SPDX-License-Identifier: MIT
contract Car{
address owner;//表示拥有者
string public model;//表示车型
address carAddr;//表示合约地址
constructor(address _owner, string memory _model) payable{
owner = _owner;
model = _model;
carAddr = address(this);
}
}
contract CarFactory{
Car[] public cars;
function createNewCar(string memory _model) public {
cars.push(new Car(address(this), _model));
}
function createNewCarAndSendEther(string memory _model) public payable {
Car car = (new Car){value:msg.value}(address(this), _model);
cars.push(car);
}

function create2NewCar(string memory _model, bytes32 salt) public {
Car car = (new Car){salt:salt}(address(this), _model);
cars.push(car);
}
function create2NewCarWithEther(string memory _model, bytes32 salt) public payable {
Car car = (new Car){value:msg.value, salt:salt}(address(this), _model);
cars.push(car);
}
}

部署及调试

推荐视频->Learning Solidity (0.5) - Creating Contracts from a Contract

  • 部署车厂(CarFactory)合约

  • 传入相关参数同时调用函数,找到cars索引复制其地址

  • 通过地址寻找车合约

异常 try/catch

在函数体中使用try/catch:

try + 合约对象+函数名称(参数) +returns(接收返回值) / try + 合约对象(构造函数传参)

catch + Error(string memory result):处理require异常

catch + (bytes memory somereason):处理require异常

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
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Foo {
address public owner;
constructor(address _owner) {
require(_owner != address(0), "invalid address");
assert(_owner != 0x0000000000000000000000000000000000000001);
owner = _owner;
}
function myFunc(uint x) pure public returns(string memory){
require(x!=0,"x!=0");
return "my function is called! ";
}
}
contract Bar {
event log(string message);
event logBtyes(bytes data);
Foo foo;
function tryCatchExternalCall(uint _i) public {
try foo.myFunc(_i) returns( string memory result) {
emit log(result);
} catch {
emit log("external call failed! ");
}
}

function tryCatchNewContract(address addr) public {
try new Foo(addr) {
emit log("create a new contract! ");
}catch Error(string memory reason) {
emit log(reason);//捕获recert跟require异常
}catch (bytes memory somereason) {
emit logBtyes(somereason);//捕获assert异常
}
}
}

import

例子

文件路径

1
2
├── Import.sol
└── Foo.sol

Foo.sol

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.17;

struct Point {
uint x;
uint y;
}

error Unauthorized(address caller);

function add(uint x, uint y) pure returns (uint) {
return x + y;
}

contract Foo {
string public name = "Foo";
}

Import.sol

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

// import Foo.sol from current directory
import "./Foo.sol";

// import {symbol1 as alias, symbol2} from "filename";
import {Unauthorized, add as func, Point} from "./Foo.sol";

contract Import {
// Initialize Foo.sol
Foo public foo = new Foo();

// Test Foo.sol by getting it's name.
function getFooName() public view returns (string memory) {
return foo.name();
}
}

也可以通过其他地方导入

1
2
3
4
5
6
// https://github.com/owner/repo/blob/branch/path/to/Contract.sol
import "https://github.com/owner/repo/blob/branch/path/to/Contract.sol";

// Example import ECDSA.sol from openzeppelin-contract repo, release-v4.5 branch
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol";

注意事项

没包在合约体内的函数不能有可见性

如果要调用另一个合约的状态变量,需要事先把该合约内的状态变量声明为public(会默认生成getter),然后在本合约访问时要使用合约对象名.变量名()

Library 库

一个可以去除元素同时减少空间的库

1
2
3
4
5
6
7
8
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.17;
library Array {
function remove(uint[] storage arr, uint index) internal {
arr[index] = arr[arr.length-1];
arr.pop();
}
}

如何使用库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.17;
import "./LibImported.sol";
contract testLib {
using Array for uint[];//要放在函数体外面
uint[] arr;
function testRm(uint _index) public returns(uint length1,uint length2) {
for(uint i = 0; i < 5; i++) {
arr.push(i+1);
}
//第一种调用库函数方法
arr.remove(_index);
length1 = arr.length;
//第二种调用方式
Array.remove(arr, _index);
length2 = arr.length;
}
}

更多例子

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

library Math {
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
// else z = 0 (default value)
}
}

contract TestMath {
function testSquareRoot(uint x) public pure returns (uint) {
return Math.sqrt(x);
}
}

// Array function to delete element at index and re-organize the array
// so that there are no gaps between the elements.
library Array {
function remove(uint[] storage arr, uint index) public {
// Move the last element into the place to delete
require(arr.length > 0, "Can't remove from empty array");
arr[index] = arr[arr.length - 1];
arr.pop();
}
}

contract TestArray {
using Array for uint[];

uint[] public arr;

function testArrayRemove() public {
for (uint i = 0; i < 3; i++) {
arr.push(i);
}

arr.remove(1);

assert(arr.length == 2);
assert(arr[0] == 0);
assert(arr[1] == 2);
}
}

相关视频->HERE

注意事项

  • using A for B中A可以理解为库名称,B为第一个传入参数类型

  • 如果库的函数可见性声明为internal需要嵌入本合约代码

  • 某些函数非internal时候需要实现部署它,并将其与使用该合约函数连接起来(remix做不到、可节省gas开销)