什么是 view、pure 和 payable?合约函数为什么能读、能写或能收 ETH
view、pure 和 payable 描述函数与链上状态及 ETH 的关系。本文讲清只读调用、Gas、STATICCALL、msg.value 和收款风险。
区块浏览器里,有些合约按钮放在“Read”区,点击就能看到余额;有些在“Write”区,需要钱包签名;还有些函数名旁写着 payable,可以随调用发送 ETH。很多人于是把 view 理解成“免费函数”、pure 理解成“更快函数”、payable 理解成“自动收款函数”,这些都只说对了一小部分。
Solidity 的状态可变性描述函数与链上状态和原生币的关系:view 承诺不修改状态,pure 承诺不读取也不修改状态,payable 允许调用携带 ETH。没有这些限制的普通函数则可以按代码读写状态,但仍受权限与条件约束。
可变性是函数对编译器和调用者的承诺,不是业务正确性或安全性的保证。
View:可以读,不能改
view 函数可以读取余额、配置、mapping、block.timestamp 等链上信息,但不能写状态变量、发事件、创建合约、发送 ETH 或调用会修改状态的函数。外部调用 view 时,现代 EVM 通常通过 STATICCALL 约束状态不能修改。
Pure:计算只依赖输入
pure 函数既不修改也不读取合约状态。它适合数学计算、格式转换和仅依赖参数的校验。读取状态变量、地址余额,以及大多数 block、tx、msg 属性都会违反 pure;官方文档特别说明 msg.sig 和 msg.data 属于例外。

Payable:允许这次调用带 ETH
函数没有 payable 时,向它附带 ETH 通常会回退;标记 payable 后,代码可以读取 msg.value 并决定如何处理。payable 只打开接收入口,不会自动给用户记余额,也不会自动退款或发代币。

四类函数快速对比
| 声明 | 读状态 | 写状态 | 接收 ETH |
|---|---|---|---|
| pure | 不允许 | 不允许 | 通常不允许 |
| view | 允许 | 不允许 | 通常不允许 |
| 普通非 payable | 允许 | 允许 | 不允许 |
| payable | 允许 | 允许 | 允许 |
“调用 View 免费”要看从哪里调用
用户通过 RPC 在链下读取 view,节点本地模拟执行,不产生上链交易,因此通常不支付 Gas。但若另一个状态修改函数在链上调用 view,它的计算仍消耗交易 Gas。复杂循环和大量读取不会因为写了 view 就没有成本。
为什么 Read 按钮不需要签名
链下读取不改变共识状态,任何节点都能根据指定区块计算结果。它不需要用户私钥授权。但 RPC 提供者、区块高度和代理实现可能影响读到的内容,界面显示也不是身份或真实性保证。
View 也可能因为条件失败而 Revert
只读不代表永远成功。数组越界、require 不满足、除零、外部只读调用失败或计算过重都可能让读取回退。前端应处理错误和空数据,不能把失败一律显示成零。
Pure 不是链下函数
pure 仍然编译进合约代码,也可以被链上函数调用。它只表达不依赖链上状态,不代表一定在用户设备运行。对于公开 pure 函数,链下与链上执行应得到一致结果,但调用环境和输入编码仍需正确。
读取 Immutable 为什么可能不是 Pure
即便 immutable 部署后不再变化,它的具体值由部署实例决定。官方文档指出,读取 immutable 可能被视为非 pure 操作,因为仅看函数输入无法知道该值。这再次说明“不会改变”与“不读取状态或环境”不是同一个概念。
Payable 与 Receive、Fallback 的关系
普通函数要接收伴随调用的 ETH,必须 payable;receive 本身必须 payable;fallback 若要接收 ETH 也必须 payable。调用具体函数和直接向地址转账的 calldata 不同,最终会进入哪个入口要按合约分流规则判断。
Msg.value 只属于当前调用
msg.value 表示当前消息调用携带的 wei 数量。内部函数沿用当前调用上下文,而新的外部调用会建立新上下文。多层合约交互中不能把最初用户发送的金额机械当成每一层的 msg.value。
Payable 最常见的记账风险
- 收到 ETH 后没有更新用户内部余额。
- 把 msg.value 与代币数量或价格单位混淆。
- 退款采用外部调用却没处理失败和重入。
- 允许多付但没有说明剩余资金怎样处理。
- 代理和路由调用改变了实际付款人或上下文。
状态修改函数模拟成功为何还会失败
钱包会先模拟交易,但真实上链时状态、价格、nonce、余额和区块时间可能已经变化。模拟结果只能说明在某个快照与参数下可执行,不保证未来区块仍成功。
开发者选择清单
- 函数无需状态时优先 pure,明确依赖边界。
- 只读链上信息时使用 view。
- 只有确实允许附带 ETH 的入口才标 payable。
- 把收款、记账、退款和提款分别测试。
- 检查 view 的循环、外部依赖和失败处理。
- 前端区分链下读取、模拟与真正交易。
普通用户如何判断钱包请求
只读查询通常不需要交易签名;改变状态需要签名和 Gas;payable 调用还要核对发送的 ETH 数量。签名前查看目标地址、函数、参数、value 和模拟结果。函数叫 deposit 或 mint 并不能证明资金会按预期处理。
官方资料
view、pure 的限制与 STATICCALL 行为参考 Solidity:State Mutability;payable 特殊入口参考同页的 Receive Ether Function 与 Fallback Function。
View、pure 和 payable 让接口意图更清楚,也让编译器阻止一部分错误。真正判断安全性时,还要继续看权限、外部调用、记账、代理和前端如何构造交易。
本文为链上指南原创科普内容,不构成任何投资建议。调用 payable 函数可能转移真实资产,请核对合约地址、msg.value、权限与模拟结果,并先进行小额验证。