Decide whether a reverted write ran out of gas or was refused by the contract (MK-035).
Why this exists. The SDK returns { hash } without awaiting the receipt, by design, so
a revert that happens after a passing simulation is something the caller discovers later
and previously had no way to classify. Nothing in the error surface distinguished
exhaustion from refusal, and the two call for opposite responses.
What evidence a consumer actually has, without a tracing endpoint, established rather
than assumed:
gasUsed === gasLimit. Conclusive for exhaustion, but ONLY at the top level. The EVM
forwards at most 63/64 of the remaining gas to a nested call, so an inner frame can
exhaust its allowance while the outer frame keeps the last 1/64. A traced occurrence
of exactly that showed gasUsed 710023 against gasLimit 720980.
Re-executing the same call at the mined block. If it reverts WITH a reason, the
contract refused and the reason is the answer. This is the strong case.
If the re-execution does NOT revert, that is genuinely ambiguous. eth_call at a
block number runs against END of block state, after the failing transaction and
everything else in it, so a condition that was true mid block is invisible to it. It
is consistent with a nested out of gas AND with a race the caller lost.
Case 3 returns INDETERMINATE, and that is not a hedge: it is the honest boundary of what
is knowable here. debug_traceTransaction settles it, and most public endpoints do not
expose it. The advice for that case says so.
Decide whether a reverted write ran out of gas or was refused by the contract (MK-035).
Why this exists. The SDK returns
{ hash }without awaiting the receipt, by design, so a revert that happens after a passing simulation is something the caller discovers later and previously had no way to classify. Nothing in the error surface distinguished exhaustion from refusal, and the two call for opposite responses.What evidence a consumer actually has, without a tracing endpoint, established rather than assumed:
gasUsed === gasLimit. Conclusive for exhaustion, but ONLY at the top level. The EVM forwards at most 63/64 of the remaining gas to a nested call, so an inner frame can exhaust its allowance while the outer frame keeps the last 1/64. A traced occurrence of exactly that showedgasUsed 710023againstgasLimit 720980.eth_callat a block number runs against END of block state, after the failing transaction and everything else in it, so a condition that was true mid block is invisible to it. It is consistent with a nested out of gas AND with a race the caller lost.Case 3 returns
INDETERMINATE, and that is not a hedge: it is the honest boundary of what is knowable here.debug_traceTransactionsettles it, and most public endpoints do not expose it. The advice for that case says so.