Guard system

How to write calls that pass the Guard System, and how to handle rejections

For the risk-model framing — what the Guard System prevents and why — see Security: guard system. This page is the integrator's side: how to write calls that don't revert, how to enumerate what's supported, and how to surface failures back to users.

Call flow

Every vault interaction flows through PoolLogic.execTransaction(address to, bytes data):

  1. The vault looks up the contract guard registered for to via the factory.

  2. The guard's txGuard(poolManagerLogic, to, data) is called. It decodes the calldata, enforces protocol-specific rules, and returns a txType plus isPublic flag.

  3. If the contract guard returns txType == 0 (no match or rejection), or isn't registered, the vault falls back to the asset guard for to. If no asset guard is registered either, it defaults to the governance-configured ERC20Guard as a last resort. A revert with dh23 means every layer returned txType == 0.

  4. On approval, the external call executes.

  5. Some guards implement afterTxGuard to track post-call state (for example, recording open perp positions).

The manager/trader permission check happens at the vault layer before the guard runs. The guard itself doesn't distinguish manager from trader — see trader delegation for how that's enforced.

Two guard categories

Contract guards — registered against a destination contract address (a swap router, a lending pool, a position manager). They govern what calls can be made to that contract. Examples: OneInchV6Guard, AaveLendingPoolGuardV3, UniswapV3NonfungiblePositionGuard, GmxExchangeRouterContractGuard.

Asset guards — registered against an asset type (a token, an LP NFT, a perp position). They govern how the vault holds and unwinds that asset — balance calculation, USD pricing, withdrawal behavior. Examples: ERC20Guard, UniswapV3AssetGuard, AaveLendingPoolAssetGuard, HyperliquidPositionGuard.

A single vault operation may touch both. A Uniswap V3 mint needs the position manager contract guard to approve the call and an asset guard registered for the resulting LP NFT so the vault can price and withdraw it.

Enumerating support

Is this protocol callable from a vault?

Is this asset supported?

Is this asset enabled on a specific vault?

A guard being registered globally is necessary but not sufficient — the manager also has to have enabled that asset on their vault.

Common revert codes

Vault reverts use dh-prefixed strings. The full list of codes with descriptions is maintained in the contracts repo:

dHEDGEV2ErrorCodes.jsonarrow-up-right

Guard registry

The exact set of guards differs per chain (see deployment matrix). The full per-chain lists of contract guards, asset guards, and deprecated guards are maintained in the config/arrow-up-right directory of the contracts repo — each chain folder contains:

  • dHEDGE Governance Contract Guards.csv — registered contract guards

  • dHEDGE Governance Asset Guards.csv — registered asset guards

  • dHEDGE Assets list.json — supported assets

You can also query getContractGuard() and getAssetGuard() on the factory at runtime.

See also

Last updated