跳到主要内容

Web3 速查表

本章提供 Web3 开发中常用的命令、代码片段和概念速查,方便快速查阅。

以太坊单位换算

单位Wei 值说明
wei1最小单位
gwei10^9Gas 价格单位
ether10^18以太币
// ethers.js 单位换算
ethers.parseEther("1.0") // "1000000000000000000"
ethers.formatEther(1000000000000000000n) // "1.0"
ethers.parseUnits("1.0", "gwei") // "1000000000"
ethers.formatUnits(1000000000n, "gwei") // "1.0"
// Solidity 单位
uint256 a = 1 ether; // 10^18 wei
uint256 b = 1 gwei; // 10^9 wei
uint256 c = 1 wei; // 1 wei

Solidity 数据类型速查

值类型

类型说明示例
bool布尔值bool isActive = true;
uint无符号整数uint256 amount = 100;
int有符号整数int256 temperature = -10;
address地址address owner = 0x...;
bytes1-bytes32定长字节bytes32 hash = keccak256(...);
string字符串string name = "Token";
enum枚举enum Status { Active, Paused }

引用类型

类型说明示例
array数组uint256[] items;
struct结构体struct User { string name; }
mapping映射mapping(address => uint256) balances;

可见性修饰符

修饰符本合约子合约外部
private
internal
public
external

状态可变性

修饰符说明
pure不读取也不修改状态
view读取但不修改状态
payable可以接收 ETH
可以修改状态

常用 Solidity 模式

访问控制

// 仅所有者
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}

// 角色控制
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
modifier onlyRole(bytes32 role) {
require(hasRole(role, msg.sender), "Not authorized");
_;
}

重入保护

bool private locked;

modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}

提款模式

function withdraw() public {
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0; // 先更新状态

(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}

事件定义

event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);

// 发出事件
emit Transfer(msg.sender, to, amount);

错误处理

// 自定义错误(节省 Gas)
error InsufficientBalance(uint256 available, uint256 required);

// 使用
if (balance < amount) {
revert InsufficientBalance(balance, amount);
}

// 传统方式
require(balance >= amount, "Insufficient balance");

ethers.js 常用代码

连接网络

const { ethers } = require("ethers");

// JSON-RPC 提供者
const provider = new ethers.JsonRpcProvider("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY");

// 浏览器钱包(MetaMask)
const browserProvider = new ethers.BrowserProvider(window.ethereum);

账户操作

// 创建钱包
const wallet = ethers.Wallet.createRandom();
console.log(wallet.address);
console.log(wallet.privateKey);
console.log(wallet.mnemonic.phrase);

// 从私钥恢复
const wallet = new ethers.Wallet(privateKey, provider);

// 从助记词恢复
const wallet = ethers.Wallet.fromPhrase(mnemonic, provider);

// 获取余额
const balance = await provider.getBalance(address);
console.log(ethers.formatEther(balance));

// 发送交易
const tx = await wallet.sendTransaction({
to: recipientAddress,
value: ethers.parseEther("0.1")
});
await tx.wait();

合约交互

// 合约实例
const contract = new ethers.Contract(address, abi, provider);

// 读取数据
const name = await contract.name();
const balance = await contract.balanceOf(userAddress);

// 写入数据(需要签名者)
const contractWithSigner = contract.connect(wallet);
const tx = await contractWithSigner.transfer(recipient, amount);
await tx.wait();

// 监听事件
contract.on("Transfer", (from, to, amount) => {
console.log(`Transfer: ${from} -> ${to}: ${amount}`);
});

签名验证

// 签名消息
const message = "Hello, Ethereum!";
const signature = await wallet.signMessage(message);

// 验证签名
const recoveredAddress = ethers.verifyMessage(message, signature);
console.log(recoveredAddress === wallet.address); // true

// 签名 Typed Data(EIP-712)
const domain = {
name: "My App",
version: "1",
chainId: 1,
verifyingContract: contractAddress
};

const types = {
Message: [
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "amount", type: "uint256" }
]
};

const value = {
from: wallet.address,
to: recipientAddress,
amount: ethers.parseEther("1.0")
};

const signature = await wallet.signTypedData(domain, types, value);

Hardhat 常用命令

# 编译合约
npx hardhat compile

# 运行测试
npx hardhat test

# 运行特定测试
npx hardhat test test/Token.test.js

# 显示 Gas 报告
REPORT_GAS=true npx hardhat test

# 启动本地节点
npx hardhat node

# 部署到本地网络
npx hardhat run scripts/deploy.js

# 部署到测试网
npx hardhat run scripts/deploy.js --network sepolia

# 打开控制台
npx hardhat console

# 清理编译产物
npx hardhat clean

# 验证合约
npx hardhat verify --network sepolia CONTRACT_ADDRESS CONSTRUCTOR_ARGS

OpenZeppelin 常用合约

ERC20 代币

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
constructor(uint256 initialSupply) ERC20("My Token", "MTK") {
_mint(msg.sender, initialSupply * 10 ** decimals());
}
}

ERC721 NFT

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFT is ERC721 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;

constructor() ERC721("My NFT", "MNFT") {}

function mint() public {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(msg.sender, tokenId);
}
}

访问控制

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract MyContract is Ownable, AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

constructor() Ownable(msg.sender) {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
}

function adminFunction() public onlyRole(ADMIN_ROLE) {
// 管理员功能
}
}

可暂停

import "@openzeppelin/contracts/utils/Pausable.sol";

contract MyContract is Pausable {
function pause() public onlyOwner {
_pause();
}

function unpause() public onlyOwner {
_unpause();
}

function deposit() public whenNotPaused {
// 存款逻辑
}
}

常用全局变量

区块信息

变量类型说明
block.coinbaseaddress出块者地址
block.difficultyuint256区块难度
block.gaslimituint256区块 Gas 上限
block.numberuint256区块高度
block.timestampuint256区块时间戳(秒)
blockhash(uint)bytes32指定区块的哈希

交易信息

变量类型说明
msg.databytes完整调用数据
msg.senderaddress调用者地址
msg.sigbytes4函数选择器
msg.valueuint256发送的 Wei 数量
tx.gaspriceuint256Gas 价格
tx.originaddress交易发起者

其他

变量/函数说明
gasleft()剩余 Gas
now (已弃用)使用 block.timestamp
this当前合约地址
super父合约

常用 ERC 标准接口

ERC20

function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);

ERC721

function balanceOf(address owner) external view returns (uint256);
function ownerOf(uint256 tokenId) external view returns (address);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);

ERC1155

function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;

常见网络配置

测试网

网络Chain IDRPC URL
Sepolia11155111https://rpc.sepolia.org
Goerli5https://rpc.goerli.mudit.blog/
Holesky17000https://rpc.holesky.ethpandaops.io

主网

网络Chain IDRPC URL
Ethereum1https://eth.llamarpc.com
Polygon137https://polygon-rpc.com
Arbitrum42161https://arb1.arbitrum.io/rpc
Optimism10https://mainnet.optimism.io
BSC56https://bsc-dataseed.binance.org

Gas 优化技巧

存储优化

// 不优化:3 个存储槽
contract Bad {
uint128 a;
uint256 b;
uint128 c;
}

// 优化:2 个存储槽
contract Good {
uint128 a;
uint128 c;
uint256 b;
}

使用 calldata

// 不优化:复制到内存
function process(uint256[] memory arr) public pure returns (uint256) {
return arr[0];
}

// 优化:直接使用 calldata
function process(uint256[] calldata arr) public pure returns (uint256) {
return arr[0];
}

使用 unchecked

// 0.8.0 之后自动检查溢出
for (uint256 i = 0; i < length; i++) {
// ...
}

// 跳过溢出检查(节省 Gas)
for (uint256 i = 0; i < length;) {
// ...
unchecked { i++; }
}

使用自定义错误

// 不优化:字符串错误
require(balance >= amount, "Insufficient balance");

// 优化:自定义错误
error InsufficientBalance();
if (balance < amount) revert InsufficientBalance();

安全检查清单

  • 使用 ReentrancyGuard 防止重入攻击
  • 先更新状态再发送 ETH
  • 使用 SafeMath 或 Solidity 0.8.0+
  • 验证外部调用的返回值
  • 限制循环次数防止 DoS
  • 使用 pull 模式而非 push 模式
  • 验证输入参数
  • 设置合理的访问控制
  • 使用时间锁管理关键功能
  • 进行充分的单元测试
  • 进行专业安全审计

小结

本章提供了 Web3 开发的常用速查信息,包括 Solidity 语法、ethers.js 代码片段、Hardhat 命令和 OpenZeppelin 合约模板。建议将此页面加入书签,在开发过程中快速查阅。

参考资料