文章

Solidity 智能合约入门

Web3

Web3学习日记

Web3

什么是智能合约

智能合约是运行在链上的程序,合约开发者可以通过智能合约实现与链上资产、数据进行交互,用户可以通过自己的链上账户来调用合约、访问资产与数据。

与一般程序的差异在于,智能合约的部署与后续写入需要一定费用,所以会更看重资源的消耗。

什么是Solidity

Solidity 是一门面向合约的、为实现智能合约而创建的高级编程语言,在 EVM 虚拟机上运行,语法与 JavaScript 类似,是目前最流行的智能合约语言,也是入门区块链与web3必须掌握的语言

如何部署智能合约

Solidity 是以 .sol 为后缀的文件,无法直接执行,需要编译为 EVM 可识别的字节码才能在链上运行。

image-20240921154158111

开发框架/工具

image-20240921154828359

image-20240921160133738

Solidity核心语法

基本数据类型

  • boolean
  • int
  • uint
  • address
  • bytes

复杂数据类型

枚举 enum

enum Status {
	Unknown,
	Start,
	End,
	Pause
}
 
// 实例化枚举类型
Status public status;
 
// 更新枚举值
function pause() public {
	status = Status.Pause;
}
 
// 初始化枚举值
function reset() public {
	delete status;
}

数组 array

// 定义数组类型
uint[7] public arr;
 
// 添加数据
arr.push(7);
 
// 删除某个索引值数据
delete arr[1];
 
// 获取数组长度
uint len = arr.length;

映射 mapping 类型,key 必须是基本数据类型,并且需要自己实现一个迭代器,并不能被原生迭代器遍历。

// 定义嵌套 mapping 类型
mapping(string => mapping(string => string)) nestedMap;
 
// 设置值
nestedMap[id][key] = "01";
 
// 读取值
string value = nestedMap[id][key];
 
// 删除值
delete nestedMap[id][key];

结构体 struct 类型

contract Struct {
	struct Data {
		string id;
		string hash;
	}
	
	Data public data;
	
	// 添加数据
	function create(string calldata _id) public {
		data = Data { id: _id, hash: "0000" };
	}
	
	// 更新数据
	function update(string _id) public {
		// 查询数据
		string id = data.id;
		
		// 更新
		data.hash = "1111";
	}
}

变量

类型:

  • local(只存在于方法里,不会存储在链上)
  • state(可以存储在链上)
  • global

关键字声明:

  • storage(声明的变量可以存在链上)
  • memory(只有方法被调用时,声明的变量才会被定义)
  • calldata(特殊的memory)

常量

  • constant(值不可变,节约 gas fee)
  • immutable(可以在constructor中初始化,但不可以再次改变)

函数

  • 可见性
    • public
    • private
    • internal
    • external
  • 关键字
    • view(可以读取变量,但是不能更改)
    • pure(不可以读取和更改)
  • 函数修饰符
    • modifier(可以在函数运行前或运行后被调用,常用于参数校验和权限控制)
modifier onlyOwner() {
	require(msg.sender == owner, "Not owner");
	_; // 表明开始执行真正的合约代码的逻辑
}
 
modifier validAddress(address _addr) {
	require(_addr != address(0), "Not valid address");
	_;
}
 
modifier noReentrancy() {
	require(!locked, "No reentrancy");
	locked = true;
	_;
	locked = false;
}
 
function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner) {
	owner = _newOwner;
}
 
function decrement(uint i) public noReentrancy {
	x -= i;
	
	if (i > 1) {
		decrement(i - 1);
	}
}

函数选择器

addr.call(abi.encodeWithSignature("transfer(address,uint256)", 0xSomeAddress, 123));
 
contract FunctionSelector {
	function getSelector(string calldata _func) external pure returns(bytes4) {
		return bytes4(keccak256(bytes(_func)));
	}
}

合约

  • constructor

    constructor(string memory _name) {
    	name = _name;
    }
  • interface

    contract Counter {
    	uint public count;
    	
    	function increment() external {
    		count += 1;
    	}
    }
     
    interface ICounter {
    	function count() external view returns (uint);
    	function increment() external;
    }
     
    contract MyContract() {
    	function incrementCounter(address _counter) external {
    		ICounter(_counter).increment();
    	}
    	function getCount(address _counter) external view returns (uint) {
    		return ICounter(_counter).count();
    	}
    }
  • 导入库

    library SafeMath {
    	function add(uint x, uint y) internal pure returns (uint) {
    		uint z = x + y;
    		require(z >= x, "uint overflow");
    		return z;
    	}
    }
     
    contract TestSafeMath {
    	using SafeMath for uint;
    }

事件

// 定义事件
event Log(address indexed sender, string message);
event AnotherLog();
 
// 抛出事件
emit Log(msg.sender, "Hello World");
emit Log(msg.sender, "Hello EVM");
emit AnotherLog();

错误处理

通过 require / revert / assert 处理错误

function testRequire(uint _i) public pure {
	require(_i > 10, "Input must be greater than 10");
}
 
function testRevert(uint _i) public pure {
	if (_i <= 10) {
		revert("Input must be greater than 10");
	}
}
 
function testAssert() public view {
	assert(num === 0);
}