Every transaction through Blend passes through 4 independent security layers before it executes: transaction guards, rate limiting, reentrancy protection, and slippage verification.Documentation Index
Fetch the complete documentation index at: https://docs.blend.money/llms.txt
Use this file to discover all available pages before exploring further.
Transaction guards
Every Safe has aRolesGuard installed as its transaction guard. The guard runs two pre-execution hooks.
checkTransaction fires before any direct transaction. When the system is paused, only Safe owners can execute. Non-owners receive a SystemPaused() revert.
checkModuleTransaction fires before any module-initiated transaction. When paused, ALL module transactions are blocked. There is no owner bypass for module calls. This means the automation layer stops completely during a pause, even if the Safe owner tries to trigger it.
The pause mechanism is controlled by PAUSER_ROLE via pause(bool _shouldPause). This is a kill switch for the automation layer that does not affect the owner’s ability to withdraw directly.
Rate limiting
Every rebalance and vault action is rate-limited per (safe, vault) pair. The key iskeccak256(abi.encodePacked(safe, vault)).
MIN_SECONDS_BETWEEN_OPERATIONS is set between 1 and 300 seconds at deployment. The contract enforces strictly-greater-than comparison against the last operation timestamp.
The timestamp is set before execution. If the transaction reverts, all state changes (including the timestamp) roll back.
| Parameter | Value |
|---|---|
| Rate limit key | keccak256(safe, vault) per pair |
| Min interval | 1-300 seconds (set at deployment) |
| Failure behavior | Timestamp still updates |
| Scope | Per safe/vault pair, not global |
Reentrancy protection
The StrategyManager uses separate boolean guards for two execution paths.isRebalanceInitiated is set to true during executeRebalance and checked by downstream contracts. isVaultActionInitiated is set to true during executeVaultAction. These are independent flags, not a shared mutex.
This separation matters because vault actions and rebalances call different controller contracts. A rebalance cannot trick a vault action controller into executing, and vice versa.
Slippage protection
Three independent layers protect against unfavorable swaps. Layer 1: Governance maximum.maxSlippageBps is a hard ceiling set by governance. No swap can exceed this tolerance, regardless of what the caller requests.
Layer 2: Per-swap limit. Every swap call includes a slippageBps parameter. The SwapAdapter rejects any value exceeding maxSlippageBps.
Layer 3: Oracle verification. The SwapAdapter calculates the expected output using PriceLib.quoteToBase (or baseToQuote for reverse swaps) and then applies the slippage tolerance to derive a minimum acceptable output. The actual swap result must meet or exceed this minimum.
Beyond the three layers, checkSwapInvariants enforces a zero-balance invariant. The SwapAdapter must hold zero tokens of both the input and output before and after every swap. This prevents residual token accumulation that could be drained.
Access control
Multiple layers restrict who can call what.onlyExecutor - StrategyManager.executeRebalance and executeVaultAction are restricted to a single authorized executor address. This is the off-chain worker that triggers operations.
_onlyDelegateCall - All VaultControllers, including UserWithdrawController, compare address(this) against an immutable SELF address. This ensures the code only runs via delegatecall from a Safe, never as a standalone call.
Trusted broadcaster and chain - RolesReceiver validates both the broadcaster address AND the source chain ID on every cross-chain message. Both are immutable, set at construction. setPeer() always reverts. An attacker would need to compromise LayerZero AND match both trust anchors.
Temporal access - WhitelistedSwapAdapter only allows swaps during active isRebalanceInitiated() or isVaultActionInitiated() execution windows. Outside those windows, swap calls revert.
Single-owner validation - UserWithdrawController requires the Safe to have exactly one owner. This resolves the withdrawal recipient on-chain without relying on off-chain input.
OFAC screening
Blend screens wallet addresses at three stages before any funds move.| Stage | When | What happens |
|---|---|---|
| Account resolution | GET /account or SIWE verify | EOA screened against Chainalysis API |
| Deposit quote | quoteDeposit | Deposit EOA screened separately |
| Account-scoped routes | Every authenticated request | Account OFAC flag checked |
Haltable operations
The API supports a per-account-type halt state. WhenisHalted is true, the API returns HTTP 423 Locked for deposit-related operations.
Withdrawals are intentionally allowed during halt. Users can always retrieve their funds, even when the system is in a degraded state.
| Operation | During halt |
|---|---|
| Deposit quotes | Blocked (423) |
| Deposit session lock/submit | Blocked (423) |
| Withdrawals | Allowed |
| Balance and discovery | Allowed |
| Withdrawal quotes | Allowed |
| Balance queries | Allowed |
| Discovery | Allowed |
Architecture overview
See how Safe isolation and cross-chain coordination work.
Contracts
Explore the 9 contract systems powering the protocol.
Security model
Understand the defense-in-depth model and real-world scenarios.
Audits
Review independent security audit reports.