@reverts
Whether a live call reverts: true when the chain refuses the call, false when it resolves; -!> matches the reason and a lens selects an error argument.
On-chain (@reverts!): Bare probes test call failure. Error expectations resolve live targets and arguments before matching the final call's revert data.
Returns: any
Syntax
Section titled “Syntax”@reverts(call arrow? error? lens?)Arguments
Section titled “Arguments”| Name | Type | Description |
|---|---|---|
call | address | A :: call expression (or chain, or on-chain helper) to probe |
[arrow] | string | -!> — expect a specific error |
[error] | string | Error signature to match, e.g. InsufficientBalance(uint256,uint256) (Error and Panic work by bare name) |
[lens] | array | Lens selecting one error argument as the value, e.g. [_ $] |
Examples
Section titled “Examples”# Probe whether a view call reverts, at build timeset $missing @reverts(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{decimals()(uint8)})print $missing- The argument must be a live call (or on-chain helper) — a constant
cannot fail, so passing one is an error rather than a vacuous
false. - Only the CHAIN refusing the read answers
true: a revert, or a call into an address with no code. A missing ABI, an unknown variable or an unreachable node still throw, because those are the script or the setup being wrong, and reporting them as a revert would turn a typo into a measurement. - The call argument is not resolved before the helper runs — the
resolution failing is the answer, so
@revertsreceives the expression unevaluated. - Asking the opposite question — that a read still resolves — is
@bool!(not @reverts!(…)). It is the rarer of the two: an assertion about a value already fails when the read behind it reverts, so a bare liveness probe is only needed when there is no value to check.
The arrow: match the reason
Section titled “The arrow: match the reason”-!> ErrName(types) narrows the probe from "does it revert" to "does it
revert WITH THIS ERROR". Write the error signature inline — the types are
what the selector is computed from, and what a lens navigates.
Error (the require reason string) and Panic work by bare name.
set $failsRight @reverts(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{withdraw(uint256)() 100} -!> InsufficientBalance(uint256,uint256))A trailing lens selects one of the error's ARGUMENTS as the value instead
of a boolean — the same $/_ vocabulary a return lens uses, as a
separate space-set argument:
set $required @reverts(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{withdraw(uint256)() 100} -!> InsufficientBalance(uint256,uint256) [_ $])set $reason @reverts(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{withdraw(uint256)() 100} -!> Error(string) [$])Without a lens the answer is a boolean: true only when the call reverts
AND the selector matches; a different error, a bare revert, or a call that
resolves all answer false. With a lens there is no boolean to fall back
to — a call that resolves or mismatches is an error, because the value the
lens promises does not exist.
-?!> has no probe form: an expectation either matches or the probe
fails. To fall back to a value when a read reverts, use
@orElse.
See Also
Section titled “See Also”On-chain face (@reverts!)
Section titled “On-chain face (@reverts!)”The bare probe compiles to the negation of the core's isValid(param)
primitive: 1 when anything inside the wrapped expression reverts at
assertion time, 0 when it resolves. assert folds the negation into an
Eq 0 constraint on the raw isValid operand, so the direct form costs no
extra call; only composing it inside @bool! materializes the comparison.
The arrow forms probe the final target in the same call frame that captures
its revert data. A direct call uses the core's revertData; a call with
live arguments or a computed target uses an Expressions ProbeCall node. Earlier
chain hops and arguments resolve first, then the final call's own error is
matched. Errors from an earlier hop are not mistaken for the final target's
reason.
On a match the selector is stripped, so the revert payload is a clean ABI
tuple of the error's arguments: a lens navigates them exactly as it
navigates a call's return, and the selected value composes anywhere a read
does — including as the target of a further ::! read. Asserted bare, an
arrow probe drops its isValid wrapper entirely and judges a
zero-constraint entry on revertData itself, so a failing assertion
reports DidNotRevert or UnexpectedRevertData(expected, actual) instead
of a flat constraint failure.
The faces answer the same question at different moments, which is the whole
point of having both: @reverts says whether the call fails NOW, while the
script is being built, and is the one to branch on with if; @reverts!
says whether it fails when the batch executes, and is the one to assert on.
A contract that gets paused, self-destructed or upgraded between the two
makes them disagree, and that gap is usually the thing worth asserting.
Composing @reverts! with and/or inside @bool! does NOT make the
other operand safe: the logic operators are Operations calls, so the core
resolves both operands before either is combined, and a revert in the second
one takes the whole assertion down before the first one's answer can be
read. @reverts! swallows the revert of its own operand and nothing else.
To guard a comparison on a read that may revert, use @orElse!.
# Assert that a read still reverts — nobody has approved this allowance,# and that must remain true. Nothing else can say this: a fallback can mask# a revert, but it cannot require one.assert @reverts!(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{transferFrom(address,address,uint256)(bool) 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d 1000000000000000000000000000000})
# Not just that it reverts — that it reverts for the RIGHT reasonassert @reverts!(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{transferFrom(address,address,uint256)(bool) 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d 1000000000000000000000000000000} -!> InsufficientBalance(uint256,uint256))
# An error argument as a value: the shortfall the revert reportsassert @reverts!(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{withdraw(uint256)() 100} -!> InsufficientBalance(uint256,uint256) [_ $]) >= 100
# The other direction: guard a batch on a view still resolvingassert @bool!(not @reverts!(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{decimals()(uint8)}))
# Compose into boolean logic — safe here because every operand is a probe,# so there is no revert left for the eager `and` to trip overassert @bool!(@reverts!(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{transferFrom(address,address,uint256)(bool) 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d 1000000000000000000000000000000}) and not @reverts!(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d::{symbol()(string)}))