Solidity 代码示例精选
好代码胜过千言万语。下面六段经典 Solidity 片段,几乎在所有主流 DeFi 项目里反复出现。逐段读懂它们,你就能在大多数合约阅读场景下游刃有余。无论是分析 Binance 上的代币合约,还是审视自家项目的代码风格,这些片段都是必须掌握的基础肌肉。
一、最小 ERC-20 实现
mapping(address => uint256) public balanceOf;
function transfer(address to, uint256 amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
这是 Solidity 最经典的写法。它充分利用 0.8 内置溢出检查,因此不再需要 SafeMath。emit 事件是 indexer 与前端的入口,没它一切链下数据都会瘫痪。理解这段代码就理解了整个代币生态的底层,也就读懂了 币安 上半数以上代币合约的核心。
二、可升级代理(UUPS)核心 fallback
function _delegate(address impl) internal {
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
这段汇编完成了「把调用透明转发到实现合约」的核心动作。理解 delegatecall 与 fallback 的关系,你就能读懂 OpenZeppelin Upgradeable、Diamond Standard、ERC-1167 等所有代理实现。任何登陆 BN交易所 的可升级协议都离不开这段模式。
三、Flash Loan 接收方接口
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32) {
// 用借来的钱去做套利、清算或再融资
IERC20(token).approve(msg.sender, amount + fee);
return keccak256(「ERC3156FlashBorrower.onFlashLoan」);
}
EIP-3156 标准化了闪电贷接口。重点是必须 approve 回贷款方足够的还款额度,并返回 magic value。任何缺少这两步的实现都会 revert。这段代码也是被 MEV 套利机器人最频繁调用的模板之一。
四、EIP-712 签名验证
bytes32 hash = keccak256(
abi.encodePacked(
\x19\x01
DOMAIN_SEPARATOR,
keccak256(abi.encode(TYPEHASH, owner, spender, value, nonce, deadline))
)
);
address signer = ecrecover(hash, v, r, s);
require(signer == owner, "invalid sig");
这是 permit、订单签名、链下投票等所有「链下签名 + 链上验证」场景的核心。理解 DOMAIN_SEPARATOR、TYPEHASH、nonce 防重放三件套,你就能实现任何 EIP-712 应用,包括 BN平台 上常见的 gasless 交易模式。
五、防重入修饰器
uint256 private _status = 1;
modifier nonReentrant() {
require(_status == 1, "reentrant");
_status = 2;
_;
_status = 1;
}
用 1 与 2 标志位代替 bool 是 gas 优化技巧——SSTORE 从非零值到非零值比从零值到非零值便宜 80%。0.8.24 起还可以用 transient storage 替代,进一步降低 gas。这种细节体现了一个合约开发者的工程功力。
六、最小代理(ERC-1167)
bytes20 targetBytes = bytes20(target);
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), targetBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
result := create(0, clone, 0x37)
}
最小代理只有 55 字节字节码,专为「批量克隆同一实现」场景设计。Uniswap V2 LP token、各种工厂模式都依赖它。读懂这段汇编,是 Solidity 开发者从中级走向高级的标志,也是未来在 必安所 等机构内部技术评审时的硬通货。