Declarative rules are in early access and are not enabled by default. Talk to us if you’d like to participate.
Comparisons
The basic building block compares one field with a value:
Ordering operators are for amounts (
totalRawValue) and numbers. Do not use contains to match an address: addresses share long runs of characters, so a partial match catches addresses you did not intend. Use in with the full addresses. Addresses are compared without regard to case.
Combining conditions
Lists and exists
Top-level fields such aschain.id are listed in Rule anatomy. A transaction can move several assets and touch several addresses, so those facts arrive as lists. You read a list with exists, which is true when at least one item matches its where:
Use
counterparties for denylists, so that naming an address always blocks it. Use effectiveCounterparties for allowlists, so that you do not have to list infrastructure you never chose.
Keep an asset and its amount inside the same exists. Split across two exists blocks, the two comparisons can match different assets: a small amount of the named token alongside a large amount of another one. An exists cannot contain another exists.
Matching every item
There is no “every” operator. To say “every item is X”, write “there is no item that is not X” withnot around exists:
The same pattern works for destinations:
not exists(address nin [list]) means every address is on the list.
Nesting limit
A condition can be at most five levels deep. Eachall, any, not, exists, and the final comparison counts as one level, so any then all then not then exists then a comparison is exactly five. A deeper condition is rejected when you save it. A layer can hold up to 200 rules per phase, and a list of values or conditions can hold up to 200 entries.
Combining flows
Both examples below are allow rules. Pair each with theDENY fallback. The outcomes were recorded by running the rules against the policy engine. The vendor is 0x5f09B2caaafD345EaE7B711A32CcBdf59befB4bB, and the USDC implementation is 0x43506849d7c04f9138d1a2050bbf3a0c054402dd.
Nothing but USDC to approved vendors. The first not exists says every asset is USDC, and the second says every address is approved. With plain exists, the rule would allow a transaction that moves USDC and something else.
any means either branch is enough. Each branch requires every asset to qualify, so a transaction that moves ETH and USDC together satisfies neither. This rule sits exactly at the five-level limit.
Destinations and tokens
- A token allowlist must name the token contract. A token transfer touches the token contract, so an allowlist of destinations that names only the vendor refuses the payment. On a proxy token such as USDC, name the implementation as well, and update the list if the token is upgraded to a new implementation.
- An allow rule says nothing about what it does not mention. The “either flow” rule above checks destinations only on the USDC branch, so ETH can go anywhere, including to a blocked address. If that matters, add a deny rule for the address in the same layer. A deny beats an allow.
- Test with a transaction that moves two assets. A single-asset transfer often cannot tell a correct rule from one that uses
existswhere it needsnot exists.
Next steps
Rule anatomy
Rule fields, the fallback rule, and approval requirements.
Rule examples
A ready-made rule for each common case, and two whole layers.
Declarative rules
How rules, the fallback, and the allow and deny styles work.