函数修饰符 #
一、view修饰符 #
1.1 基本概念 #
view修饰符表示函数只读取状态,不修改状态。
solidity
contract ViewModifier {
uint256 public value = 100;
// view函数:只读取状态
function getValue() public view returns (uint256) {
return value;
}
// view函数可以读取多个状态变量
function getValues() public view returns (uint256, address) {
return (value, msg.sender);
}
}
1.2 允许的操作 #
solidity
contract ViewAllowed {
uint256 public value = 100;
mapping(address => uint256) public balances;
// ✓ 读取状态变量
function readStateVariable() public view returns (uint256) {
return value;
}
// ✓ 读取mapping
function readMapping(address user) public view returns (uint256) {
return balances[user];
}
// ✓ 读取address(this).balance
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
// ✓ 读取block和msg属性
function getBlockInfo() public view returns (
uint256 blockNumber,
uint256 timestamp,
address sender
) {
return (block.number, block.timestamp, msg.sender);
}
// ✓ 调用其他view函数
function callOtherView() public view returns (uint256) {
return readStateVariable() * 2;
}
// ✓ 使用表达式
function calculate() public view returns (uint256) {
return value * 2 + 10;
}
}
1.3 禁止的操作 #
solidity
contract ViewForbidden {
uint256 public value;
// ✗ 修改状态变量
// function badModifyState() public view {
// value = 1; // 错误
// }
// ✗ 触发事件
// event ValueChanged(uint256 newValue);
// function badEmitEvent() public view {
// emit ValueChanged(1); // 错误
// }
// ✗ 创建其他合约
// function badCreateContract() public view {
// new OtherContract(); // 错误
// }
// ✗ 使用selfdestruct
// function badSelfdestruct() public view {
// selfdestruct(payable(msg.sender)); // 错误
// }
// ✗ 调用非view函数
// function nonViewFunction() public {}
// function badCallNonView() public view {
// nonViewFunction(); // 错误
// }
// ✗ 使用低级调用
// function badLowLevelCall() public view {
// address(0).call(""); // 错误
// }
}
1.4 view函数的Gas消耗 #
solidity
contract ViewGas {
uint256 public value = 100;
// view函数从外部调用:不消耗Gas(除了交易费)
// view函数从内部调用:消耗Gas
function viewFunction() public view returns (uint256) {
return value;
}
function callViewFromNonView() public returns (uint256) {
// 从非view函数调用view函数,消耗Gas
return viewFunction();
}
}
二、pure修饰符 #
2.1 基本概念 #
pure修饰符表示函数既不读取也不修改状态。
solidity
contract PureModifier {
// pure函数:不读取也不修改状态
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
function multiply(uint256 a, uint256 b) public pure returns (uint256) {
return a * b;
}
}
2.2 允许的操作 #
solidity
contract PureAllowed {
// ✓ 使用函数参数
function useParams(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// ✓ 使用局部变量
function useLocalVariables() public pure returns (uint256) {
uint256 a = 1;
uint256 b = 2;
return a + b;
}
// ✓ 使用表达式
function useExpressions(uint256 a) public pure returns (uint256) {
return a * 2 + 10;
}
// ✓ 调用其他pure函数
function callOtherPure(uint256 a, uint256 b) public pure returns (uint256) {
return add(a, b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
// ✓ 使用keccak256等密码学函数
function hash(string memory data) public pure returns (bytes32) {
return keccak256(bytes(data));
}
// ✓ 使用数学运算
function power(uint256 base, uint256 exponent) public pure returns (uint256) {
return base ** exponent;
}
// ✓ 类型转换
function convert(uint256 a) public pure returns (uint8) {
return uint8(a);
}
}
2.3 禁止的操作 #
solidity
contract PureForbidden {
uint256 public value;
// ✗ 读取状态变量
// function badReadState() public pure returns (uint256) {
// return value; // 错误
// }
// ✗ 读取address(this).balance
// function badReadBalance() public pure returns (uint256) {
// return address(this).balance; // 错误
// }
// ✗ 读取block和msg属性(部分)
// function badReadBlock() public pure returns (uint256) {
// return block.number; // 错误
// }
// ✗ 调用view函数
// function viewFunction() public view returns (uint256) {
// return value;
// }
// function badCallView() public pure returns (uint256) {
// return viewFunction(); // 错误
// }
}
2.4 pure vs view #
solidity
contract PureVsView {
uint256 public value = 100;
// pure:不读取状态
function pureFunction(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// view:读取状态
function viewFunction() public view returns (uint256) {
return value;
}
// 选择原则:
// - 如果函数不需要读取状态,使用pure
// - 如果函数需要读取状态,使用view
// - pure比view更省Gas(从外部调用时)
}
三、payable修饰符 #
3.1 基本概念 #
payable修饰符表示函数可以接收ETH。
solidity
contract PayableModifier {
mapping(address => uint256) public balances;
// payable函数:可以接收ETH
function deposit() public payable {
balances[msg.sender] += msg.value;
}
// 非payable函数:不能接收ETH
function noPayable() public {
// 如果发送ETH会revert
}
}
3.2 使用msg.value #
solidity
contract MsgValue {
event Deposit(address indexed from, uint256 amount);
// 检查发送的ETH数量
function deposit() public payable {
require(msg.value > 0, "Must send ETH");
emit Deposit(msg.sender, msg.value);
}
// 要求最小ETH数量
function depositMin(uint256 minAmount) public payable {
require(msg.value >= minAmount, "Insufficient ETH");
emit Deposit(msg.sender, msg.value);
}
// 返回多余的ETH
function depositWithRefund(uint256 requiredAmount) public payable {
require(msg.value >= requiredAmount, "Insufficient ETH");
if (msg.value > requiredAmount) {
payable(msg.sender).transfer(msg.value - requiredAmount);
}
emit Deposit(msg.sender, requiredAmount);
}
}
3.3 payable地址 #
solidity
contract PayableAddress {
address public owner;
address payable public treasury;
constructor() {
owner = msg.sender;
treasury = payable(msg.sender);
}
// 发送ETH到指定地址
function sendToTreasury() public payable {
treasury.transfer(msg.value);
}
// 使用call发送ETH
function sendWithCall(address payable to) public payable {
(bool success, ) = to.call{value: msg.value}("");
require(success, "Transfer failed");
}
// 提取合约余额
function withdraw() public {
require(msg.sender == owner, "Not owner");
payable(owner).transfer(address(this).balance);
}
}
3.4 payable与view/pure #
solidity
contract PayableViewPure {
// payable不能与view/pure一起使用
// 因为接收ETH本身就是状态修改
// ✗ 错误:payable view
// function badPayableView() public payable view {}
// ✗ 错误:payable pure
// function badPayablePure() public payable pure {}
// ✓ 正确:payable可以单独使用
function goodPayable() public payable {}
}
四、修饰符组合 #
4.1 可见性与状态修饰符 #
solidity
contract ModifierCombination {
uint256 private value;
// public + view
function publicView() public view returns (uint256) {
return value;
}
// public + pure
function publicPure(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// public + payable
function publicPayable() public payable {}
// private + view
function privateView() private view returns (uint256) {
return value;
}
// internal + pure
function internalPure(uint256 a) internal pure returns (uint256) {
return a * 2;
}
// external + view
function externalView() external view returns (uint256) {
return value;
}
}
4.2 修饰符顺序 #
solidity
contract ModifierOrder {
// 推荐顺序:可见性 -> 状态修饰符
function goodOrder() public view returns (uint256) {
return 1;
}
// 也可以:状态修饰符 -> 可见性
// 但不推荐
function alternativeOrder() view public returns (uint256) {
return 1;
}
}
五、实际应用示例 #
5.1 众筹合约 #
solidity
contract Crowdfunding {
address public owner;
uint256 public goal;
uint256 public deadline;
uint256 public totalRaised;
bool public claimed;
mapping(address => uint256) public contributions;
event Contribution(address indexed contributor, uint256 amount);
event GoalReached(uint256 totalRaised);
event FundsWithdrawn(address indexed owner, uint256 amount);
constructor(uint256 _goal, uint256 _durationInDays) {
owner = msg.sender;
goal = _goal;
deadline = block.timestamp + _durationInDays * 1 days;
}
// payable:接收ETH
function contribute() public payable {
require(block.timestamp < deadline, "Campaign ended");
require(msg.value > 0, "Must send ETH");
contributions[msg.sender] += msg.value;
totalRaised += msg.value;
emit Contribution(msg.sender, msg.value);
if (totalRaised >= goal) {
emit GoalReached(totalRaised);
}
}
// view:只读状态
function getContribution(address contributor) public view returns (uint256) {
return contributions[contributor];
}
// view:只读状态
function getCampaignStatus() public view returns (
uint256 _totalRaised,
uint256 _remaining,
uint256 _timeLeft,
bool _goalReached
) {
_totalRaised = totalRaised;
_remaining = goal > totalRaised ? goal - totalRaised : 0;
_timeLeft = deadline > block.timestamp ? deadline - block.timestamp : 0;
_goalReached = totalRaised >= goal;
}
// pure:不读取状态
function calculateShare(uint256 contribution, uint256 total)
public
pure
returns (uint256)
{
require(total > 0, "Total is zero");
return (contribution * 100) / total;
}
// 非view非pure:修改状态
function claimFunds() public {
require(msg.sender == owner, "Not owner");
require(totalRaised >= goal, "Goal not reached");
require(!claimed, "Already claimed");
require(block.timestamp >= deadline, "Campaign not ended");
claimed = true;
payable(owner).transfer(totalRaised);
emit FundsWithdrawn(owner, totalRaised);
}
// 非view非pure:修改状态
function refund() public {
require(block.timestamp >= deadline, "Campaign not ended");
require(totalRaised < goal, "Goal was reached");
require(contributions[msg.sender] > 0, "No contribution");
uint256 amount = contributions[msg.sender];
contributions[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}
}
5.2 计算器合约 #
solidity
contract Calculator {
// pure:纯计算
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
function subtract(uint256 a, uint256 b) public pure returns (uint256) {
require(a >= b, "Underflow");
return a - b;
}
function multiply(uint256 a, uint256 b) public pure returns (uint256) {
return a * b;
}
function divide(uint256 a, uint256 b) public pure returns (uint256) {
require(b != 0, "Division by zero");
return a / b;
}
function modulo(uint256 a, uint256 b) public pure returns (uint256) {
require(b != 0, "Division by zero");
return a % b;
}
function power(uint256 base, uint256 exponent) public pure returns (uint256) {
return base ** exponent;
}
function sqrt(uint256 x) public pure returns (uint256) {
if (x == 0) return 0;
uint256 z = (x + 1) / 2;
uint256 y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
return y;
}
}
六、最佳实践 #
6.1 选择正确的修饰符 #
solidity
contract BestPractices {
uint256 public value;
// ✓ 纯计算:使用pure
function calculate(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// ✓ 读取状态:使用view
function getValue() public view returns (uint256) {
return value;
}
// ✓ 接收ETH:使用payable
function deposit() public payable {}
// ✓ 修改状态:无修饰符
function setValue(uint256 _value) public {
value = _value;
}
}
6.2 Gas优化 #
solidity
contract GasOptimization {
uint256 public value;
// 从外部调用view/pure函数不消耗Gas
// 但从内部调用会消耗Gas
function externalView() external view returns (uint256) {
return value;
}
function callFromInternal() public view returns (uint256) {
// 内部调用view函数消耗Gas
return externalView();
}
}
七、总结 #
修饰符要点:
| 修饰符 | 读取状态 | 修改状态 | 接收ETH | Gas消耗(外部调用) |
|---|---|---|---|---|
| 无修饰 | ✓ | ✓ | ✗ | 消耗Gas |
| view | ✓ | ✗ | ✗ | 不消耗Gas |
| pure | ✗ | ✗ | ✗ | 不消耗Gas |
| payable | ✓ | ✓ | ✓ | 消耗Gas |
使用建议:
- 纯计算使用pure
- 只读状态使用view
- 需要接收ETH使用payable
- 修改状态不使用修饰符
接下来,让我们学习特殊函数!
最后更新:2026-03-27