Skip to content

Rule Conditions and Actions

This page describes the conditions that you can set for any single rule you create while building a policy.

When you create a rule, you first set the Action to take if all the conditions of the rule are fulfilled. There are three action options:

  • Allow a transaction.
  • Block a transaction.
  • Require approval for a transaction.

A rule matches a transaction only when every condition in the rule matches. If any single condition does not match, the rule is skipped and the policy engine moves on to the next rule in the policy. A transaction that matches no rule is handled by the default rule.

These are the conditions you can define for a policy rule:

Initiator

The Initiator condition lets admins select which users or groups of users the rule applies to. A user can appear both as an individual and as part of a group.

Origin

The Origin condition lets admins select which vaults or groups of vaults the rule applies to.

Transaction type

The Transaction Type condition lets admins limit the type of transaction for which the rule is in effect. It can be one of the following:

  • Transfer: Sending funds directly to another recipient, using either the Transfer dialog in the web console or the API.
  • Contract call: Interaction with a smart contract or a DApp.
  • Allowance: A token approval (EVM chains only).
  • Message signature: One of three types:
    • EVM personal message
    • EVM typed data message
    • Non-EVM message
  • Black-box signature: Signing any arbitrary buffer (black-box vaults only).
  • Contract deployment: Deploying a smart contract to the blockchain.
  • In-app swap: Swapping using in-app integrated DEX aggregators. This also covers the required allowance transaction.

Conditions and transaction types

Certain conditions apply only to specific types of transactions. When a rule specifies a condition that does not apply to a certain transaction type, that rule cannot match transactions of that type. The table below summarizes which conditions apply to each type. See also the specifics of setting policy rules for message signatures.

TypeSubtypeOriginInitiatorRecipientAmountPeriodic amountAssetABICosmos MessageEVM Typed Message
Contract Call✓✓✓✓✓✓✓✓x
Transfer✓✓✓✓✓✓xxx
In-app swap✓✓✓✓✓✓xxx
Allowance✓✓✓✓✓✓✓xx
MessageEVM Typed Data✓✓✓xxxxx✓
EVM Personal Message✓✓x✓✓xxxx
Non-EVM✓✓x✓✓xxxx
Black box✓✓xxxxxxx

Recipient

The Recipient condition lets admins limit where a transaction goes. What counts as the recipient depends on the transaction type:

Transaction typeRecipient
TransferThe address that receives the funds.
AllowanceThe address that receives the token allowance (the spender).
Contract callThe contract or program being called, not the final destination of any funds it moves.

Multiple recipients
On some chains, such as Solana, one transaction can call several programs, and each program is a recipient. For the rule to match, every recipient must be covered by the rule. For example, a Kamino deposit on Solana calls both the Kamino Vaults program and the Kamino Farms program. A rule that lists only one of them won't match, but a rule that specifies the Kamino DApp covers both.

A policy rule can specify the destination/recipient as one or more of the following:

  • Address book entry or address group

  • Internal vault or vault group

  • DApp on a specific chain: You can filter DApps by chain, making it easier to find the exact match when the same DApp appears on multiple chains:

    DApp condition filtered by chain
  • Custom address

If a user interacts with a smart contract that transfers funds to a third-party address using a DApp, Fordefi matches the address of the contract (and its corresponding DApp) as the recipient of the transaction, rather than the final destination of the funds.

Strict Solana recipient matching

On Solana, the Recipient condition only sees the programs a transaction calls directly, which is the behavior described above. An opt-in organization setting, under Settings > Safeguards, extends this. See Upcoming Changes for details.

Asset

The Asset condition lets admins specify the token or tokens that the transaction is sending. For a rule to match a transaction, each of the outgoing (that is, sent) tokens in the transaction must appear in the list of tokens in the rule. For example, when swapping MATIC for USDC (that is, sending 1 MATIC and getting back 1.5 USDC), a rule that specifies MATIC matches the transaction, whereas a rule that specifies USDC does not.

A transaction that sends no assets does not match any rule that specifies a particular set of assets. This includes transactions that only receive tokens, such as borrowing from a lending protocol: the vault receives the borrowed tokens and a debt token, and sends nothing. The debt token counts toward the outgoing amount, but it is not an outgoing asset for the Asset condition.

Amounts

The policy engine supports both transaction-level and periodic amounts.

Transaction amount

The Transaction Amount condition lets admins specify a maximum amount in USD for the transaction. For transfers, the amount is simply the amount of the transfer.

Determining the amount of a contract call is more subtle. First, since a contract call does not directly specify an amount, its value is simulation-based, derived from the transaction simulation results. Second, a single contract call can transfer multiple tokens both into and out of the vault. Fordefi adds up the USD value of all the token transfers in the contract call in each direction: out of the vault and into the vault. Token transfers that do not affect the vault are ignored. Native-token transfers made as part of the transaction count too (for example, SOL paid as rent for new accounts on Solana). The transaction then has two associated amounts:

  • The outgoing amount: The total USD value of transfers that reduce the vault's holdings. Sending assets and receiving liabilities both count toward this amount.
  • The net amount: The total USD value of all transfers out of the vault, minus the total USD value of all transfers into the vault.

When setting up a rule, you can choose whether to use the outgoing or net amount.

Examples

ActionWhat movesOutgoing amountNet amount
Swap $100 USDC for $99.99 USDTSends $100 USDC, receives $99.99 USDT$100$0.01
Borrow $500 USDC from AaveReceives $500 USDC and the debt token variableDebtEthUSDC, priced at −$500$500 (receiving a liability counts as outgoing)≈ $0 (the asset and its debt token cancel out)
Deposit $500 USDC into AaveSends $500 USDC, receives 500 aEthUSDC (priced at ≈ $1)$500≈ $0 (the deposit and its receipt token cancel out)
Deposit $500 USDC into a protocol whose receipt token has no priceSends $500 USDC, receives receipt tokens valued at $0$500$500

Choosing between outgoing and net

  • Use outgoing amount to cap the size of a position. The net amount of a deposit or borrow is close to $0 whatever its size, because the receipt or debt token offsets the tokens moved. A rule like "net amount ≤ $1,000" matches a $1,000,000 Aave borrow.
  • Net amount depends on whether the tokens you receive are priced. If a receipt token has no price, it is valued at $0, so the full deposit counts toward the net amount. If that token gets a price later, the same deposit's net amount drops to about $0 and the rule may start matching deposits of any size. The outgoing amount doesn't change with the receipt token's price.
  • Net amount works best for swaps, where both sides are reliably priced and the net amount reflects the value actually lost.
  • Net amounts can be slightly off from what you'd expect. For example, a lending protocol may round debt up, or count interest earned on an existing position as part of the tokens you receive.

Periodic amount

The Periodic Amount condition limits the total spending volume across transactions within a specified period of time. It lets you choose the time period, whether the limit applies cumulatively across all users or per individual user, and which transactions count toward it.

For example, you can limit the cumulative transaction volume for all users in your organization to $100,000 per day.

Periodic amounts are based on the net (rather than outgoing) amount of a transaction. As a result, transactions with a net amount near $0, such as deposits into and borrows from lending protocols, add almost nothing to the running total. To limit how much can be deposited or borrowed, use a transaction-level outgoing amount condition instead.

Edit periodic amount dialog
  • Use the > or ≤ symbols to define the amount. To set a maximum amount, use ≤.
  • Scope: choose what counts toward the limit:
    • Count matching transactions (default): only transactions matched by this rule count toward its limit. Available only with the ≤ operator. Each transaction counts toward exactly one rule: the rule it matched.
    • Count all transactions: every transaction counts toward the limit, regardless of which rule matched it. This was the default behavior before matching-transaction scope was introduced.
  • Set a time period. Counters are aligned to fixed calendar periods in UTC:
    • Hourly
    • Daily: 00:00–23:59 UTC
    • Weekly: Monday 00:00 to Sunday 23:59 UTC
    • Monthly (every 30 days)
  • Select one of the user options:
    • All users: the limit applies cumulatively across the organization.
    • Per user: the limit applies to each user separately.

Counters

Fordefi shows the current usage of each periodic amount condition in the rule view to make troubleshooting easier. Each counter shows the limit, the number of transactions counted in the current period, the spent amount, and a usage bar. A View transactions link opens the Transactions page filtered to the matching rule, period, and (for per-user counters) initiator.

Periodic amount counter
  • For "All users" rules, a single counter is shown per rule.
  • For "Per user" rules, all non-zero counters are shown: one per user with usage in the current period.

Counters and policy changes

When other conditions in a rule change (for example, the recipient list), counters are retained. Fordefi does not re-evaluate historical transactions against the updated rule. When the periodic amount condition itself changes (period, scope, or limit), the counter is reset.

Handling of various transaction states

  • A transaction counts toward the spending limit quota as soon as it is created. Until it completes, the amount is computed from the transaction simulation. Once it completes, the amount is computed from what was actually executed on the blockchain.
  • If a transaction is aborted, its amount is credited back to the quota.
  • If a transaction is accelerated, the amount counts toward the quota only once, even though both the original and accelerated versions are pending.
  • If a transaction is cancelled, its amount is credited back to the quota as soon as the cancellation transaction is confirmed.

Handling of token allowances

Token allowances count toward both transaction-level and periodic spending limits. Unlimited allowances are handled specially: they require approval if they would exceed the periodic limit, but their value is not added to the running total. This prevents a single unlimited allowance from using up the entire limit and blocking all further transactions.

For example, with a policy allowing up to $1,000 of daily spending without approval:

  • Allowance of $400 → Auto-approved (total: $400)
  • Unlimited token allowance → Requires approval (because it represents potentially unlimited spending), but doesn't add to the running total
  • Allowance of $400 → Auto-approved (total: $800)
  • Allowance of $400 → Requires approval (total would be $1,200, exceeding the $1,000 limit)

Handling of missing prices and amounts

Fordefi generally takes token prices from CoinGecko. Tokens that are not listed there, which often happens with more exotic tokens like LP tokens and lending protocol receipt tokens, have no USD price.

A token the vault receives has no price. The token is valued at $0, and the amount conditions are evaluated as usual with the remaining priced transfers. For example, depositing $500 of USDC into a protocol whose receipt token has no price gives an outgoing amount of $500 and a net amount of $500. See Choosing between outgoing and net.

The amount of the transaction can't be determined. This happens when a contract call's amount can't be estimated by simulation: simulation isn't supported on the chain, the simulation fails, or the transaction reverts in simulation. It also happens when the vault sends a token that has no price. In these cases, Fordefi evaluates the policy conservatively:

  • If a rule does not have an amount condition, the rule is matched as usual.
  • If a rule has a ≤ ("less than or equal to") amount condition, the rule is not matched. The rationale is that the unknown amount might be above the limit in the rule.
  • If a rule has a > ("greater than") amount condition, the rule is matched out of an abundance of caution.

Transaction Screening

The Transaction Screening condition lets admins act on the result of a transaction screening provider. The condition selects a provider and one or more of that provider's results. This way a rule can block a transaction that a provider flags, or allow it without further approval when the provider marks it as safe.

ProviderSelectable results
BlockaidBenign, Malicious, Warning
HypernativeAccept, Auto Accept, Deny, Warn

Because the result field is multi-select, one condition can cover several results from the same provider: selecting Hypernative with both Warn and Deny matches a transaction that Hypernative either warns on or denies. To act on more than one provider, add a separate condition for each.

Like every other condition, the screening condition is combined with the rest of the rule's conditions using AND. For example, a Block rule with Hypernative Deny and "net amount ≤ $10" blocks a transaction only when Hypernative denies it and its net amount is $10 or less. When Hypernative accepts a transaction, that rule doesn't match, whatever the amount.

The condition is available only for providers your organization has activated. If no provider is active, the Transaction Screening condition cannot be added to a rule.

See Set policy rules on screening results for examples and for how the condition behaves when a screening result is unavailable.

ABI

The ABI defines how to encode and decode data in contract calls, enabling the execution of methods within smart contracts. It includes details such as the method name.

  • Users can input the ABI in the 4-byte hash format so that Fordefi can check if the transaction matches the hash.

  • Users can also set conditions more granularly, by specifying the method's parameters and their values. Fordefi lets you create a policy based on the parameter types in contract calls listed in the table below: address, integer, Boolean, bytes (including bytes32, the fixed-size byte types bytes1–bytes31, and dynamic bytes), arrays, and tuples.

    For each parameter, you must specify its position in the method's parameter list.

Learn more by studying Advanced ABI check samples.

The following table lists the parameter types Fordefi supports in ABI conditions, the operations available for each, and how values should be formatted.

TypeDescriptionSupported operationsNotes
Addressaddress: Holds a 20-byte value (the size of an Ethereum address).=, !=, "is origin vault", "is any vault", address or group from the address book.
Integer, Unsigned Integer (uint)Numeric parameters.

int, uint: Signed and unsigned integers of various sizes. Keywords uint8 to uint256 in steps of 8 (unsigned of 8 up to 256 bits) and int8 to int256. uint and int are aliases for uint256 and int256, respectively.
=, !=, <, >, ≤, ≥
BooleanThe possible values are the constants true and false.=
bytes32A fixed-size 32-byte value, input as a hex string prefixed with 0x (exactly 64 hex characters).=, !=, is empty, contains, does NOT contain, [i:j] =The fixed bytes type is 32 bytes in ABI encoding.
bytes1–bytes31Fixed-size byte values smaller than 32 bytes, input as a hex string prefixed with 0x, padded or exactly matching the declared type size.=, !=, is empty, contains, does NOT contain, [i:j] =bytes4: Function selectors are bytes4 values. This lets policy authors constrain which inner function is called, where the outer function is fixed but the inner target varies by a selector argument.
bytes (dynamic)A dynamic byte array, input as a hex string prefixed with 0x. Empty bytes are represented as 0x.=, !=, is empty, contains, does NOT contain, [i:j] =
Array (example: address[], uint256[], uint256[3])Conditions target a specific element within an array-type parameter, by zero-indexed position. The value format follows the underlying element type.Inherited from the element type (example: =, != for address; =, !=, <, >, ≤, ≥ for numeric; =, != for boolean and bytes elements).Dynamic arrays use offset+length ABI encoding; the evaluation engine must decode the full array before applying the positional condition. Fixed-size arrays (example: uint256[3]) are statically encoded, so positional access is straightforward.
Tuple (struct)Conditions target individual fields within a tuple by zero-indexed field position. Multiple field conditions on the same tuple are combined with AND.Per field, inherited from the field type (=, != for address and boolean; =, !=, <, >, ≤, ≥ for numeric; positional element match for array fields).Tuples are ABI-encoded as a sequence of their component types. The evaluation engine must decode the tuple at the declared top-level parameter position before applying field-level conditions.

EVM typed message

See Set Policy Rules for Message Signatures to learn more.

Cosmos message

A Cosmos message is the specific instruction or action that users want to execute on the Cosmos blockchain. Messages can represent various types of actions, such as transferring tokens, voting on proposals, or delegating staking tokens to validators.

For example, suppose you want to require different approval quorums for claiming staking rewards and for un-delegating your staked assets. You can do this by setting fine-grained policy rules on the MsgUndelegate message.

Cosmos message condition example