Conditions

Conditions

Zodiac Roles implements a condition system that allows for defining complex conditions over call data and ether value.

A condition is represented by a tree structure. Each node in the tree defines a condition operator and informs the call data range it targets. The root node of the tree generally targets the entire call data range, while children condition nodes allow setting conditions on specific fields of the decoded call data.

Node Properties

Each condition node is defined by the following properties:

paramType

Informs how to pluck to value from call data. Can be one of the following:

Static – Interpret the word currently in scope as a static value, i.e., a value with a fixed length padded to 32 bytes.

Dynamic – Interpret the word currently in scope as an offset to a dynamic length value, i.e., bytes or string.

Tuple – Decode the value as a tuple. The children condition nodes inform the types of the tuple's fields.

Array – Interpret the word as an offset to an dynamic length array. The children condition nodes inform the array element type.

AbiEncoded – Decode the value using standard ABI decoding. The children condition nodes inform the individual field types.

Calldata – Like AbiEncoded, but skips over the first 4 bytes (function selector).

children

An array of sub conditions, used for any of the following purposes:

  • Inform the decoding of complex types by defining types of their fields or elements (required for ParamType.AbiEncoded, ParamType.Calldata, ParamType.Tuple, ParamType.Array)
  • Set conditions on specific fields of a tuple type value (via Operator.Matches)
  • Set conditions on elements of an array type value (via Operator.ArraySome, Operator.ArrayEvery, Operator.ArraySubset)
  • Define branch conditions for n-ary logical operators (Operator.And, Operator.Or, Operator.Nor)

operator

Defines the conditional operation to apply to the value plucked from call data. Check out the Operators section for a list of all available operators.

compValue

Only used with comparison operators. Defines the value to compare against.

Operators

EqualTo

Checks equality of the plucked value to a given comparison value.

EqualToAvatar

This is equivalent to using EqualTo with the avatar address as compValue. Since this is a commonly used comparison, this extra operator is provided for convenience and optimization purposes. The avatar address is available to the condition evaluation function and thus does not need to be stored as a comparison value.

Pass

Allows any value. Used for condition nodes that are required solely for informing how to decode complex type value. For example, the types of all fields of a tuple must be defined so that the tuple can be correctly decoded.

Examples

Balancer swap

The following condition enforces that the Balancer single swap (opens in a new tab) function must only be used to swap WETH for DAI and vice versa. Also, sender and recipient for the swap must be set to the avatar address.

ABI inputs
condition
✅ example call data (valid)
⛔ example call data (violation)
0x
52bbbe2900000000000000000000000000000000000000000000000000000000000000e00000000000000000000000004f2083f5fbede34c2714affb3105539775f7fe6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f2083f5fbede34c2714affb3105539775f7fe640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
      sender: avatar address    recipient: avatar address        WETH/DAI pool ID    WETH token address  DAI token address        

The root condition node generally targets the entire call data range after the 4 bytes function selector. It uses ParamType.AbiEncoded for defining how to decode the bytes in that range.

We define Operator.Matches checks on both tuples. For singleSwap we require an exact match on the poolId fields. For the assetIn and assetOut fields we require the values to be either WETH or DAI using Operator.Or.

For swapInfo we require the sender and recipient fields to be set to the address of the avatar using Operator.EqualToAvatar.

This example shows Operator.Matches being used to set conditions on specific fields of a tuple type value.

Custom conditions

Custom conditions can be defined by implementing the ICustomCondition interface. For an example of a custom condition, check out the AvatarIsOwnerOfERC721.sol contract.

An important restricting to be aware of is that custom conditions must not keep a state that is updated for every transaction passing through the check. The reason is that at the level of the custom condition there is no way to tell whether the transaction will finally be executed or not.