# viem 模拟成功为什么仍可能失败：核 sender、value 与覆盖状态

整理：链小知（AI 辅助）
资料核对日期：2026-10-12
引用地址：https://lianxiaozhi.com/tools/guides/viem-simulatecontract-sender-value-revert-boundary
难度：进阶
核验方式：官方文档整理，未执行真实账户操作

读取或构造写入前模拟参数，分开自定义错误、临时授权状态、返回 result 与待执行 request，不把模拟成功当真实执行。

## 使用工具
- [viem](https://lianxiaozhi.com/tools/viem) · [官方入口](https://viem.sh/)

## 前置条件
- 准备已有viem项目版本、公开sender地址、正确chainId/目标地址/ABI、nonpayable或payable函数及原args；有既存模拟日志时带block/value/override和原错误。
- 只做官方模拟流程说明或已有记录排查。sender地址无需私钥；不建立Local Account，不执行writeContract、授权、签名或广播。本篇不承诺后续交易成功或费用。

## 1. 先确定预期网络与可写方法 ABI

使用已有Public Client配置和getChainId结果核chainId，将目标地址/ABI版本/完整functionName/args并存。simulateContract针对nonpayable/payable函数，不把普通view读取与写入模拟混成一份完成记录。

官方页面有mint示例ABI标view但Solidity段为可写，以及文案“模拟成功即可写”的概括；固定源码类型限定nonpayable/payable。按真实目标ABI和项目类型核验，不原样复制不一致示例或示例token地址。

核对结果：网络、目标、方法可变性与参数类型相符，示例差异未当实际能力。
依据：[viem getChainId：返回number](https://viem.sh/docs/actions/public/getChainId)
依据：[viem simulateContract：sender、request和自定义错误](https://viem.sh/docs/contract/simulateContract)
依据：[viem 固定源码：模拟结果与请求](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/simulateContract.ts)

## 2. 明确 sender 而不创建签名账户

以公开地址传account，如既有senderAddress；这表示模拟msg.sender，不要求私钥，也不证明拥有该地址控制权。避免从文档Local Account示例导入privateKeyToAccount。

未提供account或设null时，页面称transport可能负责填sender；固定实现也可继承client.account。因此排查已有模拟必须保存实际sender和client配置，不能把匿名调用成功推到另一个用户。sender不同可改变权限/余额相关分支。

核对结果：模拟地址与预期sender一致；未知sender时停止权限结论。
依据：[viem simulateContract：sender、request和自定义错误](https://viem.sh/docs/contract/simulateContract)
依据：[viem 固定源码：block选择与call边界](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/call.ts)
依据：[viem 固定源码：模拟结果与请求](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/simulateContract.ts)

## 3. 固定区块和 value 的原单位

对已有记录固定blockNumber bigint或blockHash，或明确blockTag。标签默认受client.experimental_blockTag影响；网页blockNumber文字number与bigint示例冲突，固定CallParameters为bigint，不同选择不要同时塞入。

value是该链原生币wei的转入量，与ERC20 args中的数量不同；payable函数才按实际ABI传value。网页value写number但例parseEther与项目类型需核，保存原整数字符串。gas是Gas数量，gasPrice/maxFeePerGas/maxPriorityFeePerGas是wei/gas，不能把其中任一直接叫总费用。

核对结果：sender、块、args、原生value和费用参数单位分列。
依据：[viem simulateContract：sender、request和自定义错误](https://viem.sh/docs/contract/simulateContract)
依据：[viem 固定源码：block选择与call边界](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/call.ts)
依据：[viem 固定源码：模拟结果与请求](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/simulateContract.ts)
依据：[viem Public Client：缓存与聚合](https://viem.sh/docs/clients/public)

## 4. 标出 stateOverride 假设再解释结果

若模拟输入含stateOverride或blockOverrides，把每个地址/slot/value和假设写清楚。官方例用stateDiff给allowance slot临时置最大值，使transferFrom返回true；该结果是改过模拟状态后的分支。

这些覆盖不持久化真实批准、余额或区块属性。没有override与有override的结果不可直接比较；slot算错、节点不支持或ABI错误时保留原错，不能把覆盖当“刷新授权”或让用户直接发送覆盖请求。

核对结果：结果标明真实所读状态与假设覆盖的区别，未声称已经授权。
依据：[viem simulateContract：sender、request和自定义错误](https://viem.sh/docs/contract/simulateContract)
依据：[viem 固定源码：block选择与call边界](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/call.ts)

## 5. 沿错误链解析 ABI 中的自定义 error

在既有项目捕获BaseError后用err.walk(e=>e instanceof ContractFunctionRevertedError)，检查该错误的data?.errorName和args；ABI必须包含实际error条目，否则缺名不能解释为没有revert。

同时保留脱敏原错误、sender/target/function/args、revert字节和块条件。transport认证/限流、参数编码/解码与有证据的合约拒绝分开；不要用重复模拟或补授权交易替代原因排查。

核对结果：只记录有ABI/原错误支持的errorName，未知data保留未知。
依据：[viem simulateContract：sender、request和自定义错误](https://viem.sh/docs/contract/simulateContract)
依据：[viem 固定源码：模拟结果与请求](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/simulateContract.ts)

## 6. 拆开 result 与 request，停在模拟完成线

simulateContract返回result和request：result是当时call的ABI解码结果，request是可供后续writeContract的参数。固定函数中call batch:false后解码并构造request，没有发送交易。request保留的ABI、address、args、account、value及块/覆盖字段要与原输入逐项核。

不要将request当txHash或receipt；官方writeContract配对是另一个有权限和费用的执行阶段。未来状态、sender、额度或费用变化都可使真实交易不同。本任务归档原输入、覆盖、结果/错误和未执行边界，不导入Wallet Client继续发送。

核对结果：形成模拟诊断记录；无广播、交易hash或真实到账的虚构结论。
依据：[viem simulateContract：sender、request和自定义错误](https://viem.sh/docs/contract/simulateContract)
依据：[viem 固定源码：模拟结果与请求](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/simulateContract.ts)

## 常见错误
- 模拟批准或余额覆盖不写入链上状态，返回true不是资产已到账。
- 错误示例ABI可与可写源码有差异；不能借页面示例推任意合约/链支持。
- 节点服务费/限流、真实交易Gas与后续执行均未验收；当前固定commit非已安装release。

## 完成检查
- 保留版本、链、sender、方法/args、块、value/费用单位、覆盖及结果/错误完整对照。
- 明确仅文档核验与模拟流程；没有账户密钥、签名、广播或资金结果。

## 相关知识
- [钱包签消息与签交易有什么区别？不付 Gas 的签名也要读内容](https://lianxiaozhi.com/learn/wallet-message-vs-transaction)
- [Gas 怎么计算？交易失败为什么仍可能扣费](https://lianxiaozhi.com/learn/gas-and-failed-transactions)
- [合约代码已验证，为什么仍不能直接判断安全？](https://lianxiaozhi.com/learn/verified-contract-boundaries)

## 官方依据
- [viem getChainId：返回number](https://viem.sh/docs/actions/public/getChainId)
- [viem simulateContract：sender、request和自定义错误](https://viem.sh/docs/contract/simulateContract)
- [viem 固定源码：模拟结果与请求](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/simulateContract.ts)
- [viem 固定源码：block选择与call边界](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/call.ts)
- [viem Public Client：缓存与聚合](https://viem.sh/docs/clients/public)
