Solidity 中 view、pure 和 payable 函数修饰符有什么区别?
view 可读不可写状态变量,pure 不可读也不可写,payable 允许接收 ETH。无修饰符的函数可读可写。view 和 pure 不消耗 gas(外部调用时),因为节点可以本地模拟执行而不上链。但 view/pure 在合约内部被交易调用时,调用者仍需付 gas。payable 的唯一作用是让函数能通过 msg.value 收到 ETH,非 payable 函数收到 ETH 会自动 revert。
追问
view 函数真的不花 gas 吗?
外部调用(call / eth_call)不花 gas,因为是只读模拟。但如果一笔交易内部调用了 view 函数,那笔交易本身要付 gas——view 只是承诺不修改状态,不代表调用它的上下文免费。
payable 和 non-payable 的 gas 差异?
non-payable 函数开头会自动插入 require(msg.value == 0) 检查(约 200 gas)。payable 跳过这个检查,所以 gas 略低。如果函数明确需要收 ETH,加 payable 既是功能需求也省 gas。
为什么编译器会警告"view 函数修改了状态"?
因为你在 view 函数里调用了写操作(写 storage、发 ETH、触发事件等)。编译器按修饰符检查,不符合就报错。解决:要么去掉 view(确实需要写),要么确保只读。emit 事件在 view 函数中也不允许,因为事件本身是状态变更的日志。
pure 函数里能用 block.timestamp 吗?
不能。block.timestamp、block.number、msg.sender 都属于读取区块链状态,pure 里不允许。只允许用函数参数和内存变量做纯计算。如果你需要读链上状态但不写,用 view。
接口中的 view/pure 声明有什么用?
接口中声明 view/pure 是给编译器的契约——实现合约的对应函数也必须是 view/pure。如果实现合约把 view 改成 non-view,编译会报错。这保证了外部调用者可以安全地用 eth_call 调用而不用发交易。