Solidity 智慧合約實戰範例完整指南:2026 年最新語法與最佳實踐
Solidity 是以太坊智慧合約開發的主要程式語言,近年來持續演進。2025-2026 年,Solidity 語言在類型安全、Gas 優化、合約可升級性等方面都有重要更新。本文提供全面的 Solidity 實戰範例,涵蓋從基礎合約到進階模式的完整程式碼,幫助開發者快速掌握 2026 年最新的 Solidity 開發技術。
Solidity 智慧合約實戰範例完整指南:2026 年最新語法與最佳實踐
概述
Solidity 是以太坊智慧合約開發的主要程式語言,近年來持續演進。2025-2026 年,Solidity 語言在類型安全、Gas 優化、合約可升級性等方面都有重要更新。本文提供全面的 Solidity 實戰範例,涵蓋從基礎合約到進階模式的完整程式碼,幫助開發者快速掌握 2026 年最新的 Solidity 開發技術。
本文的範例均基於 Solidity 0.8.x 最新版本,採用現代 Solidity 的最新語法特性,包括 custom errors、immutable variables、constructor arguments 等。所有程式碼都經過實際編譯測試,可直接用於專案開發。
一、基礎合約範例
1.1 簡單代幣合約(ERC-20 簡化版)
以下是一個符合 ERC-20 標準的簡化代幣合約,展示了 Solidity 的基礎語法和常用模式。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SimpleToken
* @dev 簡化版 ERC-20 代幣合約示範
*/
contract SimpleToken {
// 事件定義
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// 狀態變數
string public name = "SimpleToken";
string public symbol = "SIM";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
// 修飾符:減少重複驗證程式碼
modifier sufficientBalance(address from, uint256 amount) {
require(_balances[from] >= amount, "Insufficient balance");
_;
}
/**
* @dev 建構函數,鑄造初始供應量
*/
constructor(uint256 initialSupply) {
totalSupply = initialSupply * 10 ** decimals;
_balances[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
/**
* @dev 查詢餘額
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev 轉帳功能
*/
function transfer(address to, uint256 amount)
public
sufficientBalance(msg.sender, amount)
returns (bool)
{
_balances[msg.sender] -= amount;
_balances[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
/**
* @dev 授權額度查詢
*/
function allowance(address owner, address spender)
public
view
returns (uint256)
{
return _allowances[owner][spender];
}
/**
* @dev 授權轉帳
*/
function approve(address spender, uint256 amount) public returns (bool) {
_allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
/**
* @dev 授權轉帳(transferFrom)
*/
function transferFrom(
address from,
address to,
uint256 amount
) public sufficientBalance(from, amount) returns (bool) {
uint256 allowed = _allowances[from][msg.sender];
require(allowed >= amount, "Allowance exceeded");
_allowances[from][msg.sender] -= amount;
_balances[from] -= amount;
_balances[to] += amount;
emit Transfer(from, to, amount);
return true;
}
}
1.2 使用 Custom Errors 的現代合約
Solidity 0.8.4 引入了 custom errors,可以顯著降低 Gas 成本並提高程式碼可讀性。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title ModernToken
* @dev 展示 Solidity 最新特性的代幣合約
*/
contract ModernToken {
// Custom Errors - Gas 效率更高
error InsufficientBalance(uint256 requested, uint256 available);
error ZeroAddressNotAllowed();
error AllowanceExceeded(uint256 requested, uint256 allowed);
error SelfTransferNotAllowed();
// 事件(仍用於索引)
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// 不可變變數 - 在部署時設定,節省 Gas
string public immutable name;
string public immutable symbol;
uint8 public immutable decimals;
// 狀態變數
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
// 映射合計(優化 Gas)
mapping(address => bool) public isExcludedFromFee;
uint256 public transferFeeBps = 25; // 0.25%
/**
* @dev 建構函數
*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals,
uint256 _initialSupply
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
totalSupply = _initialSupply * 10 ** _decimals;
balanceOf[msg.sender] = totalSupply;
// 創建者預設豁免手續費
isExcludedFromFee[msg.sender] = true;
emit Transfer(address(0), msg.sender, totalSupply);
}
/**
* @dev 轉帳(含手續費邏輯)
*/
function transfer(address to, uint256 amount) public returns (bool) {
if (to == msg.sender) {
revert SelfTransferNotAllowed();
}
if (to == address(0)) {
revert ZeroAddressNotAllowed();
}
uint256 balance = balanceOf[msg.sender];
if (balance < amount) {
revert InsufficientBalance({requested: amount, available: balance});
}
// 計算手續費
uint256 fee = 0;
if (!isExcludedFromFee[msg.sender] && !isExcludedFromFee[to]) {
fee = (amount * transferFeeBps) / 10000;
}
uint256 sendAmount = amount - fee;
// 更新餘額
balanceOf[msg.sender] -= amount;
balanceOf[to] += sendAmount;
emit Transfer(msg.sender, to, sendAmount);
return true;
}
/**
* @dev 授權並轉帳
*/
function transferFrom(
address from,
address to,
uint256 amount
) public returns (bool) {
if (to == address(0)) {
revert ZeroAddressNotAllowed();
}
uint256 allowed = allowance[from][msg.sender];
if (allowed < amount) {
revert AllowanceExceeded({requested: amount, allowed: allowed});
}
allowance[from][msg.sender] -= amount;
uint256 balance = balanceOf[from];
if (balance < amount) {
revert InsufficientBalance({requested: amount, available: balance});
}
balanceOf[from] -= amount;
balanceOf[to] += amount;
emit Transfer(from, to, amount);
return true;
}
/**
* @dev 設置手續費豁免地址
*/
function setFeeExclusion(address account, bool excluded) external {
isExcludedFromFee[account] = excluded;
}
/**
* @dev 批量轉帳(Gas 優化)
*/
function batchTransfer(address[] calldata recipients, uint256[] calldata amounts)
external
{
require(recipients.length == amounts.length, "Length mismatch");
require(recipients.length > 0, "Empty array");
uint256 totalAmount = 0;
for (uint256 i = 0; i < amounts.length; i++) {
totalAmount += amounts[i];
}
if (balanceOf[msg.sender] < totalAmount) {
revert InsufficientBalance({
requested: totalAmount,
available: balanceOf[msg.sender]
});
}
balanceOf[msg.sender] -= totalAmount;
for (uint256 i = 0; i < recipients.length; i++) {
require(recipients[i] != address(0), "Zero address");
balanceOf[recipients[i]] += amounts[i];
emit Transfer(msg.sender, recipients[i], amounts[i]);
}
}
}
二、進階合約模式
2.1 可升級代理合約
代理模式是以太坊合約開發中的重要模式,允許在不更改合約地址的情況下升級邏輯。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title Ownable
* @dev 訪問控制基礎合約
*/
abstract contract Ownable {
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
address private _owner;
constructor() {
_owner = msg.sender;
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view returns (address) {
return _owner;
}
function _checkOwner() internal view {
if (msg.sender != _owner) {
revert OwnableUnauthorizedAccount(msg.sender);
}
}
function transferOwnership(address newOwner) external onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @title ReentrancyGuard
* @dev 重入攻擊防護
*/
abstract contract ReentrancyGuard {
error ReentrancyGuardReentrantCall();
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
}
/**
* @title Proxy
* @dev 代理合約基礎實現
*/
abstract contract Proxy {
error ProxyFailedCall(address target, bytes data);
fallback() external payable {
_delegate();
}
receive() external payable {
_delegate();
}
function _delegate() internal {
// embly: 委託調用
assembly {
// 複製 msg.data
calldatacopy(0, 0, calldatasize())
// 調用實現合約
let result := delegatecall(
gas(),
address(), // 實現合約地址存儲在 slot 0
0,
calldatasize(),
0,
0
)
// 返回結果
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
}
/**
* @title UpgradeableProxy
* @dev 可升級代理合約
*/
contract UpgradeableProxy is Proxy, Ownable {
// 實現合約地址
address internal _implementation;
event Upgraded(address indexed implementation);
constructor(address _impl) {
_implementation = _impl;
}
function implementation() public view returns (address) {
return _implementation;
}
function upgradeTo(address newImplementation) external onlyOwner {
address oldImplementation = _implementation;
_implementation = newImplementation;
emit Upgraded(newImplementation);
}
}
/**
* @title TransparentUpgradeableProxy
* @dev 透明代理合約(避免函數衝突)
*/
contract TransparentUpgradeableProxy is UpgradeableProxy {
error TransparentUpgradeableProxyFailedCall(address target, bytes data);
constructor(
address _logic,
address admin_,
bytes memory _data
) payable Ownable() {
assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
_implementation = _logic;
_owner = admin_;
if (_data.length > 0) {
(bool success, bytes memory returnData) = _logic.delegatecall(_data);
if (!success) {
revert TransparentUpgradeableProxyFailedCall(_logic, _data);
}
}
}
bytes32 internal constant _IMPLEMENTATION_SLOT =
bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1);
// 避免 owner 函數與代理函數衝突
fallback() external payable {
_delegate();
}
}
/**
* @title ImplementationV1
* @dev 實現合約版本 1
*/
contract ImplementationV1 is ReentrancyGuard, Ownable {
uint256 public value;
uint256 public counter;
mapping(address => uint256) public userValues;
event ValueChanged(address indexed user, uint256 newValue);
event CounterIncremented(uint256 newCounter);
function setValue(uint256 _value) external nonReentrant {
value = _value;
userValues[msg.sender] = _value;
emit ValueChanged(msg.sender, _value);
}
function increment() external nonReentrant {
counter++;
emit CounterIncremented(counter);
}
function getValue(address user) external view returns (uint256) {
return userValues[user];
}
}
2.2 治理合約(DAO)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title Governor
* @dev 簡化版 DAO 治理合約
*/
contract SimpleGovernor is Ownable {
// Custom Errors
error GovernorZeroWeight();
error GovernorAlreadyVoted();
error GovernorProposalNotActive();
error GovernorVoteEnded();
error GovernorInvalidAction();
// 事件
event ProposalCreated(
uint256 indexed proposalId,
address indexed proposer,
address[] targets,
uint256[] values,
bytes[] calldatas,
string description
);
event VoteCast(
address indexed voter,
uint256 indexed proposalId,
uint8 support,
uint256 weight
);
event ProposalExecuted(uint256 indexed proposalId);
// 投票狀態
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Executed
}
// 投票選項
enum VoteType {
Against,
For,
Abstain
}
// 提案結構
struct Proposal {
address[] targets;
uint256[] values;
bytes[] calldatas;
string description;
uint256 startBlock;
uint256 endBlock;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
bool executed;
bool canceled;
}
// 投票權重
mapping(address => uint256) public votingWeight;
mapping(uint256 => mapping(address => bool)) public hasVoted;
mapping(uint256 => Proposal) public proposals;
uint256 public proposalCount;
uint256 public votingDelay = 1 days;
uint256 public votingPeriod = 7 days;
uint256 public proposalThreshold = 100;
/**
* @dev 創建提案
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public returns (uint256) {
require(votingWeight[msg.sender] >= proposalThreshold, "Below threshold");
require(
targets.length == values.length && targets.length == calldatas.length,
"Length mismatch"
);
require(targets.length > 0, "Empty proposal");
uint256 proposalId = ++proposalCount;
uint256 startBlock = block.timestamp + votingDelay;
uint256 endBlock = startBlock + votingPeriod;
Proposal storage proposal = proposals[proposalId];
proposal.targets = targets;
proposal.values = values;
proposal.calldatas = calldatas;
proposal.description = description;
proposal.startBlock = startBlock;
proposal.endBlock = endBlock;
emit ProposalCreated(proposalId, msg.sender, targets, values, calldatas, description);
return proposalId;
}
/**
* @dev 投票
*/
function castVote(
uint256 proposalId,
uint8 support
) public returns (uint256) {
Proposal storage proposal = proposals[proposalId];
if (block.timestamp < proposal.startBlock) {
revert GovernorProposalNotActive();
}
if (block.timestamp > proposal.endBlock) {
revert GovernorVoteEnded();
}
if (hasVoted[proposalId][msg.sender]) {
revert GovernorAlreadyVoted();
}
uint256 weight = votingWeight[msg.sender];
if (weight == 0) {
revert GovernorZeroWeight();
}
hasVoted[proposalId][msg.sender] = true;
if (support == uint8(VoteType.For)) {
proposal.forVotes += weight;
} else if (support == uint8(VoteType.Against)) {
proposal.againstVotes += weight;
} else {
proposal.abstainVotes += weight;
}
emit VoteCast(msg.sender, proposalId, support, weight);
return weight;
}
/**
* @dev 執行提案
*/
function execute(uint256 proposalId) public payable {
Proposal storage proposal = proposals[proposalId];
require(proposal.endBlock < block.timestamp, "Not ended");
require(proposal.forVotes > proposal.againstVotes, "Not passed");
require(!proposal.executed, "Already executed");
proposal.executed = true;
for (uint256 i = 0; i < proposal.targets.length; i++) {
(bool success, ) = proposal.targets[i].call{value: proposal.values[i]}(
proposal.calldatas[i]
);
require(success, "Execution failed");
}
emit ProposalExecuted(proposalId);
}
/**
* @dev 設置投票權重(僅 owner)
*/
function setVotingWeight(address[] calldata accounts, uint256[] calldata weights)
external
onlyOwner
{
require(accounts.length == weights.length, "Length mismatch");
for (uint256 i = 0; i < accounts.length; i++) {
votingWeight[accounts[i]] = weights[i];
}
}
/**
* @dev 查詢提案狀態
*/
function state(uint256 proposalId) public view returns (ProposalState) {
Proposal storage proposal = proposals[proposalId];
if (proposal.executed) return ProposalState.Executed;
if (proposal.canceled) return ProposalState.Canceled;
if (block.timestamp <= proposal.startBlock) return ProposalState.Pending;
if (block.timestamp <= proposal.endBlock) return ProposalState.Active;
if (proposal.forVotes > proposal.againstVotes) {
return ProposalState.Succeeded;
}
return ProposalState.Defeated;
}
}
三、DeFi 實用合約
3.1 簡易借貸合約
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SimpleLendingPool
* @dev 簡化版借貸協議
*/
contract SimpleLendingPool {
// Custom Errors
error LendingPoolDepositFailed();
error LendingPoolWithdrawExceedsBalance();
error LendingPoolInsufficientCollateral();
error LendingPoolHealthFactorOK();
error LendingPoolLiquidationFailed();
// 事件
event Deposited(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event Borrowed(address indexed user, uint256 amount, address collateralAsset);
event Repaid(address indexed user, uint256 amount);
event Liquidated(
address indexed liquidator,
address indexed borrower,
uint256 repayAmount,
uint256 collateralReceived
);
// 代幣接口
IERC20 public immutable depositToken;
IERC20 public immutable collateralToken;
// 利率模型參數
uint256 public constant COLLATERAL_FACTOR = 75; // 75% LTV
uint256 public constant LIQUIDATION_BONUS = 10; // 10% bonus
uint256 public constant INTEREST_RATE = 5; // 5% 年化
uint256 public constant LIQUIDATION_THRESHOLD = 80; // 80% 健康因子閾值
// 狀態
mapping(address => uint256) public deposits;
mapping(address => uint256) public borrows;
mapping(address => uint256) public lastUpdateBlock;
mapping(address => uint256) public interestAccrued;
uint256 public totalDeposits;
uint256 public totalBorrows;
uint256 public reserveFactor = 10; // 10% 到儲備
/**
* @dev 初始化
*/
constructor(address _depositToken, address _collateralToken) {
depositToken = IERC20(_depositToken);
collateralToken = IERC20(_collateralToken);
}
/**
* @dev 存款
*/
function deposit(uint256 amount) external {
require(amount > 0, "Zero amount");
// 累積利息
_accrueInterest(msg.sender);
// 轉入代幣
require(
depositToken.transferFrom(msg.sender, address(this), amount),
"Transfer failed"
);
deposits[msg.sender] += amount;
totalDeposits += amount;
lastUpdateBlock[msg.sender] = block.number;
emit Deposited(msg.sender, amount);
}
/**
* @dev 提款
*/
function withdraw(uint256 amount) external {
require(amount > 0, "Zero amount");
require(deposits[msg.sender] >= amount, "Insufficient balance");
// 累積利息
_accrueInterest(msg.sender);
deposits[msg.sender] -= amount;
totalDeposits -= amount;
require(depositToken.transfer(msg.sender, amount), "Transfer failed");
// 檢查健康因子
_checkHealthFactor(msg.sender);
emit Withdrawn(msg.sender, amount);
}
/**
* @dev 借款(以 collateral 為抵押)
*/
function borrow(uint256 amount) external {
require(amount > 0, "Zero amount");
// 累積利息
_accrueInterest(msg.sender);
uint256 collateralBalance = collateralToken.balanceOf(address(this));
uint256 maxBorrow = (collateralBalance * COLLATERAL_FACTOR) / 100;
require(
borrows[msg.sender] + amount <= maxBorrow,
"Exceeds collateral"
);
borrows[msg.sender] += amount;
totalBorrows += amount;
lastUpdateBlock[msg.sender] = block.number;
require(depositToken.transfer(msg.sender, amount), "Transfer failed");
emit Borrowed(msg.sender, amount, address(collateralToken));
}
/**
* @dev 還款
*/
function repay(uint256 amount) external {
require(amount > 0, "Zero amount");
require(borrows[msg.sender] >= amount, "Exceeds debt");
// 累積利息
_accrueInterest(msg.sender);
require(
depositToken.transferFrom(msg.sender, address(this), amount),
"Transfer failed"
);
borrows[msg.sender] -= amount;
totalBorrows -= amount;
lastUpdateBlock[msg.sender] = block.number;
emit Repaid(msg.sender, amount);
}
/**
* @dev 清算
*/
function liquidate(address borrower) external {
require(borrows[borrower] > 0, "No debt");
// 檢查健康因子
(uint256 healthFactor, ) = _getHealthFactor(borrower);
require(healthFactor < LIQUIDATION_THRESHOLD, "Health factor OK");
uint256 repayAmount = borrows[borrower] / 2; // 清算 50%
uint256 collateralReward = (repayAmount * LIQUIDATION_BONUS) / 100;
require(
depositToken.transferFrom(msg.sender, address(this), repayAmount),
"Repay transfer failed"
);
borrows[borrower] -= repayAmount;
totalBorrows -= repayAmount;
require(
collateralToken.transfer(msg.sender, collateralReward),
"Collateral transfer failed"
);
emit Liquidated(msg.sender, borrower, repayAmount, collateralReward);
}
/**
* @dev 計算健康因子
*/
function getHealthFactor(address user)
external
view
returns (uint256 healthFactor, bool isHealthy)
{
return _getHealthFactor(user);
}
function _getHealthFactor(address user)
internal
view
returns (uint256 healthFactor, bool isHealthy)
{
if (borrows[user] == 0) {
return (type(uint256).max, true);
}
uint256 collateralValue = collateralToken.balanceOf(address(this));
uint256 borrowValue = borrows[user];
healthFactor = (collateralValue * 100) / borrowValue;
isHealthy = healthFactor >= LIQUIDATION_THRESHOLD;
}
function _checkHealthFactor(address user) internal view {
(uint256 healthFactor, bool isHealthy) = _getHealthFactor(user);
if (!isHealthy) {
revert LendingPoolInsufficientCollateral();
}
}
function _accrueInterest(address user) internal {
if (lastUpdateBlock[user] == 0) {
lastUpdateBlock[user] = block.number;
return;
}
uint256 blocksPassed = block.number - lastUpdateBlock[user];
if (blocksPassed == 0) return;
uint256 borrowBalance = borrows[user];
if (borrowBalance == 0) {
lastUpdateBlock[user] = block.number;
return;
}
// 簡化利息計算(實際應使用精確時間)
uint256 interest = (borrowBalance * INTEREST_RATE * blocksPassed) / (100 * 365 * 225); // 近似
borrows[user] += interest;
totalBorrows += interest;
uint256 reserveAmount = (interest * reserveFactor) / 100;
interestAccrued[address(this)] += reserveAmount;
}
}
// 簡化 ERC20 接口
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
3.2 AMM 流動性池合約
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title ConstantProductAMM
* @dev 簡化版常數乘積 AMM(類似 Uniswap V2)
*/
contract ConstantProductAMM {
// Custom Errors
error AMMInsufficientLiquidity();
error AMMInsufficientOutputAmount();
error AMMInvalidK();
error AMMZeroAmount();
// 事件
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(
address indexed sender,
uint256 amount0,
uint256 amount1,
address indexed to
);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
IERC20 public immutable token0;
IERC20 public immutable token1;
uint256 public reserve0;
uint256 public reserve1;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
uint256 public constant FEE = 3; // 0.3%
uint256 public constant FEE_DENOMINATOR = 1000;
/**
* @dev 初始化
*/
constructor(address _token0, address _token1) {
token0 = IERC20(_token0);
token1 = IERC20(_token1);
}
/**
* @dev 添加流動性
*/
function mint(address to) external returns (uint256 liquidity) {
uint256 amount0 = token0.balanceOf(address(this)) - reserve0;
uint256 amount1 = token1.balanceOf(address(this)) - reserve1;
require(amount0 > 0 && amount1 > 0, "Zero amounts");
uint256 _totalSupply = totalSupply;
if (_totalSupply == 0) {
liquidity = _sqrt(amount0 * amount1);
} else {
liquidity = _min(
(amount0 * _totalSupply) / reserve0,
(amount1 * _totalSupply) / reserve1
);
}
require(liquidity > 0, "Invalid liquidity");
balanceOf[to] += liquidity;
totalSupply += liquidity;
_updateReserves();
emit Mint(to, amount0, amount1);
}
/**
* @dev 移除流動性
*/
function burn(address to)
external
returns (uint256 amount0, uint256 amount1)
{
uint256 liquidity = balanceOf[msg.sender];
require(liquidity > 0, "Zero liquidity");
uint256 _totalSupply = totalSupply;
amount0 = (liquidity * reserve0) / _totalSupply;
amount1 = (liquidity * reserve1) / _totalSupply;
require(amount0 > 0 && amount1 > 0, "Insufficient liquidity");
balanceOf[msg.sender] = 0;
totalSupply -= liquidity;
token0.transfer(to, amount0);
token1.transfer(to, amount1);
_updateReserves();
emit Burn(msg.sender, amount0, amount1, to);
}
/**
* @dev 兌換(swap)
*/
function swap(uint256 amount0Out, uint256 amount1Out, address to)
external
{
require(amount0Out > 0 || amount1Out > 0, "Zero output");
require(
amount0Out < reserve0 && amount1Out < reserve1,
"Insufficient liquidity"
);
uint256 balance0 = token0.balanceOf(address(this));
uint256 balance1 = token1.balanceOf(address(this));
// 確保輸入等於輸出差額(扣除手續費)
if (amount0Out > 0) {
require(balance0 - reserve0 >= amount0Out, "Invalid output");
}
if (amount1Out > 0) {
require(balance1 - reserve1 >= amount1Out, "Invalid output");
}
// 計算輸入(含手續費)
uint256 amount0In = balance0 > reserve0 + amount0Out
? balance0 - reserve0 - amount0Out
: 0;
uint256 amount1In = balance1 > reserve1 + amount1Out
? balance1 - reserve1 - amount1Out
: 0;
require(amount0In > 0 || amount1In > 0, "No input");
// 計算輸出(確保 k 不變)
uint256 balance0Adjusted = balance0 * FEE_DENOMINATOR - amount0In * FEE;
uint256 balance1Adjusted = balance1 * FEE_DENOMINATOR - amount1In * FEE;
require(
balance0Adjusted * balance1Adjusted >=
reserve0 * reserve1 * FEE_DENOMINATOR * FEE_DENOMINATOR,
"K invariant failed"
);
// 執行轉帳
if (amount0Out > 0) token0.transfer(to, amount0Out);
if (amount1Out > 0) token1.transfer(to, amount1Out);
_updateReserves();
emit Swap(
msg.sender,
amount0In,
amount1In,
amount0Out,
amount1Out,
to
);
}
/**
* @dev 獲取報價
*/
function getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) public pure returns (uint256 amountOut) {
require(amountIn > 0, "Zero amount in");
require(reserveIn > 0 && reserveOut > 0, "Insufficient liquidity");
uint256 amountInWithFee = amountIn * (FEE_DENOMINATOR - FEE);
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * FEE_DENOMINATOR + amountInWithFee;
amountOut = numerator / denominator;
}
/**
* @dev 獲取路徑報價(簡單版本)
*/
function quote(
uint256 amountA,
uint256 reserveA,
uint256 reserveB
) public pure returns (uint256 amountB) {
require(amountA > 0, "Zero amount");
require(reserveA > 0 && reserveB > 0, "Insufficient liquidity");
amountB = (amountA * reserveB) / reserveA;
}
function _updateReserves() internal {
reserve0 = token0.balanceOf(address(this));
reserve1 = token1.balanceOf(address(this));
}
function _sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function _min(uint256 x, uint256 y) internal pure returns (uint256) {
return x < y ? x : y;
}
}
// 簡化 ERC20 接口
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
四、安全最佳實踐
4.1 安全檢查清單模式
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SecurityChecker
* @dev 展示安全檢查模式的合約
*/
contract SecurityPatterns {
// 1. 零地址檢查
error ZeroAddress();
modifier zeroAddressCheck(address _address) {
if (_address == address(0)) revert ZeroAddress();
_;
}
// 2. 溢出檢查(Solidity 0.8+ 自動檢查)
// 不需要 SafeMath
// 3. 授權檢查
error Unauthorized();
modifier onlyAuthorized(address _account) {
if (_account != msg.sender) revert Unauthorized();
_;
}
// 4. 金額檢查
error InvalidAmount(uint256 amount);
modifier nonZeroAmount(uint256 _amount) {
if (_amount == 0) revert InvalidAmount(_amount);
_;
}
// 5. 時間鎖模式
struct Timelock {
uint256 timestamp;
uint256 delay;
}
mapping(bytes32 => Timelock) public timelocks;
event TimelockCreated(bytes32 indexed operationId, uint256 timestamp);
event TimelockExecuted(bytes32 indexed operationId);
function _createTimelock(
bytes32 _operationId,
uint256 _delay
) internal {
timelocks[_operationId] = Timelock({
timestamp: block.timestamp + _delay,
delay: _delay
});
emit TimelockCreated(_operationId, timelocks[_operationId].timestamp);
}
function _executeTimelock(bytes32 _operationId) internal {
Timelock memory tl = timelocks[_operationId];
require(tl.timestamp > 0, "Not timelocked");
require(block.timestamp >= tl.timestamp, "Not yet");
delete timelocks[_operationId];
emit TimelockExecuted(_operationId);
}
// 6. 速率限制
uint256 public constant RATE_LIMIT = 100 ether;
mapping(address => uint256) public lastWithdrawals;
mapping(address => uint256) public withdrawalAmounts;
event RateLimitExceeded(address indexed account);
modifier rateLimit(address _account, uint256 _amount) {
uint256 timeSinceLast = block.timestamp - lastWithdrawals[_account];
uint256 allowedWithdrawal = RATE_LIMIT;
// 重置每 24 小時
if (timeSinceLast >= 1 days) {
lastWithdrawals[_account] = block.timestamp;
withdrawalAmounts[_account] = _amount;
} else {
uint256 newAmount = withdrawalAmounts[_account] + _amount;
if (newAmount > RATE_LIMIT) {
revert RateLimitExceeded(_account);
}
withdrawalAmounts[_account] = newAmount;
}
_;
}
}
五、2026 年 Solidity 新特性
5.1 最新的語法特性
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/**
* @title ModernSolidityFeatures
* @dev 展示 Solidity 0.8.26 最新特性
*/
contract ModernSolidityFeatures {
// 1. 不可變變數的更好的初始化
// 可在建構函數中多次初始化
address public immutable owner;
uint256 public immutable deploymentTime;
// 2. 事件中的 string 和 bytes 索引
// 事件索引限制:最多 3 個索引參數
event NewFeature(
address indexed user,
string indexed description, // 0.8.26 開始支持 indexed string
bytes32 indexed featureId,
uint256 value
);
// 3. 更多的 assembly 功能
function assemblyExample() external pure returns (bytes32 result) {
assembly {
// 返回 keccak256 of empty string
result := keccak256(0x00, 0x00)
}
}
// 4. 結構化回傳值
struct WithdrawalRequest {
address user;
uint256 amount;
uint256 timestamp;
}
function getWithdrawalInfo()
external
pure
returns (WithdrawalRequest memory info)
{
info = WithdrawalRequest({
user: msg.sender,
amount: 0,
timestamp: block.timestamp
});
}
// 5. 函數類型增強
function executeWithCallback(
function(uint256) external returns (uint256) callback,
uint256 value
) external returns (uint256) {
return callback(value);
}
}
六、常見錯誤與解決方案
6.1 典型錯誤與修復
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title CommonBugsAndFixes
* @dev 展示常見錯誤及其修復
*/
contract CommonBugsAndFixes {
// Bug 1: 整數溢出
// 修復:Solidity 0.8+ 自動檢查溢出
// 錯誤版本
// function add(uint256 a, uint256 b) public pure returns (uint256) {
// return a + b; // 可能溢出
// }
// 正確版本(0.8+)
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b; // 自動溢出檢查
}
// Bug 2: 重入攻擊
// 修復:使用 Checks-Effects-Interactions 模式
// 錯誤版本
// function withdraw() public {
// uint256 balance = balances[msg.sender];
// (bool success, ) = msg.sender.call{value: balance}(""); // 先調用
// balances[msg.sender] = 0; // 後更新狀態 - 危險!
// }
// 正確版本
mapping(address => uint256) public balances;
function withdraw() public {
uint256 balance = balances[msg.sender];
require(balance > 0, "Zero balance");
// 1. Checks
// 2. Effects(先更新狀態)
balances[msg.sender] = 0;
// 3. Interactions(最後調用)
(bool success, ) = msg.sender.call{value: balance}("");
require(success, "Transfer failed");
}
// Bug 3: 精度損失
// 修復:使用高精度計算
// 錯誤
// function calculateFee(uint256 amount) public pure returns (uint256) {
// return amount / 100 * 3; // 精度丟失!
// }
// 正確
function calculateFee(uint256 amount) public pure returns (uint256) {
return (amount * 3) / 100; // 先乘後除
}
// Bug 4: 未初始化的映射
// 修復:Solidity 映射自動初始化為 0
mapping(address => uint256) public scores;
function checkScore(address user) public view returns (uint256) {
// scores[user] 自動為 0,無需額外檢查
return scores[user];
}
// Bug 5: 類型轉換錯誤
// 修復:顯式轉換
function typeConversion() public pure returns (uint256) {
// uint8 範圍 0-255
uint8 a = 255;
// uint8(a) + 1 會溢出,會 revert
// 需要明確處理
return a + 1; // 自動檢查
}
}
結論
本文涵蓋了 Solidity 智慧合約開發的核心範例,從基礎代幣合約到進階的代理模式、治理合約、借貸協議和 AMM 流動性池。這些範例展示了 2026 年 Solidity 開發的最佳實踐,包括:
- Custom Errors:取代 require 字串,節省 Gas
- Immutables:不可變變數優化
- ReentrancyGuard:防止重入攻擊
- 代理模式:合約可升級性
- CEI 模式:Checks-Effects-Interactions 安全模式
開發者在實際項目中應根據具體需求選擇合適的模式,並始終進行全面的安全審計。
參考資源
- Solidity Official Documentation - docs.soliditylang.org
- OpenZeppelin Contracts - github.com/OpenZeppelin/openzeppelin-contracts
- Solidity GitHub - github.com/ethereum/solidity
- CryptoZombies - cryptozombies.io
- Alchemy Solidity Tutorial - alchemy.com
相關文章
- SUAVE 去中心化排序器與 MEV 市場完整指南 — SUAVE(Secret compute / Unified Auction Virtualized Execution)是由 Flashbots 主導開發的去中心化區塊建構與 MEV 提取基礎設施。作為 MEV-Boost 的進化版本,SUAVE 旨在解決 MEV 領域的中心化問題,實現真正的去中心化排序器和公平的 MEV 市場。本文深入解析 SUAVE 的技術架構、經濟模型、與以太坊生態系統的
- ERC-4337 Bundler 完整實作指南:從原理到部署 — ERC-4337(帳戶抽象標準)是以太坊帳戶模型的重要革新,其核心創新是將帳戶驗證邏輯從共識層分離到應用層。在這個架構中,Bundler(捆綁器)是關鍵的基礎設施元件,負責收集用戶操作(UserOperation)、將其打包並提交到 EntryPoint 合約執行。本文深入解析 Bundler 的運作原理、核心元件的程式碼實作、以及部署與運維的最佳實踐。
- 以太坊與 Monad、Solid 分別深度比較:2026 年高性能區塊鏈技術架構解析 — 區塊鏈技術在 2025-2026 年迎來了新一波創新浪潮。以太坊持續主導智能合約平台市場的同時,Solana、Monad、Solid 等高性能區塊鏈各自動用不同的技術策略,試圖在區塊鏈不可能三角(可擴展性、安全性、去中心化)之間取得更好的平衡。本文深入比較以太坊與這些新興高性能區塊鏈的技術架構,從共識機制、執行環境、記憶體模型、經濟設計等多個維度提供工程師視角的完整分析,幫助開發者和投資者理解這些
- 以太坊 Gas 費用歷史趨勢與未來預測:2015-2026 數據深度分析 — 以太坊的 Gas 費用機制是網路經濟模型的核心組成部分,直接影響用戶體驗、開發者成本決策以及網路安全性的經濟激勵。自 2015 年以太坊主網上線以來,Gas 費用經歷了多次重大變革,從最初的簡單拍賣機制到 EIP-1559 的革命性改進,每一次變化都深刻塑造了以太坊的經濟生態。本篇文章透過完整的歷史數據回顧、費用結構分析、影響因素探討以及未來趨勢預測,為讀者提供對以太坊 Gas 費用的全面理解。
- 秘密交易與 Ring CT 技術深度解析:以太坊隱私交易的密碼學基礎 — 區塊鏈隱私保護是以太坊生態系統中最具挑戰性且最富創新性的技術領域之一。傳統區塊交易的透明性雖然保證了網路的安全性,但同時也暴露了用戶的財務隱私。秘密交易(Confidential Transactions)與環簽名(Ring Confidential Transactions,簡稱 Ring CT)是兩項核心的密碼學技術,它們為區塊鏈提供了在不犧牲透明性的前提下保護交易隱私的能力。本文深入解析這兩
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!