Every evmquery monitor has one expression and one trigger, and the pair decides exactly when you get notified. Pick the wrong trigger and you either get nothing or get paged every check. This guide explains the two trigger kinds, condition and change, and then writes six real smart contract alert conditions you can copy.
TL;DR
Use condition when the expression returns a boolean and you want to hear
when it turns true. Use change when you want to hear that a value moved,
optionally by a minimum amount or in one direction. Both fire once per event,
not on every check.
Condition: fire when it becomes true
A condition trigger needs an expression that returns a bool. The monitor fires on the edge: the check where the result goes from false (or unknown, on the first check) to true. While it stays true, the monitor stays quiet. It fires again only after it has gone false and then true again.
usdc.balanceOf(wallet) < parseUnits("1000", 6)If the wallet is already under 1,000 USDC when you create the monitor, the first check fires. After that you hear nothing until the balance has recovered above the line and fallen below it again. To hear about recovery too, add a second monitor with the opposite comparison.
Change: fire when the value moves
A change trigger accepts expressions that return an integer, boolean, address, bytes or string. The first check records the value as a baseline and does not fire. From then on, any difference from the baseline fires, and the baseline resets to the new value. Each fire compares against the last fire, not against the original value.
For integers you can narrow what counts:
direction:any,upordown.threshold: the minimum movement, eitherbps(relative to the baseline, where 10000 is 100%) orabsolute(raw units of the value). The amount is a positive integer string.
Other types have no direction or threshold: any difference fires.
{ "kind": "change", "direction": "down", "threshold": { "unit": "bps", "amount": "500" }}That fires when the value falls to 95% of the baseline or lower. Because the baseline resets on each fire, a slow slide produces one alert per 5% step, not one alert forever.
Which one to use:
| You want to know | Trigger | Expression returns |
|---|---|---|
| A number crossed a fixed line | condition |
bool |
| A flag turned on | condition or change |
bool |
| Something moved, whatever the size | change, any |
int, address, bytes, string |
| Something moved by at least N percent | change with bps |
int |
| Something dropped by at least N units | change, down, absolute |
int |
Six expressions
All of these use the contract alias syntax from evmquery queries. Aliases such as usdc are declared in the monitor’s schema.contracts, and variables such as wallet in schema.context.
1. Balance below a threshold
usdc.balanceOf(wallet) < parseUnits("1000", 6)Trigger condition, with usdc at 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 on evm_ethereum and wallet declared as sol_address. USDC has 6 decimals, so parseUnits("1000", 6) is 1,000 USDC. For a hot wallet’s native balance, the low gas balance guide uses w.balance() on an address.
2. Total supply moves 5%
usdc.totalSupply()Trigger change, direction any, threshold bps 500. The expression returns an integer, so thresholds apply. 500 bps is 5%, relative to the baseline.
3. An NFT changes owner
nft.ownerOf(solInt(1))Trigger change, any. nft is the collection address, for example 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D (BAYC), and token ID 1 is a literal. The expression returns an address, so any new owner fires.
4. A pause flag flips
usdc.paused()USDC exposes paused(). Use condition to be told when it becomes true. Use change if you want both directions, since a bool under change fires whenever it flips, so you hear about the unpause too.
5. A Safe threshold changes
safe.getThreshold()Trigger change, any, with safe at a Safe multisig address, for example 0x0da0c3e52c977ed3cbc641ff02dd271c3ed55afe on Ethereum. A Safe that goes from 3-of-5 to 1-of-5 fires with the old and new value. To watch the owner count instead, use safe.getOwners().size(). The Safe owners and threshold guide shows what else the contract exposes.
6. Aave health factor below 1.1
aave_pool.getUserAccountData(wallet).healthFactor < parseUnits("1.1", 18)Trigger condition, with aave_pool at 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2 (Aave V3 on Ethereum) and wallet as sol_address. The health factor is an 18-decimal value, so 1.1 is parseUnits("1.1", 18). Wallets with no debt return a huge number, which is never below 1.1, so they never fire. The health factor guide explains the full struct.
Checking an expression before you save it
When you create a monitor, evmquery evaluates the expression once and returns the current value, its type and the block it was read at. If you pick condition but the expression returns an integer, the definition is rejected. The same test runs when you resume a paused monitor. A constant expression such as 1 < 2 is also rejected, because it reads no onchain state and could never change.
The result type is stored at creation. If a contract upgrade later changes what the expression returns, the monitor reports a deterministic error instead of guessing.
Limits to design around
- An expression can use at most 10 distinct contract methods and make at most 25 contract calls per check.
- The interval must be a multiple of 60 seconds, between your plan’s minimum and 24 hours. The Free plan checks every 15 minutes at best.
- A monitor reads one chain: Ethereum (
evm_ethereum), Base (evm_base), BNB Chain (evm_bnb_mainnet) or Polygon (evm_polygon). - Monitors compare state at each check, so pick an interval shorter than the window you care about.
Editing a trigger
Changing the expression, schema, context or trigger resets the monitor’s state. A condition that is still true fires again, and a change monitor takes a new baseline. Renaming a monitor or changing only its channels leaves the history alone. Edits go through the web app or PATCH /api/v1/monitors/:id, and the MCP guide covers creating monitors from prompts. See the developers page for the REST routes, and the webhook receiver guide for what arrives when a trigger fires.
Next steps
- evmquery Monitors launch post for the Free plan limits and how monitors work.
- Receive monitor webhooks to act on a fired trigger.
- Create monitors from Claude or Cursor to generate these expressions from a prompt.


