Solidity语言
大约 9 分钟
Solidity语言
- Etherscan: https://etherscan.io/
- Solidity文档: https://docs.soliditylang.org
- WRTF.academy: https://www.wtf.academy
- Remix: https://remix.ethereum.org/
- Etherscan: https://etherscan.io/
- Foundry: https://github.com/foundry-rs/
- Ganache: http://truffleframework.com/ganache
- Hardhat: https://hardhat.org/
- Truffle: https://archive.trufflesuite.com/
- Brownie: https://eth-brownie.readthedocs.io/en/stable/
- Huff: https://github.com/huff-language/huffc
- ChainID: https://chainid.network/
HardHat
- 安装
npm install --save-dev hardhat
- 编译工程
npx hardhat compile
- 测试工程
npx hardhat test
- 测试节点
npx hardhat node
Foundry
- 安装
forge init hello_foundry
- 编译工程
forge build
- 测试工程
forge test
- 测试节点
anvil
Hello World
// SPDX-License-Identifier: MIT
/*
comment
*/
pragma solidity ^0.8.0;
contract HelloWorld{
string public _string = "Hello World";
}
数值类型
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 定义一个名为Person的结构体
struct Person {
string name; // 姓名
uint age; // 年龄
address walletAddress; // 钱包地址
}
contract ValueTypes{
// 布尔值
bool public _bool = true;
// 布尔运算
bool public _bool1 = !_bool; //取非
bool public _bool2 = _bool && _bool1; //与
bool public _bool3 = _bool || _bool1; //或
bool public _bool4 = _bool == _bool1; //相等
bool public _bool5 = _bool != _bool1; //不相等
// 整数
int public _int = -1;
uint public _uint = 1;
uint256 public _number = 20220330;
// 整数运算
uint256 public _number1 = _number + 1; // +,-,*,/
uint256 public _number2 = 2**2; // 指数
uint256 public _number3 = 7 % 2; // 取余数
bool public _numberbool = _number2 > _number3; // 比大小
// 地址
address public _address = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
address payable public _address1 = payable(_address); // payable address,可以转账、查余额
// 地址类型的成员
uint256 public balance = _address1.balance; // balance of address
// 固定长度的字节数组
bytes32 public _byte32 = "DeeLMind"; // bytes32: 0x4465654c4d696e64000000000000000000000000000000000000000000000000
bytes1 public _byte = _byte32[0];
// Enum
// 将uint 0, 1, Zero, One, Two
enum ActionSet { Zero, One, Two }
// 创建enum变量 action
ActionSet action = ActionSet.Two;
// enum可以和uint显式的转换
function enumToUint() external view returns(uint){
return uint(action);
}
// 声明一个Person类型的变量
Person public myPerson;
// 可以通过函数修改结构体的值
function updatePerson(string memory _name, uint _age, address _walletAddress) public {
myPerson.name = _name;
myPerson.age = _age;
myPerson.walletAddress = _walletAddress;
}
// 固定长度 Array
uint[8] array1;
bytes1[5] array2;
address[100] array3;
// 可变长度 Array
uint[] array4;
bytes1[] array5;
address[] array6;
bytes array7;
// 初始化可变长度 Array
uint[] array8 = new uint[](5);
bytes array9 = new bytes(9);
// 给可变长度数组赋值
function initArray() external pure returns(uint[] memory){
uint[] memory x = new uint[](3);
x[0] = 1;
x[1] = 3;
x[2] = 4;
return(x);
}
// memory 存储在内存中
function arrayPush() public returns(uint[] memory){
uint[2] memory a = [uint(1),2];
array4 = a;
array4.push(3);
return array4;
}
// calldata 不能修改
function fCalldata(uint[] calldata _x) public pure returns(uint[] calldata){
// calldata aaa;
// _x = 1;
return(_x);
}
uint[] x1 = [1,2,3]; // 状态变量:数组 x
// 存储在链上
function fStorage() public{
uint[] storage xStorage = x1;
xStorage[0] = 100;
}
// delete操作符
bool public _bool21 = true;
function d() external {
delete _bool21; // delete 会让_bool21变为默认值,false
}
// constant变量必须在声明的时候初始化,之后不能改变
uint256 constant CONSTANT_NUM = 10;
string constant CONSTANT_STRING = "0xAA";
bytes constant CONSTANT_BYTES = "AA";
address constant CONSTANT_ADDRESS = 0x0000000000000000000000000000000000000000;
// immutable变量可以在constructor里初始化,之后不能改变
uint256 public immutable IMMUTABLE_NUM = 9999999999;
address public immutable IMMUTABLE_ADDRESS;
uint256 public immutable IMMUTABLE_BLOCK;
uint256 public immutable IMMUTABLE_TEST;
// 利用constructor初始化immutable变量,因此可以利用
constructor(){
myPerson = Person("DeeLMind", 18, address(0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71));
IMMUTABLE_ADDRESS = address(this);
IMMUTABLE_BLOCK = block.number;
// CONSTANT_NUM = 11;
}
}
函数类型
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract FunctionTypes{
uint256 public number = 5;
constructor() payable {}
// 函数类型
// function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]
function add() external{
number = number + 1;
}
// public: 内部外部均可见。
// private: 只能从本合约内部访问,继承的合约也不能用。
// external: 只能从合约外部访问(但是可以用this.f()来调用,f是函数名)。
// internal: 只能从合约内部访问,继承的合约可以用。
function addPure(uint256 _number) external pure returns(uint256 new_number){
new_number = _number+1;
}
function addView() external view returns(uint256) {
uint256 new_number = number + 1;
return new_number;
}
function minus() internal {
number = number - 1;
}
function minusCall() external {
minus();
}
function minusPayable() external payable returns(uint256 balance) {
minus();
balance = address(this).balance;
}
function returnss() public pure returns (uint256, bool) {
return (1, true);
}
uint256 one1;
bool tw1o;
function callReturnss() public {
(one1, tw1o) = returnss();
}
}
发送接收
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReceiveETH {
// 收到eth事件,记录amount和gas
// 日志记录都包含主题topics和数据data两部分
event XXXLOG(string name,uint amount, uint gas);
event XXXLOG1(address indexed from,string name,uint amount, uint gas);
// https://etherscan.io/tx/0xf23f89131cffe115ee20a9f4b039b2ffaf3c51ad17bfec301b1a975f9478030b#eventlog
// receive方法,接收eth时被触发
receive() external payable{
emit XXXLOG1(msg.sender,"receive",msg.value, gasleft());
}
// fallback() external payable {
// emit XXXLOG("fallback",msg.value, gasleft());
// }
// 返回合约ETH余额
function getBalance() view public returns(uint) {
return address(this).balance;
}
}
contract CallContract{
// 对应每个用户的资产 key = value
mapping(address => uint) private userBalances;
// 存入资产
function deposit() public payable {
userBalances[msg.sender] += msg.value;
}
function getCurUBalance() public view returns (uint){
return userBalances[msg.sender];
}
function getAllBalance() public payable returns (uint){
return address(this).balance;
}
// send()发送ETH
function sendETH_send(address payable _to, uint256 amount) external payable{
// 处理下send的返回值,如果失败,revert交易并发送error
// bool success = _to.send(amount);
// if(!success){
// revert("send failed");
// }
// _to.transfer(amount);
// (bool success ,)= _to.call{value:amount}("");
// if(!success){
// revert("send failed");
// }
}
}
循环控制
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Flow {
// if else
function ifElseTest(uint256 _number) public pure returns(bool){
if(_number == 0){
return(true);
}else{
return(false);
}
}
// for loop
function forLoopTest() public pure returns(uint256){
uint sum = 0;
for(uint i = 0; i < 10; i++){
sum += i;
}
return(sum);
}
// while
function whileTest() public pure returns(uint256){
uint sum = 0;
uint i = 0;
while(i < 10){
sum += i;
i++;
}
return(sum);
}
// do-while
function doWhileTest() public pure returns(uint256){
uint sum = 0;
uint i = 0;
do{
sum += i;
i++;
}while(i < 10);
return(sum);
}
// 三元运算符 ternary/conditional operator
function ternaryTest(uint256 x, uint256 y) public pure returns(uint256){
// return the max of x and y
return x >= y ? x: y;
}
}
修饰器
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Owner {
address public owner; // 定义owner变量
// 构造函数
constructor(address initialOwner) {
owner = initialOwner; // 在部署合约的时候,将owner设置为传入的initialOwner地址
}
// 定义modifier
modifier onlyOwner {
require(msg.sender == owner); // 检查调用者是否为owner地址
_; // 如果是的话,继续运行函数主体;否则报错并revert
}
// 定义一个带onlyOwner修饰符的函数
function changeOwner(address _newOwner) external onlyOwner{
owner = _newOwner; // 只有owner地址运行这个函数,并改变owner
}
}
异常错误
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
// 自定义error
error TransferNotOwner();
// error TransferNotOwner(address sender);
contract Errors {
// 一组映射,记录每个TokenId的Owner
mapping(uint256 => address) private _owners;
// Error方法: gas cost 24457
// Error with parameter: gas cost 24660
function transferOwner1(uint256 tokenId, address newOwner) public {
if (_owners[tokenId] != msg.sender) {
revert TransferNotOwner();
// revert TransferNotOwner(msg.sender);
}
_owners[tokenId] = newOwner;
}
// require方法: gas cost 24755
function transferOwner2(uint256 tokenId, address newOwner) public {
require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
_owners[tokenId] = newOwner;
}
// assert方法: gas cost 24473
function transferOwner3(uint256 tokenId, address newOwner) public {
assert(_owners[tokenId] == msg.sender);
_owners[tokenId] = newOwner;
}
}
合约库
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) public pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) public pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) public pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
// 用函数调用另一个库合约
contract UseLibrary{
// 利用using for操作使用库
using Strings for uint256;
function getString1(uint256 _number) public pure returns(string memory){
// 库函数会自动添加为uint256型变量的成员
return _number.toHexString();
}
// 直接通过库合约名调用
function getString2(uint256 _number) public pure returns(string memory){
return Strings.toHexString(_number);
}
}
合约代理
(bool success, bytes memory data) = _addr.delegatecall(abi.encodeWithSignature("setVars(uint256)", _num));
合约创建
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract A{
}
contract B{
A public immutable a;
A public immutable a1;
constructor(){
a = new A();
a1 = new A{salt: keccak256('')}();
}
function getA() public view returns(address){
return address(a);
}
function getA1() public view returns(address){
return address(a1);
}
function calculateAddress() public view returns (address) {
return address(uint160(uint(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
keccak256(''),
keccak256(type(A).creationCode)
)))));
}
}
合约调用
A a = new A{}();
address(this).call{value:amount}(abi.encodeWithSelector(0x6a627842, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4));
ABI
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract ABIEncode{
uint x = 10;
address addr = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
string name = "b";
uint[2] array = [5, 6];
function encode() public view returns(bytes memory result) {
result = abi.encode(x, addr, name, array);
}
function encodePacked() public view returns(bytes memory result) {
result = abi.encodePacked(x, addr, name, array);
}
function encodeWithSignature() public view returns(bytes memory result) {
result = abi.encodeWithSignature("test(uint256,address,string,uint256[2])", x, addr, name, array);
}
function encodeWithSelector() public view returns(bytes memory result) {
result = abi.encodeWithSelector(bytes4(keccak256("test(uint256,address,string,uint256[2])")), x, addr, name, array);
}
function decode(bytes memory data) public payable returns(uint dx, address daddr, string memory dname, uint[2] memory darray) {
(dx, daddr, dname, darray) = abi.decode(data, (uint, address, string, uint[2]));
}
}
HASH计算
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Hash{
bytes32 _msg = keccak256(abi.encodePacked("0xAA"));
// 唯一数字标识
function hash(
uint _num,
string memory _string,
address _addr
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_num, _string, _addr));
}
// 弱抗碰撞性
function weak(
string memory string1
)public view returns (bool){
return keccak256(abi.encodePacked(string1)) == _msg;
}
// 强抗碰撞性
function strong(
string memory string1,
string memory string2
)public pure returns (bool){
return keccak256(abi.encodePacked(string1)) == keccak256(abi.encodePacked(string2));
}
}
选择器
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Selector{
// event 返回msg.data
event Log(string name,bytes data);
// 输入参数 to: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
function mint(address /*to*/) external{
emit Log("MySelectorLog",msg.data);
}
receive() external payable{
}
fallback() external payable {
}
// 输出selector
// "mint(address)": 0x6a627842
function mintSelector() external pure returns(bytes4 mSelector){
return bytes4(keccak256("mint(address)"));
}
// 使用selector来调用函数
function callWithSignature() external returns(bool, bytes memory){
// 只需要利用`abi.encodeWithSelector`将`mint`函数的`selector`和参数打包编码
(bool success, bytes memory data) = address(this).call(abi.encodeWithSelector(0x6a627842, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4));
return(success, data);
}
}
Openzeppelin
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
contract MyToken is ERC20, ERC20Permit {
constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}
}
Yul
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
library GetCode {
function at(address addr) public view returns (bytes memory code) {
assembly {
// 获取代码大小,这需要汇编语言
let size := extcodesize(addr)
// 分配输出字节数组 – 这也可以不用汇编语言来实现
// 通过使用 code = new bytes(size)
code := mload(0x40)
// 包括补位在内新的 “memory end”
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// 把长度保存到内存中
mstore(code, size)
// 实际获取代码,这需要汇编语言
extcodecopy(addr, add(code, 0x20), 0, size)
}
}
}
ERC20
https://docs.openzeppelin.com/contracts/5.x/erc20
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
contract GEEKARKTOKEN is ERC20, ERC20Burnable, Ownable, ERC20Permit {
constructor(address initialOwner)
ERC20("GEEKARK TOKEN", "GKT")
Ownable(initialOwner)
ERC20Permit("GEEKARK TOKEN")
{}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
ERC721
// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract GameItem is ERC721URIStorage {
uint256 private _nextTokenId;
constructor() ERC721("GameItem", "ITM") {}
function awardItem(address player, string memory tokenURI)
public
returns (uint256)
{
uint256 tokenId = _nextTokenId++;
_mint(player, tokenId);
_setTokenURI(tokenId, tokenURI);
return tokenId;
}
}
ERC1155
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract GEEKTK is ERC1155, Ownable {
constructor(address initialOwner) ERC1155("") Ownable(initialOwner) {}
function mint(address account, uint256 id, uint256 amount, bytes memory data)
public
onlyOwner
{
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
public
onlyOwner
{
_mintBatch(to, ids, amounts, data);
}
}