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.
- singleSwap
tuple
- funds
tuple
- limit
uint256
- deadline
uint256
- Matches
Calldata
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.