# Get DeFi protocol integrations Source: https://docs.mellow.finance/api-reference/get-defi-protocols GET /v1/defi/protocols Retrieves information about all DeFi protocol integrations and their points distribution across different chains. # Get user's vaults DeFi positions Source: https://docs.mellow.finance/api-reference/get-user-defi-positions GET /v1/defi/users/{user_address} Retrieves detailed information about a user's positions across all integrated DeFi protocols, including points earned, balances, and protocol-specific details. # Get user's vaults positions Source: https://docs.mellow.finance/api-reference/get-user-positions GET /v1/users/{user_address} Retrieves detailed information about a specific user's positions across all Mellow Protocol vaults. This includes their liquidity provisions, earned profits, and current holdings as a Liquidity Provider. # Get all vaults Source: https://docs.mellow.finance/api-reference/get-vaults GET /v1/vaults Retrieves comprehensive information about all available vaults in the Mellow Protocol ecosystem. Each vault represents a smart contract that manages liquidity across different DeFi protocols. The response includes vault configurations, associated tokens, current performance metrics, and operational parameters. # Core Vaults and Other Approaches Source: https://docs.mellow.finance/core-vaults-and-other-approaches How Core Vaults compare to ERC-4626, ERC-7540, and common vault architectural patterns – evaluated against institutional requirements. ## Core Vaults and other approaches Mellow provides vault infrastructure for launching onchain Earn, treasury, and managed yield products. Distribution partners use Core Vaults to launch these products under their own brand, without constructing the management layer from scratch. ERC-4626 and ERC-7540 are not competitors to Core Vaults – they define vault interfaces. Core Vaults operate at the management layer: strategy execution, permissions, accounting, compliance, and multi-venue control. This page compares both interface standards and broader architectural patterns against the requirements institutional capital introduces. *** ### Institutional requirements Basic vault interfaces cover deposits, share issuance, and redemptions. Institutional vault infrastructure requires additional controls for settlement timing, permissions, accounting, compliance, NAV, and multi-venue execution. **Settlement flexibility.** Not all strategies settle atomically. Some strategies have unbonding periods. Tokenized funds and private credit may redeem on fixed schedules. CEX positions settle offchain. A vault managing capital across these sources needs synchronous and asynchronous flows, often within the same product. **Strategy isolation.** Strategy isolation limits the impact of failures or misconfiguration in a single allocation. Conservative lending and higher-risk strategies need independent execution contexts with independent risk parameters. **Operation-level risk controls.** Risk policies described only in documentation depend on process. Risk policies enforced at the contract level become execution rules – scoped per strategy, per asset, per operator, and per action. **Multi-venue execution.** A delta-neutral strategy or cross-venue carry trade needs DeFi protocol access and CEX execution under one permission model, one NAV framework, and one vault-level control surface. **Time to new venues.** Yield opportunities move quickly. When a new protocol or market becomes attractive, the speed at which a vault can safely add it determines whether the curator captures the opportunity or misses it. Architectures that require custom code per integration turn every new venue into an engineering and audit cycle. **NAV for externalized capital.** When capital is deployed to a CEX, held in tokenized treasuries, or locked in an unbonding period, onchain balances do not reflect reality. NAV must be oracle-defined. **Compliance at the vault level.** Jurisdiction-aware mandates, allowlisted depositors, transfer-restricted shares, and per-issuer eligibility constraints should be enforced through the vault permission system, not handled only at the distribution layer. **Agent-bounded execution.** Autonomous agents can operate under the same guardrails as human curators, with permissions enforced by verifiers rather than manual oversight. *** ### How common patterns address these requirements **Interface standards: ERC-4626 and ERC-7540.** ERC-4626 standardized synchronous deposit-and-share operations. ERC-7540 added async requests. Both address the interface layer – neither addresses strategy isolation, operation-level permissions, multi-asset strategy accounting, oracle-defined NAV, or compliance. These are outside the standards' scope. Core Vaults operate at that management layer, using ERC-4626 and ERC-7540 as interfaces rather than as the system itself. **Adapter-based architectures.** Protocol-specific adapters handle deposits, withdrawals, and accounting per integration. Each adapter adds audit surface, maintenance load, and integration-specific failure modes. Some adapter-based systems add distribution layers or strategy composition on top, but the core integration model remains per-protocol code. Core Vaults replace per-protocol code with verifier configuration, so the audit surface does not grow with each new venue. **MPC-based architectures.** Capital routes through institutional custody for offchain execution. The vault has limited ability to enforce onchain constraints while capital is outside the contract. NAV depends on offchain reporting. DeFi composability is limited. This pattern fits pure CeFi execution where onchain composability is not a requirement. Core Vaults instead keep DeFi and CeFi under one onchain permission model, with CeFi accessed through off-exchange settlement rather than routed entirely off-chain. **Single-protocol architectures.** Vaults designed around one protocol offer tighter integration and smaller audit surface. The curator's allocation must fit within that protocol's supported markets – cross-venue strategies are architecturally constrained, and the vault's roadmap, risk profile, and economics are coupled to a single protocol's governance and development decisions. Core Vaults are multi-protocol by design, so a curator can diversify across venues and is not bound to one protocol's roadmap. *** ### How Core Vaults address these requirements | Requirement | Common gap | Core Vaults | | ---------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Settlement flexibility | Sync-only or async-only, no mixed flows | Async queues for deposits and redemptions. Sync and async strategies coexist. Signature queues for pre-approved participants | | Strategy isolation | Capital pools into a single execution context | Each strategy operates in its own subvault with dedicated verifier constraints. Strategy isolation limits cross-strategy spillover | | Operation-level risk | Permissions are external or vault-boundary-only | 60+ onchain permission types scoped by strategy, asset, operator, action. Verifiers revert unauthorized transactions before execution | | Multi-venue execution | DeFi-only or CeFi-only, not both | DeFi under verifier constraints. CeFi via Copper ClearLoop and Ceffu. One permission model across both | | Time to new venues | Each integration is a code and audit cycle | Verifier configuration rather than new vault code. For supported interaction patterns, new venues are added without expanding the core audit surface | | NAV for externalized capital | Share price from onchain balances only | Oracle-defined NAV with three-tier validation – normal, suspicious, rejected – and automatic escalation | | Vault-level compliance | External or at distribution endpoint | Jurisdiction-aware mandates, allowlisted depositors, transfer-restricted shares, and per-issuer eligibility can be enforced at the vault level rather than only at the distribution endpoint | | Agent-bounded execution | No onchain guardrails for autonomous operators | Agents operate under the same verifiers as human curators, with the same permission checks and execution bounds | *** ### The verifier model The way a vault connects to yield sources determines its audit surface, maintenance cost, and how quickly it can support new venues. Traditional adapter-based architectures encode protocol-specific logic for every venue, so each new integration means new code, new audit surface, and new maintenance overhead. Core Vaults separate what the vault can do from what it is allowed to do. Execution is handled by shared vault logic. Permissions are defined by verifiers – declarative rules that specify which operations are allowed, scoped to target contracts, function selectors, asset whitelists, and parameter bounds. Any transaction outside those rules reverts before it executes. The result is that integration becomes configuration. For supported interaction patterns, adding a new venue is primarily a mandate and verifier setup task rather than new vault code, so the core audit surface stays constant as venues are added. *** ### Capability matrix | Capability | Core Vaults | ERC-4626 | ERC-7540 | Adapter-based | MPC-based | Single-protocol | | --------------------------- | ------------------------------ | -------- | ------------------------ | ---------------------- | ---------- | --------------- | | Sync operations | Yes | Yes | Implementation-dependent | Usually yes | Varies | Yes | | Async operations | Yes | No | Yes | Varies | Yes | Varies | | Mixed sync and async | Yes | No | Implementation-dependent | Varies | Varies | Varies | | Strategy isolation | Yes – subvaults | No | No | Varies | No | Varies | | Multi-venue: DeFi and CeFi | Yes | No | No | Usually no | CeFi only | No | | Operation-level permissions | 60+ types | No | No | Varies / external | External | Varies | | Oracle-defined NAV | Vault-native | External | External | External | Offchain | Varies | | Multi-source composition | Yes | No | No | Yes, adapter-dependent | No | Limited | | Agent-bounded execution | Yes | No | No | Not native / external | No | No | | CeFi custody integration | Off-exchange settlement rails | No | No | No | Core model | No | | Vault-level compliance | Vault-level | No | No | Varies | Varies | Varies | | Verifier-based integration | Yes | No | No | No | No | No | | New venue without new code | Yes – configuration | N/A | N/A | No – per-protocol code | No | Limited | | Vendor independence | Yes – multi-protocol by design | N/A | N/A | Varies | No | No | *** ### Choosing the right approach Interface standards and single-purpose architectures each solve a slice of the problem: a synchronous interface, an async primitive, a fixed set of protocol adapters, or an off-chain custody route. Each works until a strategy needs more than that slice – a second venue, a CeFi leg, a compliance constraint, a faster integration path. Core Vaults are built for the full surface rather than a slice. Multi-protocol allocation, mixed DeFi and CeFi execution, operation-level risk controls, subvault isolation, jurisdiction-aware compliance, oracle-defined NAV, fast venue integration, and agent-bounded execution operate within one management layer, under one permission model, on one audit surface. A curator does not assemble these from separate systems or migrate when a strategy outgrows its original design. The same infrastructure covers a single-protocol vault on day one and a multi-venue structured product later, without re-architecting. For institutional onchain products that need to hold up across venues, asset types, and regulatory requirements, Core Vaults are the management layer to build on. *** ### Core Vaults at a glance * **Architecture:** Verifier-based – declarative rules, not protocol-specific adapters * **Permissions:** 60+ onchain permission types, operation-level scoping * **Strategy model:** Subvault isolation with independent execution contexts * **DeFi access:** Aave, Morpho, Euler, Fluid, Spark, Ethena, Gearbox, Curve, Uniswap, Cowswap, Pendle, Symbiotic, EigenLayer, and others * **CeFi access:** Binance, Bybit, Deribit, OKX, Coinbase, Gate.io, Kraken via off-exchange settlement rails – Copper ClearLoop and Ceffu * **NAV:** Oracle-defined, three-tier validation with automatic escalation * **Compliance:** Jurisdiction-aware mandates, allowlisted depositors, transfer-restricted shares * **Audits:** Sherlock, Nethermind * **Track record:** Vault infrastructure since 2021, zero security incidents * **Product evolution:** Concentrated Liquidity Vaults → MultiVaults → Interop Vaults → Core Vaults # Core Vaults for RWA Allocation Source: https://docs.mellow.finance/core-vaults-for-rwa-allocation Core Vault infrastructure for managing tokenized RWA allocations with per-issuer eligibility, async redemption, oracle-defined NAV, and verifier-enforced controls. ## Core Vaults for RWA Allocation #### Overview Tokenized real-world assets (RWA) are one of the primary asset types for vault-based allocation products built on Mellow. Asset managers, funds, treasuries, and asset issuers can use Core Vaults to hold and manage eligible tokenized instruments – tokenized treasuries, money-market funds, private credit, and commodities – under defined mandates with onchain risk enforcement and compliance controls. Tokenization creates the asset. It does not create the management layer around it. Each tokenized instrument carries its own eligibility, pricing, settlement, and transfer requirements, and a managed product has to respect them throughout the asset lifecycle. The product is defined by the mandate. The infrastructure remains the same. #### Time to market Building an RWA allocation product from scratch requires smart contracts, security audits, integrations with regulated issuers, risk monitoring, and compliance review per jurisdiction. Mellow provides these components as existing, audited infrastructure. The distribution partner and vault manager configure a vault, define the mandate, and launch. This can reduce launch timelines from months of custom development to weeks of configuration and review. #### Key concepts **Mandate** A mandate is the set of rules that defines how a vault may operate. It includes eligible depositors, supported assets and issuers, approved venues, allocation caps, liquidity requirements, verifier rules, and oracle constraints. The mandate is encoded at the contract level and enforced by onchain verifiers. Operations that fall outside the mandate revert. **Roles** * Depositors provide capital * Distribution partners own the product surface and customer relationship. For RWA allocation they include: * Institutions and asset managers – onchain allocation products with eligibility, oracle-defined NAV, liquidity, and risk controls * Asset issuers – strategy products that make their tokenized instruments more useful * Wallets, exchanges, brokers, custodians – managed RWA and treasury products under their own brand * Vault managers or curators operate the strategy within the mandate * Mellow provides the vault infrastructure * Vault contracts enforce the mandate #### RWA use cases **Allocation vaults** A multi-asset vault that holds tokenized treasuries, tokenized credit, commodity exposure, lending positions, and liquidity buffers under one mandate. The mandate defines eligible assets and issuers, depositors, venues, allocation limits, and liquidity requirements. The vault manager operates inside that mandate. The vault enforces the boundary. Per-issuer eligibility, asynchronous settlement, transfer-restriction handling, and oracle-defined NAV apply across positions with different pricing sources and settlement timings. Tokenized treasuries held in these vaults can also serve as the conservative base layer of a stablecoin treasury product; see[ Core Vaults for Stablecoins](https://docs.mellow.finance/core-vaults-for-stablecoins) for that use case. **Asset-issuer vaults** Asset issuers can use vaults to make tokenized instruments more useful across treasury, collateral, reinvestment, and allocation products. A stablecoin issuer can build treasury yield products. A tokenized treasury issuer can build reinvestment vaults. A credit issuer can build allocation vaults around its origination flow. Vault infrastructure becomes part of how an asset competes on utility, not only on trust and liquidity. #### Vault architecture for RWA allocation RWA allocation uses the full Core Vault feature set – multi-asset accounting, asynchronous liquidity, asset eligibility, and oracle-defined NAV – rather than a simple ERC-4626 interface. **Strategy allocation** The mandate defines the approved asset and strategy set. The following are example categories. Actual availability depends on the vault mandate, integrations, jurisdiction, and asset eligibility. * Tokenized treasuries and money-market funds – BlackRock's BUIDL, Ondo's USDY, Superstate's USTB, Franklin Templeton's BENJI, Ondo's OUSG, Circle's USYC. Conservative base layer. Redemption timing varies by issuer (T+0 to T+2); the vault handles async settlement natively. * Tokenized credit – tokenized private credit and credit funds such as ACRED and ACRDX. Longer redemption windows and offchain administration; the vault processes redemptions through queues and tracks withdrawable separately from economic NAV. * Commodity-backed tokens – gold-backed instruments such as PAXG and XAUT. Largely permissionless; these exercise multi-asset accounting and oracle-defined NAV rather than eligibility controls. * Lending markets – Aave, Morpho, Compound, and other onchain lending protocols, where RWA positions can also serve as collateral. The vault enforces approved markets and allocation caps. * CeFi venues – via Copper ClearLoop and Ceffu, under the same permission model as onchain allocations. A single vault can hold positions across all categories simultaneously. Multi-asset accounting and oracle-defined NAV produce a coherent portfolio view across assets with different pricing sources, settlement timings, and liquidity profiles. Adding a new approved asset or venue is typically configuration rather than new contract code. Beyond being held, tokenized RWAs can serve as allocation inputs – collateral, liquidity buffers, or yield legs – within a broader mandate. **Risk enforcement and verifiers** Core Vaults enforce 60+ onchain permissions at the contract level: * Allocation caps per strategy, per asset, and per venue * Asset whitelists – the vault only holds approved assets * Venue whitelists – the vault only routes to approved targets * Oracle price checks before operations execute * Withdrawal queue controls for fair redemption ordering Verifiers are the onchain components that validate each action against the vault's permission set. The verifier model is composable – routine operations may require a single verifier, while high-impact operations can require multiple verifier approvals, time locks, or enhanced validation. Verifiers do not distinguish between human curators and autonomous agents. The same rules apply. Guardrails are configurable through structured governance but cannot be breached during execution. **Compliance** Regulation is moving into the asset and the mandate. The token enforces who can hold it. The mandate enforces what can be done with it. The vault enforces who can operate it. Compliance rules are applied at the vault level, so different products can enforce different eligibility and transfer requirements without changing the base vault architecture. Core Vaults treat mandates as configurable objects. * Share-token eligibility – configurable hooks for KYC and KYB providers, transfer agents, accreditation registries, and jurisdiction-based restrictions * Asset eligibility – per-asset and per-issuer rules; regulated tokenized instruments with transfer restrictions are handled separately from permissionless positions Two consequences for RWA allocation: the vault itself must be an eligible holder of each instrument it allocates to, and the vault's own share token may need downstream transfer restrictions so that vault exposure stays inside the same eligibility perimeter as the underlying. Different products on the same platform enforce different compliance perimeters without separate codebases. #### Agent-operated RWA vaults RWA allocation suits agent operation: continuous, mandate-bounded decisions across instruments with different settlement and liquidity profiles. The same verifier model that bounds a human vault manager bounds an agent – approved assets, venues, allocation bands, and NAV constraints are enforced before execution, and operations outside the mandate revert. An agent can rebalance and manage liquidity at frequencies impractical for human operators, while the vault ensures it never exceeds its mandate. #### Related pages * [Core Vaults for Stablecoins](https://docs.mellow.finance/core-vaults-for-stablecoins) – stablecoin yield and treasury vaults * [Vault Infrastructure for Fintech Earn Products](https://docs.mellow.finance/vault-infrastructure-for-fintech-earn-products) – architecture overview and full use case reference # Core Vaults for Stablecoins Source: https://docs.mellow.finance/core-vaults-for-stablecoins How fintechs, payment platforms, and institutional treasuries use Mellow vault infrastructure to manage stablecoin balances – multi-strategy allocations, full treasury mandates, and Earn products. ### Overview Stablecoins are one of the primary asset types for vault-based yield and treasury products on Mellow. Companies holding stablecoin balances – payment processors, fintechs, exchanges, banks, neobanks, stablecoin issuers, marketplaces, trading platforms – can use Core Vaults to manage that capital under defined mandates with onchain risk enforcement and compliance controls. The same vault architecture supports multiple stablecoin use cases, from narrow yield mandates to full treasury management. The product is defined by the mandate. The infrastructure remains the same. ### Time to market Building a stablecoin vault product from scratch requires smart contracts, security audits, DeFi integrations, risk monitoring, and compliance review per jurisdiction. Mellow provides these components as existing, audited infrastructure. The distribution partner and vault manager configure a vault, define the mandate, and launch. This can reduce launch timelines from months of custom development to weeks of configuration and review. ### Key concepts **Mandate** A mandate is the set of rules that defines how a vault may operate. It includes eligible depositors, supported assets, approved venues, allocation caps, liquidity requirements, verifier rules, and oracle constraints. The mandate is encoded at the contract level and enforced by onchain verifiers. Operations that fall outside the mandate revert. **Roles** * **​Depositors** provide stablecoin capital * **​Distribution partners** – wallets, exchanges, fintechs, banks, brokers, stablecoin platforms – own the product surface and customer relationship * **​Curators or vault managers** operate the strategy within the mandate * **​Mellow** provides the vault infrastructure * ​**Vault contracts** enforce the mandate ​ ### Stablecoin use cases #### **Earn products** Earn products on Mellow exist across multiple asset types. This section covers stablecoin Earn specifically. A distribution partner – wallet, exchange, fintech, bank, neobank, broker, asset issuer, or stablecoin platform – offers yield on stablecoin balances under their own brand. The user deposits an eligible stablecoin, sees an APY, and can withdraw according to the product's redemption rules.​ In the stablecoin context, an Earn product is a vault with a yield-focused mandate: approved strategies, risk limits, compliance constraints, and depositor eligibility configured for the partner's product requirements. The distribution partner owns the customer and the brand. The vault manager operates the strategy. Mellow provides the vault infrastructure. The examples below describe possible product profiles. Actual availability depends on the vault mandate, integrations, jurisdiction, and asset eligibility. | Configuration | Strategy set | Risk profile | Typical partner | | ---------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------- | | **Conservative** | Tokenized T-bills and money-market funds only | Low yield, low risk | Regulated platforms needing defensible underlying assets | | **Balanced** | Tokenized treasuries + onchain lending markets | Moderate yield, transparent risk parameters, vault-enforced allocation limits | Fintechs and exchanges with institutional user base | | **Amplified** | Lending, stablecoin AMMs, basis opportunities, CeFi venues via institutional custody integrations | Higher return target, higher operational and market risk | Platforms with higher risk tolerance and active strategy management | | **Ecosystem** | Chain-specific or protocol-specific native venues | Yield from ecosystem incentives + native sources | L1/L2 networks, DeFi protocols, RWA and stablecoin issuers | #### **Treasury management** Treasury management applies to any holder of stablecoin balances – whether a company managing its balance sheet or an individual managing personal holdings. In both cases, the vault serves the holder's own capital, not an end-user yield product. A Core Vault defines the full operating mandate: liquidity reserve ratios, approved strategies, allocation caps, asset and venue whitelists, and compliance perimeter. The vault manager – or an authorized agent – operates within these constraints. **Companies** Companies using stablecoin settlement – payment processors, marketplaces, fintechs, trading platforms – accumulate balances that need active management: maintain operating reserves, deploy idle capital into approved strategies, enforce risk limits, and satisfy compliance requirements across jurisdictions. **Users** Individuals holding stablecoin balances can manage them under the same vault architecture – a defined mandate with approved strategies, risk limits, and automated enforcement, operated directly or by an authorized agent. ### Vault architecture for stablecoin capital #### **Strategy allocation** The mandate defines the approved strategy set. The following are example strategy categories. Actual availability depends on the vault mandate, integrations, jurisdiction, and asset eligibility. ​ * ​**Tokenized T-bills and money-market funds** – BlackRock's BUIDL, Ondo's USDY, Superstate's USTB, Franklin Templeton's BENJI. Conservative base layer. Redemption timing varies by issuer (T+0 to T+2); the vault handles async settlement natively. * **​Lending markets** – Aave, Morpho, Compound, and other onchain lending protocols. The vault enforces which markets are approved, maximum allocation per market, and utilization thresholds for rebalancing. * ​**Short-duration yield** – liquidity pools, stablecoin AMMs, and other venues with intraday deployment and withdrawal. Serve as both yield generation and liquidity buffer. * **​CEX venues** – via Copper ClearLoop and Ceffu, capital routes to centralized opportunities under the same permission model as DeFi allocations. ​ A single vault can hold positions across all strategy types simultaneously. Multi-asset accounting and oracle-defined NAV produce a coherent portfolio view across assets with different pricing sources, settlement timings, and liquidity profiles. Adding a new approved venue is typically configuration rather than new contract code. #### **Risk enforcement and verifiers** Core Vaults enforce 60+ onchain permissions at the contract level: * Allocation caps per strategy, per asset, and per venue * ​Asset whitelists – the vault only holds approved assets * ​Venue whitelists – the vault only routes to approved targets * ​Oracle price checks before operations execute * ​Withdrawal queue controls for fair redemption ordering Verifiers are the onchain components that validate each action against the vault's permission set. The verifier model is composable – routine operations may require a single verifier, while high-impact operations can require multiple verifier approvals, time locks, or enhanced validation. Verifiers do not distinguish between human curators and autonomous agents. The same rules apply.​ Guardrails are configurable through structured governance but cannot be breached during execution. #### **Compliance** Compliance rules are applied at the vault level, so different products can enforce different eligibility and transfer requirements without changing the base vault architecture. Core Vaults treat mandates as configurable objects. ​Share-token eligibility – configurable hooks for KYC/KYB providers, transfer agents, accreditation registries, jurisdiction-based restrictions Asset eligibility – per-asset and per-issuer rules; regulated tokenized instruments with transfer restrictions are handled separately from permissionless positions Different products on the same platform enforce different compliance perimeters without separate codebases. ### Agent-operated stablecoin vaults As autonomous agents take on more asset management responsibilities, the infrastructure they operate in matters as much as the models behind them. Autonomous agents need execution environments with hard constraints that prevent unauthorized actions at the contract level. Core Vaults provide this through the verifier model – approved targets, functions, assets, allocation bands, NAV constraints, and time-locked operations are enforced before execution. Operations outside the mandate revert without requiring human oversight. Agents operating stablecoin vaults can rebalance, manage liquidity, and react to strategy conditions at frequencies impractical for human operators – while the vault ensures they never exceed their mandate.
### Related pages * [Vault Infrastructure for Fintech Earn Products](https://docs.mellow.finance/vault-infrastructure-for-fintech-earn-products) – architecture overview and full use case reference # Core Vaults Integration Guide Source: https://docs.mellow.finance/core-vaults-integration-guide Reference for integrating deposit and redemption flows with Mellow Core Vaults, including contract ABIs, supported networks, fees, errors, and events, with TypeScript examples. This guide covers the full lifecycle for integrating deposits and redemptions with **Mellow Core Vaults**. ## 1. Architecture Overview A **Mellow Core Vault** is a programmable, modular asset management contract. It serves as the central hub for capital management, risk control, and composable logic. **Depositors** provide capital; **Curators** manage that capital within guardrails set by the vault configuration. All deposit and redemption flows are time-buffered through an off-chain oracle — protecting depositors against flash-loan attacks and front-running by design. ### Core Components | Component | Role | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Vault** | Central contract. Orchestrates ACLModule (access control), ShareModule (queues & shares), VaultModule (subvault management), and BaseModule (reentrancy). | | **DepositQueue** | Accepts token deposits, stores them as timestamped checkpoints, and mints vault shares after oracle pricing. | | **RedeemQueue** | Accepts share redemptions, locks shares immediately, and releases assets after oracle pricing and liquidity settlement. | | **ShareManager** | ERC20-compatible contract managing share supply, whitelisting, global lockups, and compliance controls. | | **Oracle** | Trusted off-chain price reporter. Submits `handleReport()` with a price and timestamp. | | **Curator** | Manages capital allocation across subvaults; calls `handleBatches()` to settle redemption liquidity. | ### Deposit Lifecycle #### Async queue Time-buffered. Oracle prices the batch; Curator settles liquidity. Claim is a separate transaction after processing. ``` Step 1 — User calls deposit(assets, referral, merkleProof) └─► Request stored as a timestamped checkpoint in DepositQueue Step 2 — Handle Report submitted handleReport(priceD18, depositTimestamp) └─► Processes all requests older than the configured depositInterval └─► Shares are allocated lazily using a Fenwick tree (computed at claim time) Step 3 — User calls DepositQueue.claim(account) └─► Share amount computed, deposit fee deducted, shares transferred to user ``` #### Sync queue Shares issued or assets returned in the same transaction. No separate claim step. ``` Step 1 — User calls deposit(assets, referral, merkleProof) └─► Share amount computed, deposit fee deducted, shares transferred to user ``` ### Redemption Lifecycle ``` Step 1 — User calls RedeemQueue.redeem(shares) └─► Shares locked immediately from the user's wallet Step 2 — Handle Report submitted handleReport(priceD18, redeemTimestamp) └─► Prices all requests older than the configured redeemInterval Step 3 — Curator calls RedeemQueue.handleBatches(n) └─► Pulls required liquidity from vault/subvaults into RedeemQueue └─► RedeemRequestsHandled event emitted; isClaimable becomes true Step 4 — User calls RedeemQueue.claim(receiver, timestamps[]) └─► Underlying assets transferred to receiver ``` ## 2. Supported Networks | Chain ID | Network | | -------- | ----------------- | | 1 | Ethereum | | 8453 | Base | | 42161 | Arbitrum | | 17000 | Holesky (testnet) | | 560048 | Hoodi (testnet) | | 143 | Monad (testnet) | | 9745 | Plasma | | 999 | HyperEVM | | 31612 | Mezo | ## 3. Vault Discovery Fetch the list of all vaults from the Mellow REST API. No authentication is required. ``` GET https://api.mellow.finance/v1/vaults → VaultData[] ``` ```typescript theme={null} async function fetchVaults(): Promise { const response = await fetch('https://api.mellow.finance/v1/vaults'); if (!response.ok) { throw new Error(`Failed to fetch vaults: ${response.status} ${response.statusText}`) } return response.json() as Promise } ``` ## 4. TypeScript Interfaces & Constants ```typescript theme={null} import type { Address } from 'viem' // ── Token ───────────────────────────────────────────────────────────────────── interface Token { address: Address symbol: string decimals: number } // ── Queue ───────────────────────────────────────────────────────────────────── interface Queue { /** The queue contract address */ queue: Address /** The token this queue accepts (for deposits) or pays out (for redemptions) */ asset: Address /** When true, new submissions are rejected */ is_paused: boolean /** Async queues require a separate claim step after oracle processing */ type: 'async' | 'sync' } // ── VaultData ───────────────────────────────────────────────────────────────── interface VaultData { id: string chain_id: number address: Address symbol: string /** Decimals used for vault shares — use this when parsing redeem amounts */ decimals: number name: string base_token: Token deposit_tokens: Token[] withdraw_tokens: Token[] collector: Address deposit_queues: Queue[] redeem_queues: Queue[] } // ── RedeemRequest ───────────────────────────────────────────────────────────── interface RedeemRequest { /** uint32 unix timestamp identifying this request */ timestamp: bigint /** Shares submitted for this request */ shares: bigint /** True when the oracle has processed this batch and assets can be claimed */ isClaimable: boolean /** Assets available to claim (0 until isClaimable is true) */ assets: bigint } // ── Constants ───────────────────────────────────────────────────────────────── /** Sentinel address representing native ETH in the Mellow protocol */ const NATIVE_ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' as const /** Maximum value for Solidity uint224 — the deposit() assets parameter type */ const UINT224_MAX = (1n << 224n) - 1n function isNativeEth(address: string): boolean { return address.toLowerCase() === NATIVE_ETH_ADDRESS.toLowerCase() } ``` ## 5. ABIs Only the functions and events relevant to integrations are shown here. ### 5.1 Deposit Queue ABI ```typescript theme={null} const DEPOSIT_QUEUE_ABI = [ // ── View functions ────────────────────────────────────────────────────────── { type: 'function', name: 'asset', inputs: [], outputs: [{ name: '', type: 'address' }], stateMutability: 'view', }, { type: 'function', name: 'requestOf', inputs: [{ name: 'account', type: 'address' }], outputs: [ { name: 'timestamp', type: 'uint256' }, { name: 'assets', type: 'uint256' }, ], stateMutability: 'view', }, { type: 'function', name: 'claimableOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: 'shares', type: 'uint256' }], // returns claimable shares, not assets stateMutability: 'view', }, // ── Write functions ───────────────────────────────────────────────────────── { type: 'function', name: 'deposit', inputs: [ { name: 'assets', type: 'uint224' }, { name: 'referral', type: 'address' }, { name: 'merkleProof', type: 'bytes32[]' }, ], outputs: [], stateMutability: 'payable', }, { type: 'function', name: 'claim', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: '', type: 'bool' }], stateMutability: 'nonpayable', }, { type: 'function', name: 'cancelDepositRequest', inputs: [], outputs: [], stateMutability: 'nonpayable', }, // ── Events ────────────────────────────────────────────────────────────────── { type: 'event', name: 'DepositRequested', inputs: [ { name: 'account', type: 'address', indexed: true }, { name: 'referral', type: 'address', indexed: true }, { name: 'assets', type: 'uint224', indexed: false }, { name: 'timestamp', type: 'uint32', indexed: false }, ], }, { type: 'event', name: 'DepositRequestClaimed', inputs: [ { name: 'account', type: 'address', indexed: true }, { name: 'shares', type: 'uint256', indexed: false }, { name: 'timestamp', type: 'uint32', indexed: false }, ], }, { type: 'event', name: 'DepositRequestCanceled', inputs: [ { name: 'account', type: 'address', indexed: true }, { name: 'assets', type: 'uint256', indexed: false }, { name: 'timestamp', type: 'uint32', indexed: false }, ], }, // ── Errors ────────────────────────────────────────────────────────────────── { type: 'error', name: 'PendingRequestExists', inputs: [] }, { type: 'error', name: 'ClaimableRequestExists', inputs: [] }, { type: 'error', name: 'NoPendingRequest', inputs: [] }, { type: 'error', name: 'QueuePaused', inputs: [] }, { type: 'error', name: 'DepositNotAllowed', inputs: [] }, { type: 'error', name: 'ZeroValue', inputs: [] }, { type: 'error', name: 'InsufficientBalance', inputs: [ { name: 'balance', type: 'uint256' }, { name: 'needed', type: 'uint256' }, ], }, ] as const ``` ### 5.2 Redeem Queue ABI ```typescript theme={null} const REDEEM_QUEUE_ABI = [ // ── View functions ────────────────────────────────────────────────────────── { type: 'function', name: 'asset', inputs: [], outputs: [{ name: '', type: 'address' }], stateMutability: 'view', }, { type: 'function', name: 'requestsOf', inputs: [ { name: 'account', type: 'address' }, { name: 'offset', type: 'uint256' }, { name: 'limit', type: 'uint256' }, ], outputs: [ { name: 'requests', type: 'tuple[]', components: [ { name: 'timestamp', type: 'uint256' }, { name: 'shares', type: 'uint256' }, { name: 'isClaimable', type: 'bool' }, { name: 'assets', type: 'uint256' }, ], }, ], stateMutability: 'view', }, // ── Write functions ───────────────────────────────────────────────────────── { type: 'function', name: 'redeem', inputs: [{ name: 'shares', type: 'uint256' }], outputs: [], stateMutability: 'nonpayable', }, { type: 'function', name: 'claim', inputs: [ { name: 'receiver', type: 'address' }, { name: 'timestamps', type: 'uint32[]' }, ], outputs: [{ name: 'assets', type: 'uint256' }], stateMutability: 'nonpayable', }, // ── Events ────────────────────────────────────────────────────────────────── { type: 'event', name: 'RedeemRequested', inputs: [ { name: 'account', type: 'address', indexed: true }, { name: 'shares', type: 'uint256', indexed: false }, { name: 'timestamp', type: 'uint256', indexed: false }, ], }, { type: 'event', name: 'RedeemRequestClaimed', inputs: [ { name: 'account', type: 'address', indexed: true }, { name: 'receiver', type: 'address', indexed: true }, { name: 'assets', type: 'uint256', indexed: false }, { name: 'timestamp', type: 'uint32', indexed: false }, ], }, // ── Errors ────────────────────────────────────────────────────────────────── { type: 'error', name: 'QueuePaused', inputs: [] }, { type: 'error', name: 'ZeroValue', inputs: [] }, { type: 'error', name: 'InsufficientBalance', inputs: [ { name: 'balance', type: 'uint256' }, { name: 'needed', type: 'uint256' }, ], }, ] as const ``` ### 5.3 ERC20 ABI (subset — for approval) ```typescript theme={null} const ERC20_ABI = [ { type: 'function', name: 'allowance', inputs: [ { name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }, ], outputs: [{ name: '', type: 'uint256' }], stateMutability: 'view', }, { type: 'function', name: 'approve', inputs: [ { name: 'spender', type: 'address' }, { name: 'amount', type: 'uint256' }, ], outputs: [{ name: '', type: 'bool' }], stateMutability: 'nonpayable', }, { type: 'function', name: 'balanceOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: '', type: 'uint256' }], stateMutability: 'view', }, ] as const ``` ### 5.4 Vault ABI (subset — for share balance lookup) ```typescript theme={null} const VAULT_ABI = [ { type: 'function', name: 'shareManager', inputs: [], outputs: [{ name: '', type: 'address' }], stateMutability: 'view', }, ] as const ``` The `shareManager` address is itself an ERC20-compatible contract. Use `ERC20_ABI` with `balanceOf`, or use the richer `SHARE_MANAGER_ABI` below for more precise balance reads. ### 5.5 ShareManager ABI (subset) ```typescript theme={null} const SHARE_MANAGER_ABI = [ // ── Balance reads (prefer these over raw balanceOf) ───────────────────────── { type: 'function', name: 'sharesOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: '', type: 'uint256' }], stateMutability: 'view', }, { type: 'function', name: 'activeSharesOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: '', type: 'uint256' }], stateMutability: 'view', // Returns only the shares that are not locked in a redeem queue. // Use this to check the redeemable balance. }, { type: 'function', name: 'claimableSharesOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: '', type: 'uint256' }], stateMutability: 'view', // Shares processed by the oracle and awaiting DepositQueue.claim(). }, // ── Whitelist / permissions ────────────────────────────────────────────────── { type: 'function', name: 'flags', inputs: [], outputs: [ { name: '', type: 'tuple', components: [ { name: 'hasMintPause', type: 'bool' }, { name: 'hasBurnPause', type: 'bool' }, { name: 'hasTransferPause', type: 'bool' }, { name: 'hasWhitelist', type: 'bool' }, // deposit whitelist active { name: 'hasTransferWhitelist', type: 'bool' }, { name: 'globalLockup', type: 'uint32' }, // seconds all shares are locked after mint ], }, ], stateMutability: 'view', }, { type: 'function', name: 'isDepositorWhitelisted', inputs: [ { name: 'account', type: 'address' }, { name: 'merkleProof', type: 'bytes32[]' }, ], outputs: [{ name: '', type: 'bool' }], stateMutability: 'view', }, ] as const interface ShareManagerFlags { hasMintPause: boolean hasBurnPause: boolean hasTransferPause: boolean hasWhitelist: boolean // when true, deposits require a valid Merkle proof hasTransferWhitelist: boolean globalLockup: number // seconds before newly minted shares become transferable } ``` ## 6. Deposit ### Overview Depositing submits tokens to a `DepositQueue` contract. For **async** queues the shares are not immediately available — an oracle processes the batch and sets a price, after which the user calls `claim` to receive their shares. ``` Step 1 — approve token spend (ERC20 only) Step 2 — call deposit() ↓ [oracle processes batch] ↓ Step 3 — call claim() to receive vault shares ``` ### Steps Pick a deposit queue from `vault.deposit_queues`. You can match by `queue.asset` address and `queue.type` deposit type (`"sync"` | `"async"`). Check `queue.is_paused === false`. Throw early if paused. Read `shareManager.flags()`. If `flags.hasWhitelist === true`, call `shareManager.isDepositorWhitelisted(userAddress, merkleProof)`. If it returns `false`, the deposit will revert with `DepositNotAllowed`. For public vaults (`hasWhitelist === false`), pass `[]` as the proof. Find the matching `Token` in `vault.deposit_tokens` for `queue.asset`. Parse the human-readable amount with `parseUnits(amount, token.decimals)`. Validate that: * `parsedAmount > 0n` * `parsedAmount <= UINT224_MAX` Only one pending deposit request per user is allowed per queue. For async queues, call `requestOf(userAddress)` and check `timestamp === 0n` before depositing. If the asset is native ETH (`queue.asset === NATIVE_ETH_ADDRESS`), check the user's ETH balance, then call `deposit()` with `value = parsedAmount`. If the asset is an ERC20: * Read `allowance(userAddress, queueAddress)`. * If `currentAllowance > 0n`, send `approve(queueAddress, 0n)` first. This is required for tokens like USDT that revert if you set a non-zero allowance on top of an existing one. * Send `approve(queueAddress, parsedAmount)`. * Then call `deposit(parsedAmount, zeroAddress, merkleProof)` with `value = 0n`. For **async** queues: do not expect shares immediately. Poll `claimableOf` or listen for `DepositRequestClaimed` events, then call `claim`. ### Code Example ```typescript theme={null} import { createPublicClient, createWalletClient, http, parseUnits, zeroAddress, } from 'viem' import { mainnet } from 'viem/chains' import { privateKeyToAccount } from 'viem/accounts' const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY') const publicClient = createPublicClient({ chain: mainnet, transport: http() }) const walletClient = createWalletClient({ account, chain: mainnet, transport: http() }) async function deposit( vault: VaultData, queueAddress: string, humanAmount: string, // Pass [] for public vaults. For whitelisted vaults, obtain proof from the Mellow API. merkleProof: `0x${string}`[] = [], ) { const userAddress = account.address // 1. Find the queue and validate it is open const queue = vault.deposit_queues.find(q => q.queue.toLowerCase() === queueAddress.toLowerCase()) if (!queue) throw new Error('Queue not found') if (queue.is_paused) throw new Error('Queue is paused') // 2. Whitelist check — read shareManager.flags() to see if this vault requires a proof const shareManagerAddress = await publicClient.readContract({ address: vault.address, abi: VAULT_ABI, functionName: 'shareManager', }) const flags = await publicClient.readContract({ address: shareManagerAddress, abi: SHARE_MANAGER_ABI, functionName: 'flags', }) as ShareManagerFlags if (flags.hasWhitelist) { const allowed = await publicClient.readContract({ address: shareManagerAddress, abi: SHARE_MANAGER_ABI, functionName: 'isDepositorWhitelisted', args: [userAddress, merkleProof], }) if (!allowed) throw new Error('Address is not whitelisted for this vault') } // 3. Resolve token metadata const token = vault.deposit_tokens.find(t => t.address.toLowerCase() === queue.asset.toLowerCase()) if (!token) throw new Error('Token not found') // 4. Parse and validate amount const parsedAmount = parseUnits(humanAmount, token.decimals) if (parsedAmount <= 0n) throw new Error('Amount must be greater than zero') if (parsedAmount > UINT224_MAX) throw new Error('Amount exceeds maximum (uint224)') const native = isNativeEth(queue.asset) if (native) { // ── Native ETH path ──────────────────────────────────────────────────────── const balance = await publicClient.getBalance({ address: userAddress }) if (parsedAmount > balance) throw new Error('Insufficient ETH balance') const hash = await walletClient.writeContract({ address: queue.queue as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'deposit', args: [parsedAmount, zeroAddress, merkleProof], value: parsedAmount, }) return publicClient.waitForTransactionReceipt({ hash }) } else { // ── ERC20 path ───────────────────────────────────────────────────────────── // Check balance const balance = await publicClient.readContract({ address: queue.asset as `0x${string}`, abi: ERC20_ABI, functionName: 'balanceOf', args: [userAddress], }) if (parsedAmount > balance) throw new Error('Insufficient token balance') // Check for pending request — only one pending request per user per queue is allowed if (queue.type === 'async') { const [timestamp] = await publicClient.readContract({ address: queue.queue as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'requestOf', args: [userAddress], }) if (timestamp > 0n) { throw new Error('Pending deposit request already exists — cancel or claim it first') } } // Read allowance BEFORE sending any transactions const currentAllowance = await publicClient.readContract({ address: queue.asset as `0x${string}`, abi: ERC20_ABI, functionName: 'allowance', args: [userAddress, queue.queue as `0x${string}`], }) // Reset allowance if needed (required for tokens like USDT) if (currentAllowance > 0n) { const resetHash = await walletClient.writeContract({ address: queue.asset as `0x${string}`, abi: ERC20_ABI, functionName: 'approve', args: [queue.queue as `0x${string}`, 0n], }) await publicClient.waitForTransactionReceipt({ hash: resetHash }) } // Approve exact amount const approveHash = await walletClient.writeContract({ address: queue.asset as `0x${string}`, abi: ERC20_ABI, functionName: 'approve', args: [queue.queue as `0x${string}`, parsedAmount], }) await publicClient.waitForTransactionReceipt({ hash: approveHash }) // Deposit const depositHash = await walletClient.writeContract({ address: queue.queue as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'deposit', args: [parsedAmount, zeroAddress, merkleProof], value: 0n, }) return publicClient.waitForTransactionReceipt({ hash: depositHash }) } } ``` **Referral address:** Pass `zeroAddress` (`0x0000000000000000000000000000000000000000`) unless you have been issued a referral address by the Mellow team. **Merkle proof & whitelisting:** Pass `[]` for public vaults. When `shareManager.flags().hasWhitelist === true`, the vault is permissioned — `deposit()` will revert with `DepositNotAllowed` if the proof is invalid or empty. Contact the Mellow team or query the Mellow API to obtain a valid proof for whitelisted addresses. ## 7. Cancel Deposit ### Overview A pending async deposit request can be cancelled before the oracle processes it. Cancelling returns the deposited tokens to the user. You **cannot** cancel once the request is claimable — call `claim` instead. ### Steps Call `requestOf(userAddress)` on the deposit queue. Check `timestamp > 0n` — if zero, there is no pending request. Call `claimableOf(userAddress)`. If `> 0n`, the oracle has already processed the request — you must claim it, not cancel it. Call `cancelDepositRequest()`. This function takes no arguments — it cancels the caller's own request. ### Code Example ```typescript theme={null} async function cancelDeposit(queueAddress: string) { const userAddress = account.address // 1. Check for a pending request const [timestamp] = await publicClient.readContract({ address: queueAddress as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'requestOf', args: [userAddress], }) if (timestamp === 0n) throw new Error('No pending deposit request to cancel') // 2. Check whether it is already claimable const claimable = await publicClient.readContract({ address: queueAddress as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'claimableOf', args: [userAddress], }) if (claimable > 0n) { throw new Error('Request already processed — call claim() instead of cancel') } // 3. Cancel const hash = await walletClient.writeContract({ address: queueAddress as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'cancelDepositRequest', args: [], }) return publicClient.waitForTransactionReceipt({ hash }) } ``` ## 8. Claim Deposit ### Overview After the oracle processes an async deposit request, vault shares are held in the queue contract ready for collection. Call `claim` to transfer them to the user. ### Steps Call `claimableOf(userAddress)`. If `> 0n`, shares are ready to claim. If `claimableOf` returns `0n`, call `requestOf(userAddress)`. If `timestamp > 0n`, the oracle has not yet processed the request — wait and retry later. Call `claim(userAddress)`. Returns `true` when shares are successfully transferred. ### Code Example ```typescript theme={null} async function claimDeposit(queueAddress: string) { const userAddress = account.address // 1. Check if there is anything to claim const claimable = await publicClient.readContract({ address: queueAddress as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'claimableOf', args: [userAddress], }) if (claimable === 0n) { // Check if still pending const [timestamp] = await publicClient.readContract({ address: queueAddress as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'requestOf', args: [userAddress], }) if (timestamp > 0n) { throw new Error('Deposit is pending oracle processing — try again later') } throw new Error('No claimable deposit found') } // 2. Claim shares const hash = await walletClient.writeContract({ address: queueAddress as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, functionName: 'claim', args: [userAddress], }) return publicClient.waitForTransactionReceipt({ hash }) } ``` ## 9. Redeem ### Overview Redemption burns vault shares and, after oracle processing, returns the underlying asset to the user. Redeem queues are **always async** — there is always a separate claim step. ``` Step 1 — call redeem(shares) ↓ [oracle processes batch] ↓ Step 2 — call claim(receiver, timestamps) ``` ### Steps Pick a redeem queue from `vault.redeem_queues`. Check `queue.is_paused === false`. * Call `vault.shareManager()` to get the share manager address. * Call `shareManager.activeSharesOf(userAddress)` — this returns only shares that are **not** currently locked in a pending redeem request. Use `sharesOf` if you want the total including locked shares. Parse the share amount using **vault decimals** (`vault.decimals`), **not** the token's decimals. This is a common mistake — the share token uses the vault's decimal precision. Validate `parsedShares > 0n` and `parsedShares <= activeShareBalance`. Call `redeem(parsedShares)`. No ETH value, no ERC20 approval — the vault contract locks shares directly from the caller. Wait for oracle processing and `handleBatches()`, then call `claimRedeem` (see Section 10). **Multiple requests are allowed.** Unlike deposits, a user can have many concurrent redemption requests. Each `redeem()` call creates a new request with its own timestamp. **Requests cannot be cancelled.** Once submitted, a redemption request is permanent. This is intentional — cancellable redemptions would allow yield-griefing (requesting redemption to force liquidity pulls from external protocols, then withdrawing the request). The locked shares remain locked until claimed. **Settlement flow:** After `redeem()`, two off-chain steps must happen before you can claim: (1) the oracle calls `handleReport()` to price the batch, then (2) the Curator calls `handleBatches()` on the RedeemQueue to pull the required liquidity from subvaults. Listen for the `RedeemRequestsHandled` event — it fires when `handleBatches()` settles one or more batches and requests become claimable. The timing depends on vault configuration (`redeemInterval`) and curator activity, typically ranging from minutes to hours. ### Code Example ```typescript theme={null} async function redeem(vault: VaultData, queueAddress: string, humanShares: string) { const userAddress = account.address // 1. Find the queue const queue = vault.redeem_queues.find(q => q.queue.toLowerCase() === queueAddress.toLowerCase()) if (!queue) throw new Error('Redeem queue not found') if (queue.is_paused) throw new Error('Redeem queue is paused') // 2. Get redeemable share balance via vault's shareManager // activeSharesOf excludes shares already locked in pending redeem requests const shareManagerAddress = await publicClient.readContract({ address: vault.address, abi: VAULT_ABI, functionName: 'shareManager', }) const activeShareBalance = await publicClient.readContract({ address: shareManagerAddress, abi: SHARE_MANAGER_ABI, functionName: 'activeSharesOf', args: [userAddress], }) // 3. Parse share amount using vault decimals (NOT the output token's decimals) const parsedShares = parseUnits(humanShares, vault.decimals) if (parsedShares <= 0n) throw new Error('Redeem amount must be greater than zero') if (parsedShares > activeShareBalance) { throw new Error(`Insufficient redeemable share balance: have ${activeShareBalance}, need ${parsedShares}`) } // 4. Submit redeem — no approval required, vault locks shares from caller const hash = await walletClient.writeContract({ address: queue.queue as `0x${string}`, abi: REDEEM_QUEUE_ABI, functionName: 'redeem', args: [parsedShares], }) return publicClient.waitForTransactionReceipt({ hash }) } ``` **No approval needed:** Unlike deposits, redemptions do not require an ERC20 `approve`. The vault contract has the authority to lock and burn shares on behalf of the caller. ## 10. Claim Redeem ### Overview A user may accumulate multiple redemption requests over time. The `requestsOf` function returns all requests paginated. Once the oracle marks a request `isClaimable`, the user can batch-claim them by passing the corresponding timestamps to `claim`. ### Steps Paginate `requestsOf(userAddress, offset, 100)`: * Start with `offset = 0`. * Increment by `100` each iteration. * Stop when a page returns fewer than `100` items. Filter to requests where `isClaimable === true`. Extract the `timestamp` field from each claimable request. Cast to `number` — timestamps are `uint32` values, safely representable as JavaScript numbers until year 2106. Call `claim(userAddress, timestamps)`. Returns the total `assets` transferred. **`claim()` is idempotent.** Non-claimable or already-claimed timestamps are silently skipped — the contract does not revert. You may safely pass all known timestamps and let the contract filter them. ### Code Example ```typescript theme={null} async function claimRedeem(vault: VaultData, queueAddress: string) { const userAddress = account.address const PAGE_SIZE = 100 // 1. Collect all requests via pagination const allRequests: RedeemRequest[] = [] let offset = 0 while (true) { const page = await publicClient.readContract({ address: queueAddress as `0x${string}`, abi: REDEEM_QUEUE_ABI, functionName: 'requestsOf', args: [userAddress, BigInt(offset), BigInt(PAGE_SIZE)], }) as RedeemRequest[] allRequests.push(...page) if (page.length < PAGE_SIZE) break offset += PAGE_SIZE } if (allRequests.length === 0) { throw new Error('No redemption requests found') } // 2. Filter to claimable requests const claimable = allRequests.filter(r => r.isClaimable) if (claimable.length === 0) { throw new Error( `${allRequests.length} redemption request(s) are pending oracle processing — try again later`, ) } // 3. Extract timestamps as number[] (uint32 — safe as JS number) const timestamps = claimable.map(r => Number(r.timestamp)) console.log(`Claiming ${claimable.length} of ${allRequests.length} redemption request(s)...`) // 4. Batch claim const hash = await walletClient.writeContract({ address: queueAddress as `0x${string}`, abi: REDEEM_QUEUE_ABI, functionName: 'claim', args: [userAddress, timestamps], }) const receipt = await publicClient.waitForTransactionReceipt({ hash }) const pending = allRequests.length - claimable.length if (pending > 0) { console.log(`${pending} request(s) are still pending and will need to be claimed later.`) } return receipt } ``` ## 11. Fees Fees in Mellow Core Vaults are paid in **vault shares**, not in underlying assets. The `FeeManager` contract calculates and deducts fees automatically during oracle report handling — integrators do not call fee functions directly. | Fee Type | When Applied | Effect on Integrator | | ------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------ | | **Deposit fee** | At `DepositQueue.claim()` time | User receives fewer shares than the raw price implies. Calculated as `shares * depositFeeD6 / 1e6`. | | **Redeem fee** | At `RedeemQueue.redeem()` time | A portion of shares is deducted before the redemption amount is finalized. | | **Performance fee** | Oracle report trigger | Accrued to the vault as yield is generated; does not directly affect per-request calculations. | | **Protocol fee** | Continuous accrual | Time-based, deducted from share supply; transparent to depositors but reduces NAV per share over time. | > Fees are vault-specific and set by Curators. Check the vault configuration or the Mellow API for exact fee parameters before displaying estimated returns to users. ## 12. Error Reference | Error | Contract | When it occurs | What to do | | ------------------------ | ------------ | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | `PendingRequestExists` | DepositQueue | `deposit()` called when a pending request already exists | Call `cancelDepositRequest()` first, or wait for oracle and then `claim()` | | `ClaimableRequestExists` | DepositQueue | `cancelDepositRequest()` called when request is already claimable | The oracle processed the request — call `claim()` instead | | `NoPendingRequest` | DepositQueue | `cancelDepositRequest()` called with no pending request | Nothing to cancel | | `QueuePaused` | Both | Queue is temporarily suspended | Check `queue.is_paused` before submitting; wait for it to re-open | | `DepositNotAllowed` | DepositQueue | Deposit rejected — queue paused, or vault has a whitelist and address is not included | Check `flags.hasWhitelist`; if true, obtain a valid Merkle proof via the Mellow API | | `ZeroValue` | RedeemQueue | `redeem()` called with `shares = 0` | Validate amount > 0 before calling | | `InsufficientBalance` | Both | Token or share balance too low | Validate balance on-chain before submitting | | `Forbidden` | Both | Caller does not have the required role for the called function | This is a contract-operator error; user-facing code should not hit this | ## 13. Events Reference Listen for these events to drive UI state or index on-chain activity. | Event | Contract | Emitted when | | ------------------------------------------------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------- | | `DepositRequested(account, referral, assets, timestamp)` | DepositQueue | `deposit()` succeeds | | `DepositRequestClaimed(account, shares, timestamp)` | DepositQueue | `claim()` succeeds — user received vault shares | | `DepositRequestCanceled(account, assets, timestamp)` | DepositQueue | `cancelDepositRequest()` succeeds — tokens returned | | `RedeemRequested(account, shares, timestamp)` | RedeemQueue | `redeem()` succeeds | | `RedeemRequestsHandled(counter, demand)` | RedeemQueue | Curator called `handleBatches()` and settled one or more batches — **listen for this to know when claims become available** | | `RedeemRequestClaimed(account, receiver, assets, timestamp)` | RedeemQueue | `claim()` succeeds — user received underlying assets | ### Example: watching for claim events with viem ```typescript theme={null} const unwatch = publicClient.watchContractEvent({ address: depositQueueAddress as `0x${string}`, abi: DEPOSIT_QUEUE_ABI, eventName: 'DepositRequestClaimed', args: { account: userAddress }, onLogs: (logs) => { for (const log of logs) { console.log(`Shares received: ${log.args.shares}, timestamp: ${log.args.timestamp}`) } }, }) // Stop watching when done unwatch() ``` ## 14. Integration with Para Para is a wallet and authentication suite for crypto and fintech developers. It provides infrastructure for embedded wallets, external wallet connections, authentication, and transaction flows inside third-party applications. Applications built with Para can integrate Mellow Core Vault interactions directly into their own interface. This allows teams using Para for the wallet layer to add direct Mellow Vaults access without building separate wallet or vault interaction infrastructure from scratch. Para maintains a dedicated walkthrough for integrating Mellow Core Vaults with Para-powered wallets. The walkthrough covers the Para-side implementation for Core Vault interactions. Get started with the [Mellow Core Vaults walkthrough in Para Docs](https://docs.getpara.com/v3/walkthroughs/mellow-core-vaults). # Factory Source: https://docs.mellow.finance/core-vaults/architecture/factory #### Overview The `Factory` contract is a generalized deployment mechanism for creating upgradeable proxy instances of pre-approved implementations. It tracks multiple implementation versions, proposal workflows, blacklisting for security, and ownership-based access control. It conforms to the `IFactory` interface and supports deploying any `IFactoryEntity`-compliant contracts via `TransparentUpgradeableProxy`, using `initialize()` for configuration. #### Key Capabilities * **Versioned Deployment**: Track multiple logic contract versions, each deployable by index. * **Proposal System**: Allow anyone to propose implementations, with owner approval required. * **Blacklist Mechanism**: Prevent deployment of insecure or deprecated versions. * **Deterministic Deployments**: Uses `create2` salt to ensure predictable addresses. * **Entity Tracking**: Keeps a registry of all deployed entities. #### Storage Structure The contract uses a deterministic storage layout via: ```solidity theme={null} bytes32 _factoryStorageSlot = SlotLibrary.getSlot("Factory", name_, version_); ``` Storage fields (in `FactoryStorage`) include: | Field | Description | | ----------------- | ------------------------------------- | | `entities` | Set of all deployed proxy instances | | `implementations` | Approved logic contract addresses | | `proposals` | Pending implementation proposals | | `isBlacklisted` | Mapping from version → is blacklisted | #### Initialization ```solidity theme={null} function initialize(bytes calldata data) external initializer ``` * Accepts encoded owner address * Sets initial admin and emits `Initialized` #### Entity Deployment ```solidity theme={null} function create(uint256 version, address owner, bytes calldata initParams) external returns (address instance) ``` Deploys a new `TransparentUpgradeableProxy`: * Uses implementation from `implementations.at(version)` * Rejects if version is out-of-bounds or blacklisted * Uses `salt = keccak256(version, owner, initParams, currentEntityCount)` for deterministic deployment * Calls `initialize(initParams)` on the new proxy Emits: ```solidity theme={null} event Created(address instance, uint256 version, address owner, bytes initParams); ``` #### Implementation Management #### Propose New Implementation ```solidity theme={null} function proposeImplementation(address implementation) external ``` * Fails if already in `implementations` or `proposals` * Adds to `proposals` * Emits: `ProposeImplementation(implementation)` * Permissionless function #### Accept Proposed Implementation ```solidity theme={null} function acceptProposedImplementation(address implementation) external onlyOwner ``` * Only callable by owner * Fails if not proposed * Moves from `proposals` → `implementations` * Emits: `AcceptProposedImplementation(implementation)` #### Blacklisting ```solidity theme={null} function setBlacklistStatus(uint256 version, bool flag) external onlyOwner ``` * Blocks deployments using specific version * Enforces that version index exists * Emits: `SetBlacklistStatus(version, flag)` #### View Functions | Function | Returns | | ------------------------- | -------------------------------------------- | | `entities()` | Total number of deployed entities | | `entityAt(index)` | Deployed entity at index | | `isEntity(address)` | Checks if address is a deployed entity | | `implementations()` | Total implementation count | | `implementationAt(index)` | Implementation at index | | `proposals()` | Total proposals pending approval | | `proposalAt(index)` | Proposal at index | | `isBlacklisted(version)` | Whether a version is blocked from deployment | #### Access Control * Uses `OwnableUpgradeable` * Only owner can accept implementations and blacklist versions #### Security Considerations * **Immutable logic whitelist**: Only approved contracts can be deployed * **Blacklisting**: Emergency response for vulnerabilities * **Replay protection**: Deployment salt ensures unique addresses * **Decentralized proposals**: Anyone can propose implementations, but only owner can accept # BasicRedeemHook Source: https://docs.mellow.finance/core-vaults/architecture/hooks/basicredeemhook ### Overview `BasicRedeemHook` is a minimal hook implementation for `IHook`, designed to dynamically fetch liquidity from subvaults during redemption processing in a `VaultModule`. It ensures that enough assets are available for user redemptions by pulling liquidity from a set of registered subvaults. This hook is typically invoked by a vault’s redemption queue or during `redeem()` operations when assets must be made liquid. ### Purpose * Ensures **sufficient liquidity** in the vault to fulfill asset redemptions. * Minimizes idle capital by **pulling only when needed**. * Supports **liquidity routing** across subvaults. ### Key Functions ### `callHook(address asset, uint256 assets)` Attempts to make `assets` of `asset` liquid within the main vault by pulling from subvaults if needed. **Execution Flow:** 1. Checks how much of the `asset` the vault already holds. 2. If balance is sufficient → no-op. 3. Otherwise: * Iterates over subvaults (via `subvaultAt(i)`) * For each subvault: * Pulls **only the required portion** via `hookPullAssets()` * Stops when total required assets have been pulled **Guarantees:** * Only pulls the **exact missing amount**, no over-pulling * Efficient: stops once liquidity need is satisfied * Skips subvaults with `0` balance ### `getLiquidAssets(address asset) → uint256` Returns the **total liquid amount of a given asset** available across the vault and all its subvaults. * Reads balances: * `vault.balanceOf(asset)` * `subvault[i].balanceOf(asset)` for each subvault * Aggregates and returns sum ### Contract Assumptions * The `vault` invoking this hook implements `IVaultModule` and supports: * `subvaults()` → total number of subvaults * `subvaultAt(index)` → address of a given subvault * `hookPullAssets(subvault, asset, amount)` → callable method to move funds ### Security Considerations * Hook only pulls assets using vault-controlled `hookPullAssets()`, ensuring controlled asset flow. * Assumes vault validates which hook is active — no permissioning within the hook itself. # Hooks Source: https://docs.mellow.finance/core-vaults/architecture/hooks/index In this directory, you will find a detailed per-contract overview of the "Hooks" contract category, including the following hooks: [BasicRedeemHook](/core-vaults/architecture/hooks/basicredeemhook) [LidoDepositHook](/core-vaults/architecture/hooks/lidodeposithook) [RedirectingDepositHook](/core-vaults/architecture/hooks/redirectingdeposithook) # LidoDepositHook Source: https://docs.mellow.finance/core-vaults/architecture/hooks/lidodeposithook ### Overview `LidoDepositHook` is an implementation of the `IHook` interface that acts as a **conversion adapter** for incoming deposits. It standardizes various ETH-like assets into **`wstETH`** for use in downstream vault logic. The hook supports **ETH**, **WETH**, and **stETH** as input formats and ensures conversion to `wstETH` before optionally forwarding execution to a downstream `nextHook`. ### Primary Purpose * Converts **ETH**, **WETH**, or **stETH** into **`wstETH`** on deposit. * Ensures compatibility with protocols that expect `wstETH`. * Provides **composable hooks** by chaining into a downstream `IHook` (`nextHook`). ### Constructor Parameters | Parameter | Type | Description | | ----------- | ------- | -------------------------------------------------------------------- | | `wsteth_` | address | Address of the `wstETH` token contract | | `weth_` | address | Address of the `WETH` token contract | | `nextHook_` | address | Address of the optional downstream hook to forward to after wrapping | ### Key Function ### `callHook(address asset, uint256 assets)` Handles conversion of an input asset into `wstETH` and optionally delegates the call to a downstream hook. **Supported Input Types:** 1. `wstETH` — forwarded directly 2. `stETH` — wrapped into `wstETH` via `IWSTETH(wsteth).wrap()` 3. `WETH` — unwrapped into ETH via `IWETH(weth).withdraw()`, then deposited into `wstETH` 4. `ETH` (i.e. `address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)`) — directly deposited into `wstETH` **Execution Steps:** * If `asset == wstETH`: do nothing * If `asset == stETH`: * Approves `wstETH` to pull `stETH` * Calls `wrap()` on `wstETH` to convert to `wstETH` * If `asset == WETH`: * Unwraps to ETH using `withdraw()` * Sends ETH to `wstETH` contract to mint `wstETH` * If `asset == ETH`: * Sends ETH directly to `wstETH` * After conversion: * Calculates how much new `wstETH` was received * If `nextHook` is configured, delegates `callHook(wstETH, amount)` to it ### Errors * `UnsupportedAsset(address asset)` — Thrown if the provided asset is neither `wstETH`, `stETH`, `WETH`, nor `ETH` ### Assumptions: It is assumed this hook will not trigger `STAKE_LIMIT` or other limit-related errors in the Lido contracts. If such errors do occur, the vault admin can reconfigure the system to bypass the automated staking hook. In this case, `RedirectingDepositHook` can be assigned to the relevant queues, delegating staking responsibilities to the vault curator via manual liquidity management. # RedirectingDepositHook Source: https://docs.mellow.finance/core-vaults/architecture/hooks/redirectingdeposithook ### Overview `RedirectingDepositHook` is a deposit-time liquidity allocation hook implementing the `IHook` interface. It is designed to **redirect newly deposited assets** from a vault into its underlying **subvaults**, based on per-subvault risk and capacity constraints defined by a `RiskManager`. This hook helps distribute liquidity optimally during deposit flows. ### Purpose * Automatically **forwards newly deposited assets** from the main vault into eligible subvaults. * Delegates decision-making to the vault’s configured `RiskManager`, which determines per-subvault deposit limits. * Ensures that **subvaults do not exceed their capacity constraints**. ### Key Function ### `callHook(address asset, uint256 assets)` Distributes a given amount of `asset` across available subvaults based on their individual deposit capacity. **Execution Logic:** 1. Retrieves: * Active `vault` context via `IVaultModule(address(this))` * Configured `RiskManager` via `vault.riskManager()` * Number of subvaults via `vault.subvaults()` 2. Iterates over each subvault: * Fetches max allowed deposit via `riskManager.maxDeposit(subvault, asset)` * If allowed amount is zero → skip * Otherwise: * Pushes `min(assets, allowed)` via `vault.hookPushAssets()` * Decrements `assets` accordingly * Stops early if full amount has been distributed ### Components and Assumptions * **Vault:** Must implement `IVaultModule`: * `subvaults()` → number of subvaults * `subvaultAt(index)` → returns subvault address * `hookPushAssets(subvault, asset, amount)` → transfers tokens to subvault * `riskManager()` → returns associated `IRiskManager` * **Risk Manager:** Must implement: * `maxDeposit(subvault, asset)` → returns max allowed deposit into that subvault # Architecture Source: https://docs.mellow.finance/core-vaults/architecture/index Full technical architecture: Deposit Queue, Redeem Queue, Signature Queues, Vault modules, Subvaults, Verifiers, Oracle, Share Manager, Fee Manager, Risk Manager, Access Control, supported protocols ## Mellow Core Vaults ### Abstract The DeFi market continues to expand with an increasing number of yield opportunities and protocols, each with its own level of decentralization, permissionlessness, and risk. Navigating this landscape is becoming challenging – even for advanced users. Meanwhile, the latest wave of adoption is bringing thousands of new participants from traditional markets into crypto. These users aren’t looking for raw protocols – they expect structured, accessible, and compliant financial products. To meet this demand, the onchain financial stack must evolve. It needs to be productized, abstracted and made composable. Mellow’s response is our **Core Vaults** – infrastructure purpose-built for curators and asset managers to design, deploy, and scale structured products onchain. ### Vaults Architecture Core Vaults are built around a modular architecture that orchestrates interactions across both DeFi protocols and centralized exchanges. It enables trustless execution of complex, institutional-grade strategies while maintaining full composability and transparency. No matter how diverse or sophisticated the underlying strategies become, the vault framework remains stable and predictable – providing a unified, programmable setup for managing assets, risks, and logic. The result is infrastructure that supports scalable, curated access to onchain yield – secure, standardized, and ready for real users. #### Core Vaults Features & Functionality * Deposits & Withdrawals for the Liquidity Providers * Valuation Oracle * Strategy performance analytics for Liquidity Providers * Fee management * Vault management tools & Curation UI * Reward distribution engine * Receipt token distribution infrastructure * Smart contract safeguards * Granular role-based control * Access to external apps and protocols #### Vault system architecture overview Vault system architecture overview #### #### 📥 Deposit Queue Manages user deposits through a time-buffered queuing system. This delay helps prevent front-running and price manipulation by ensuring deposits are not executed based on stale or externally influenced oracle price data. The deposit queue is also responsible for issuing receipt tokens. * **Technical Details\`** **Deposit flow:** * Step 1: **User Deposits** * A user submits a deposit request via `deposit(assets, referral, merkleProof)`. * Deposits are validated via optional Merkle whitelist logic (using `merkleProof`) or onchain mapping (if `hasWhitelist` flag is set). * If a previous request exists, it must be claimed or canceled before creating a new one. * The deposited amount and timestamp is stored in the `DepositQueue` contract * Step 2: **Oracle Report** * Oracle report is propagated via the `handleReport(priceD18, timestamp)` method. * The queue validates the report: * It must be called by the `Vault`. * Provided `timestamp` must be in the past (usually `request.timestamp - depositInterval`). * `priceD18` must be non-zero. * The queue handles deposit requests that are pending for at least `depositInterval` seconds (the interval is specified in the oracle’s security parameters). * The contract stores a `(timestamp, reducedByDepositFeePriceD18)` pair in the `prices` array. This value is used to convert accumulated assets into shares based on the user’s request timestamp. The `reducedByDepositFeePriceD18` is derived by applying the deposit fee to the actual reported `priceD18`. * The corresponding shares are allocated but not yet minted. * Emits the `ReportHandled` event. * Step 3: **User Claims** * A user calls `claim(account)` (or `claimShares(account)` in the `Vault`) to mint and receive previously allocated shares. * The number of shares is computed as: ```solidity theme={null} uint256 shares = (request.assets * reducedByDepositFeePriceD18) / 1e18; ``` * Shares are minted to the user via `mintAllocatedShares`. #### Assumptions & Properties 1. Single Active Request Each user may have at most one unprocessed deposit request. New deposits are blocked until the previous request is claimed. If the pending request is already claimable, the claim will be automatically processed during the next deposit. 2. Delayed Execution Deposit processing requires an Oracle report submitted after a configured `depositInterval`. 3. Lazy Claiming Deposits are converted into shares during oracle processing, but users must call the claim function (in `ShareModule`, `ShareManager` or in each `DepositQueue` separately) to receive them. However, even without explicitly claiming, the user's full share balance — including all claimable shares across all deposit queues — is accurately reflected in `shareManager.sharesOf(user)`. 4. Whitelist Enforcement Deposits may require Merkle proof for depositor whitelisting.\ \\
#### 📤 Redeem Queue Serving as the counterpart to the Deposit Queue, it manages withdrawal requests. Redemptions are processed in two phases: 1. Oracle pricing – reports are processed in batches, with each batch assigned a specific conversion price derived from its corresponding Oracle report. 2. Liquidity settlement — Vault liquidity is pulled asynchronously, allowing the curator to finalize withdrawals and perform asset swaps before processing user redemption requests. This separation allows asynchronous liquidity management, gas efficiency, and protection against griefing. Unlike deposit requests, withdrawal requests in the Redeem Queue cannot be canceled to prevent yield-griefing. * **Technical Details** **Redeem flow**: * **Step 1: User redeems** * User submits `redeem` request, vault shares are immediately burned. * The vault curator monitors and manages liquidity across connected Subvaults, pulling funds and swapping assets as needed to fulfill redemption requests. * **Step 2: Oracle report** * Valid and non-suspicious `Oracle` `report` arrives. * The vault curator invokes `handleBatches(n)` on the `RedeemQueue`. * This triggers the movement of required assets from the vault (and associated subvaults) to process redemption requests. * **Step 3: User claims** * Users call `claim(receiver, timestamps)` to withdraw assets. #### Assumptions & Properties 1. Non-Cancellable Requests Prevents griefing where a user requests redemption, causing curator to pull liquidity, then cancels. 2. Time-sensitive request handling Oracle reports can only process a redemption request if at least `redeemInterval` seconds have passed since the request was submitted — i.e., `report.timestamp` must be greater than or equal to `request.timestamp + redeemInterval`. 3. Asynchronous Fulfillment Liquidity can be managed independently of oracle report submission.\ \\
#### 🔏 Signature Deposit and Redeem Queues Signature Queues allow deposits and redemptions to be processed instantly, bypassing the standard time-buffered flow. That happens when a trusted consensus group issues offchain-signed approvals. Key Features: * **Instant execution** without waiting for Oracle price reports * **Nonce-based signature protection** to prevent replay attacks * **EIP-712/EIP-1271 compatible** signed orders * **Oracle price validation** enforced onchain * **Stateless and removable** (does not accumulate shares or process claims) * **Fee-bypass**: No Deposit Fee or Redeem Fee is charged for actions via this queue * **Technical Details** **Signature Queue flow:** * **Step 1: Offchain signing** * Offchain consensus actors (operators, curators, admins) generate signed `Order` messages. * **Step 2: Onchain execution** * A user submits this order to the `SignatureQueue` contract for execution. * The queue: * Verifies the order signature * Validates nonce, queue address, asset match, deadline, and caller * Computes the implied asset/share price and checks it against the vault's oracle * If all checks pass, the order is executed atomically. #### Assumptions & Properties * Only **trusted offchain actors** (consensus group) are authorized to sign orders. * Price quotes must be valid and non-suspicious. * Users cannot reuse old signatures due to nonce tracking. * Orders must be executed before `deadline`.\ \\
#### 💼 Vault The Vault contract serves as the central entry point into the Core Vault system. It is configured by four internal modules: * **BaseModule**: Implements different auxiliary interfaces such as `onERC721Received`, `getStorageAt` and `receive` callback * **ACLModule:** Role-based access control for system components * **ShareModule:** Management of shares, fees, deposit and redeem queues lifecycle, `Oracle` report handling * **VaultModule:** Subvaults management, pushing and pulling of assets in subvaults\ \\
#### 🗃️ Subvault The Subvault contract is a vault component designed to manage delegated asset strategies, acting as a controlled execution unit within a system. Like the Vault contract, it is configured by modules: * **BaseModule**: Implements different auxiliary interfaces such as `onERC721Received`, `getStorageAt` and `receive` callback. * **SubvaultModule:** Represents an isolated child vault within a modular vault system, responsible for securely holding and releasing assets upon authenticated requests. * **VerifierModule:** is an abstract extension of the Base Module designed to provide standardized access to a Verifier contract. * **CallModule:** Enables arbitrary low-level calls to external contracts (used by curator of the vault), and verification through a verifier module.\ \\
#### 👁️ Verifier Each Subvault is paired with a Verifier contract, which validates function calls and ensures only pre-approved actions from valid actors are permitted across vault-connected modules. While each Subvault is expected to have its own dedicated Verifier contract, it is still possible for the same Verifier to be shared across multiple Subvaults. * **Technical Details** Verifier allows multiple types of verification: * **ONCHAIN\_COMPACT**: Checks `CompactCall` (who | where | selector) hash against internal admin-controlled set * **MERKLE\_COMPACT**: Verifies Merkle proof of `CompactCall` (who | where | selector) hash * **MERKLE\_EXTENDED**: Verifies Merkle proof of `ExtendedCall` (who | where | value | callData) hash * **CUSTOM\_VERIFIER:** Delegates full verification to an external verifier Two admin-owned parameters are saved in the state of the Verifier contract: `compactCallHashes` that defines all allowed calls for **ONCHAIN\_COMPACT** verification type `compactCalls` is an optional mapping for reverse lookup of call metadata by hash `merkleRoot` that defines all allowed calls for **MERKLE\_COMPACT, MERKLE\_EXTENDED** and **CUSTOM\_VERIFIER**\ \\
#### 🔮 Oracle The Oracle contract handles secure and configurable price reporting for supported assets. Closely integrated with the `ShareModule`, it provides price validation, deviation monitoring, and time-restricted report submissions. It enforces strict guarantees around **report timing** and **trust minimization** using role permissions and deviation thresholds. This oracle ensures consistent pricing across all queue, share, and limit-related calculations. Key considerations: * Only authorized roles can submit price updates * Suspicious reports require explicit approval before acceptance * Price validation is performed locally without relying on external oracle feeds * Manipulation is prevented through absolute and relative deviation limits * **Technical Details** Oracle Configurable Security Parameters: * **Absolute Deviation**: Hard limits on price delta in price units * **Relative Deviation**: Tolerance as a percentage (e.g., 5% = 0.05e18) * **Timeout**: Minimum time between valid reports (ignored if the previous report is suspicious) * **depositInterval**: Minimum age required for a deposit to be processed * **redeemInterval**: Same, but for redemptions **Reporting flow**: * **Step 1: Report is submitted:** * Each asset is checked for support * The previous report state is evaluated: * If `timeout` has not passed, and the report is not suspicious → revert `TooEarly` * Price is compared against previous: * `maxAbsolute` → revert `InvalidPrice` * `maxRelative` → flagged `isSuspicious` * If the report is valid and non-suspicious (deviation \< suspicious && deviation \< max), it is immediately accepted * If the report is suspicious it will be accepted only after validation from the Admin (ACCEPT\_REPORT\_ROLE holder) * **Step 2: Accepted report propagation:** * Triggers `vault.handleReport(...)`, processing deposit requests and pending redeem requests * Emits `ReportsSubmitted` **Validation Logic:** Reports are validated by: * Calculating **absolute deviation (**`maxAbsolute` ) * Calculating **relative deviation (**`maxRelative` ) * Comparing against `max` and `suspicious` thresholds A report is: * **Rejected** if either deviation exceeds max * **Accepted but marked as suspicious** if above the suspicious threshold, flagging the report * **Accepted as normal** if within all limits Used by: * `SignatureDepositQueue`, `SignatureRedeemQueue`, `DepositQueue` and `RedeemQueue` contracts * Vault's limit accounting (`RiskManager`)\ \\
#### 📊 Share Manager The Share Manager is an upgradeable contract responsible for managing vault share issuance, allocation, whitelisting, permissions, and lockups within a modular vault system. Key responsibilities include: * Tracking total and active share supply * Managing global and targeted account lockups * Verifying whitelist status and transfer permissions * Enforcing mint, burn, and transfer pauses * Handling share allocation and claims through queues * Whitelisting for implementing KYC & compliance features **Technical Details** Share Manager relies on a compact bitmask (`flags`) for enabling/disabling features and supports configurable per-account permissions. Controlled via `ShareManagerFlagLibrary`: * `hasMintPause` * `hasBurnPause` * `hasTransferPause` * `hasWhitelist` * `hasTransferWhitelist` * `globalLockup` * `targetedLockup` Lockups are enforced in `updateChecks`. Share Manager operates under the control of defined roles: * `SET_FLAGS_ROLE`: Allows changing global flags (e.g., mint pause, whitelist enforcement). * `SET_ACCOUNT_INFO_ROLE`: Grants permission to set per-account configuration. * `SET_WHITELIST_MERKLE_ROOT_ROLE`: Grants permission to set new whitelist merkle root.\\
#### 💰 Fee Manager The Fee Manager oversees the calculation and management of multiple fee types within the vault system. Currently, it supports four fee-acquiring methods: * **Deposit Fee:** Charged on asset deposits * **Redeem Fee:** Applied on share redemptions * **Performance Fee:** Based on decreases in share price (assets × price = shares) * **Protocol Fee:** Time-based fee accrued per vault All fees are paid in vault shares rather than the underlying assets. Admin can update recipient and fee parameters. **Technical Details** Fee Manager will require identifying the base asset of the vault. `setBaseAsset`: Called once per vault to register its performance reference token. `updateState(asset, price)`: Vaults call this to refresh timestamp and minimum price. Performance and Protocol fees are activated by a fresh Oracle report on the base asset. #### Fee Calculation Logic: * **Deposit fee** – applied linearly and calculated as `shares * depositFeeD6 / 1e6`, deducted during the oracle report handling process. * **Redeem fee** – applied linearly and calculated as `shares * redeemFeeD6 / 1e6`, deducted when shares are requested for redemption. * **Performance fee** – due to the use of a non-standard pricing mechanism (`price = shares / assets`), delegation yield will result in a lower reported price by the oracle; if `priceD18` falls below `minPriceD18`, the fee is charged as `(minPriceD18 - priceD18) * performanceFeeD6 * totalShares / 1e24` to capture the implied yield. * **Protocol fee** – a continuously accruing time-based fee calculated as `totalShares * protocolFee * (block.timestamp - timestamps[vault]) / (365 * 24 * 3600 * 1e6)`, proportional to both share supply and elapsed time since the last timestamp update.\\
#### 🎯 Risk Manager The Risk Manager contract defines and enforces asset deposit limits across the Vault and its associated Subvaults. It maintains internal accounting of balances, limits, and approved assets for each subvault. This module is responsible for: * Setting and enforcing share limits (practically representing approximate deposit limits) at both vault and subvault levels * Permitting or restricting specific assets to be pulled into the Subvaults * Tracking pending balances for Deposits that are not yet finalized * Validating limits using Oracle price reports **Technical Details** * **Vault Limit**: Global cap across all assets managed by the vault (in shares). * **Subvault Limit**: Individual cap per subvault, enforced independently (in shares). * **Allowed Assets**: Only explicitly allowlisted assets are permitted for push / pull operations in a given subvault. * **Pending Assets**: Temporarily tracked assets, e.g., during deposit queueing * **Shares Conversion**: All balances are internally tracked in shares, calculated using latest report in the `Oracle` contract. All vault and subvault-level limits are treated as **approximate** and computed using the most recent Oracle report available **at the time of the state update** (on Subvault pull/push event or Deposit/Redeem operations). If actual balances deviate significantly from the stored `balance` values due to oracle drift, delayed execution, or protocol-side changes, a **trusted actor** can apply a ‘corrections’ to mitigate the difference: * `modifyVaultBalance` for the Vault, or * `modifySubvaultBalance` for individual Subvaults. Since the system is expected to hold only correlated assets, such manual adjustments are assumed to be **rare** under normal operating conditions.\\
#### 🔐Access Control Granular Access Control (MellowACL) is a lightweight yet extensible layer built on an OpenZeppelin contract. It adds automatic tracking and enumeration of *active roles* to enhance governance transparency and enable dynamic role management. Responsibilities include: * Granting and revoking access control roles to addresses * Maintaining a dedicated set of all active (assigned) roles * Providing enumerable functions for external auditing of granted roles * Emitting events when roles are assigned or fully revoked\\
### Supported protocols and integrations Core Vaults architecture supports integration with a wide range of apps, including both DeFi protocols and centralized exchanges. Most of the protocol integrations can work out of the box. Below is a brief example of potential connections to subvaults. #### Major protocols: * Aave (leverage & supply side LPing) * Gearbox (leverage & supply side LPing) * Curve, Uniswap (DEX liquidity provisioning) * Cowswap (limit orders) * Symbiotic, EigenLayer (provide liquidity for restaking rewards) * Pendle (splitting and selling future yield or holding for boosted returns) * Morpho (leverage & supply side LPing) * Euler (leverage & supply side LPing) * Fluid (leverage & supply side LPing) * Hyperliquid #### **CEXes** via custodial off-exchange solutions (Copper and Ceffu) * Deribit * ByBit * Binance * Most of tier 1 and 2 CEXes ## Case Study **Prerequisites:** The flow occurs within a Core Vault under the following conditions: 1. Two subvaults representing different yield sources: a delta-neutral trading strategy and restaking 2. Liquidity is evenly allocated 50%-50% between the subvaults 3. 1% management fee and 15% performance fee 4. Annual Percentage Rate (APR) of 10% **Initial Action:** A Liquidity Provider (LP) deposits 1,000,000 USDC into the Mellow Core Vault. **Process Flow:** 1. Deposits are processed and transferred into the vault after passing through the time-buffered Deposit Queue. 2. The LP receives receipt tokens representing the 1,000,000 USDC position. 3. The LP can use these receipt tokens as collateral in various DeFi protocols to generate additional yield, such as leverage looping on Gearbox. 4. Liquidity is allocated from the Vault to the Subvaults according to the limit-based rules. 5. 50% of unallocated funds are pulled into a Subvault 1 by a Curator. 6. 50% of unallocated funds are pulled into a Subvault 2 by a Curator. 7. The Curator allocates funds from Subvault 1 to the delta-neutral strategy through integrated centralized exchanges. 8. Funds from Subvault 2 are allocated to the restaking strategy via Symbiotic. 9. After 12 months, the LP requests a full withdrawal. 10. Throughout the holding period, Protocol and Performance Fees are automatically accrued with each Oracle update, resulting in a management fee of 10,000 USDC and a performance fee of 15,000 USDC - both paid in vault receipt tokens. 11. The withdrawal request is queued and detected onchain. 12. At the end of the withdrawal interval, the Curator transfers 1,075,000 USDC (principal plus accrued yield) from the Subvaults back to the Core Vault Contract. 13. The LP redeems the full amount directly from the Redeem Queue. *Process Flow for Curator Allocation:* 1. Curator asks Admin to add 2 subvaults with correct verifier configs: * First one allowing liquidity transfer into a Copper or Ceffu account * Second one allowing deposits, withdrawals, withdrawal claims and reward claims & swaps from Symbiotic 1. Curator pushes unallocated funds: `Vault.pushAssets(USDC, *500000*)`. 2. Allocates assets in Subvault 1 (Delta-Neutral strategy on ByBit via Copper ClearLoop). * In UI, Curator clicks **New Call** → sets target to **Copper Subvault** → selects **USDC** asset and B**ybit Clearloop** as destination. * Generate `VerificationPayload` via Mellow API. * Executes call: ```solidity theme={null} subvault1.call( asset, // address of the asset (USDC) to be sent to the Copper account 0, // eth value abi.encodeCall( IERC20.tranfer, // transfer call encoding (copperAccountAddress, 5e11) // (recipient, amount) ), verificationPayload // extra data for Verifer contract ) ``` * Inside Bybit UI, strategy is realized by the Curator, with settlements occurring every few hours in the Copper Clearloop. 3. Allocate to Subvault 2 (Symbiotic Restaking). * Pushes unallocated funds to the second subvault: `vault.pushAssets(subvault2, asset, 5e11)`. * Clicks **New Call** → target = **Restaking Subvault** → calls `deposit(subvault, 5e11)` * Gets the verification result and `VerificationPayload` from the Mellow API for this call. * Executes call: ```solidity theme={null} subvault2.call( symbioticVault, // address of the symbiotic vault 0, // eth value abi.encodeCall( ISymbioticVault.deposit, // deposit call encoding (subvault, 5e11) // (onBehalfOf, amount) ), verificationPayload // extra data for Verifer contract ) ``` 4. Curator regularly claims rewards from Symbiotic Restaking and swaps them into assets before depositing them using `subvault2.call` 5. Curator monitors Performance & Exit upon user request by repeating **New Call** steps to withdraw according to net returns. # FenwickTreeLibrary Source: https://docs.mellow.finance/core-vaults/architecture/libraries/fenwicktreelibrary This library implements a **0-indexed** Fenwick Tree for tracking cumulative values over a dynamic array. It is suitable for systems where frequent prefix sum queries and point updates are required, such as time-based accounting, queuing systems, or share tracking. ### Design Characteristics * `O(log n)` complexity for both updates and prefix sum queries. * Storage-efficient using a `mapping(uint256 => int256)`, instead of an array. * Only supports lengths that are exact powers of two (`2^k`), which simplifies internal logic and allows future extensions via `extend()`. ### Invariants and Constraints * The tree must be initialized with a power-of-two length > 0 via `initialize(...)`. * Index bounds are enforced — access beyond the current capacity reverts with `IndexOutOfBounds()`. * To support dynamic resizing, `extend()` can double the current tree length (up to a safe limit). * Use of negative values is supported in `modify(...)`, allowing decrement operations. ### Data Structures ```solidity theme={null} struct Tree { mapping(uint256 index => int256) _values; // Internal Fenwick Tree nodes. uint256 _length; // Capacity of the tree (must be power of two). } ``` ### Functions ### `initialize(Tree storage tree, uint256 length_)` Initializes the tree with the specified length. * Reverts with `InvalidLength()` if `length_ == 0` or not a power of two. * Only callable once (re-initialization is not allowed). ### `length(Tree storage tree) → uint256` Returns the current capacity of the tree. ### `extend(Tree storage tree)` Doubles the tree's capacity. * Preserves prefix sum structure. * Reverts with `InvalidLength()` on overflow. ### `modify(Tree storage tree, uint256 index, int256 value)` Increments or decrements the value at a given index by `value`. * Performs `tree[index] += value`. * Reverts if index is out of bounds. * No-op if `value == 0`. ### `get(Tree storage tree, uint256 index) → int256` Returns the **prefix sum** for the range `[0, index]`. * If `index >= length`, it is clamped to `length - 1`. ### `get(Tree storage tree, uint256 from, uint256 to) → int256` Returns the sum over the range `[from, to]` (inclusive). * Returns `0` if `from > to`. ### Internals ### `_modify(...)` Low-level implementation of Fenwick update using bitwise operations: * Updates `tree[index]` and propagates changes upward via `index |= index + 1`. ### `_get(...)` Assembly-optimized prefix sum computation: * Aggregates values by descending via `index := and(index, index + 1) - 1`. ### References * [CP Algorithms: Fenwick Tree](https://cp-algorithms.com/data_structures/fenwick.html) * [Wikipedia: Binary Indexed Tree](https://en.wikipedia.org/wiki/Fenwick_tree) # Libraries Source: https://docs.mellow.finance/core-vaults/architecture/libraries/index In this directory, you will find a detailed per-contract overview of the libraries used in Core Vaults, including the following: [FenwickTreeLibrary](/core-vaults/architecture/libraries/fenwicktreelibrary) [ShareManagerLibrary](/core-vaults/architecture/libraries/sharemanagerlibrary) [SlotLibrary](/core-vaults/architecture/libraries/slotlibrary) [TransferLibrary](/core-vaults/architecture/libraries/transferlibrary) # ShareManagerLibrary Source: https://docs.mellow.finance/core-vaults/architecture/libraries/sharemanagerlibrary This library helps pack multiple boolean flags and lockup durations into a compact `uint256` bitmask. It enables efficient storage and quick access to share manager configuration in vault systems. Designed for the `ShareManager` component to control: * Whether minting, burning, or transfers are paused * Whether deposit/transfer whitelists are active * How long global or user-specific lockups last All data is packed into a single `uint256` using bit-level encoding for optimal storage and gas efficiency. ### Bitmask Layout | Bit Range | Purpose | | ---------- | ----------------------------- | | `[0]` | `hasMintPause` (bool) | | `[1]` | `hasBurnPause` (bool) | | `[2]` | `hasTransferPause` (bool) | | `[3]` | `hasWhitelist` (bool) | | `[4]` | `hasTransferWhitelist` (bool) | | `[5..36]` | `globalLockup` (uint32) | | `[37..68]` | `targetedLockup` (uint32) | ### Functions ### `hasMintPause(uint256 mask) → bool` Returns `true` if minting is paused (bit 0 is set). ### `hasBurnPause(uint256 mask) → bool` Returns `true` if burning is paused (bit 1 is set). ### `hasTransferPause(uint256 mask) → bool` Returns `true` if transfers are paused (bit 2 is set). ### `hasWhitelist(uint256 mask) → bool` Returns `true` if a deposit whitelist is enabled (bit 3 is set). ### `hasTransferWhitelist(uint256 mask) → bool` Returns `true` if a transfer whitelist is enabled (bit 4 is set). ### `getGlobalLockup(uint256 mask) → uint32` Returns the **global lockup duration** in seconds (timestamp), encoded in bits `[5..36]`. ### `getTargetedLockup(uint256 mask) → uint32` Returns the **targeted lockup duration** in seconds, encoded in bits `[37..68]`. ### `createMask(IShareManager.Flags calldata f) → uint256` Encodes the values in a `Flags` struct into a single bitmask: ```solidity theme={null} struct Flags { bool hasMintPause; bool hasBurnPause; bool hasTransferPause; bool hasWhitelist; bool hasTransferWhitelist; uint32 globalLockup; uint32 targetedLockup; } ``` # SlotLibrary Source: https://docs.mellow.finance/core-vaults/architecture/libraries/slotlibrary This library generates unique and collision-resistant storage slots for use in upgradeable Solidity contracts. It ensures that different modules or instances do not unintentionally overwrite each other’s storage, even when used via proxy or delegate calls. ### Storage Slot Strategy * Based on EIP-7201 * Inputs include: * Contract name (`contractName`) * Human-readable name (`name`) * Version number (`version`) * Final slot: ```solidity theme={null} keccak256( abi.encode( uint256( keccak256( abi.encodePacked( "mellow.flexible-vaults.storage.", contractName, name, version ) ) ) - 1 ) ) & ~bytes32(uint256(0xff)); ``` This structure ensures: * **Namespacing:** Prevents overlap between different modules (`ShareModule`, `FeeManager`, etc.) * **Instance separation:** Multiple deployments with different names produce distinct slots * **Versioning:** Upgrades can cleanly migrate to new versions without collision ### Function ### `getSlot(string contractName, string name, uint256 version) → bytes32` **Description:** Computes a deterministic, collision-resistant storage slot for a contract module. **Parameters:** * `contractName`: Logical name of the module (e.g., `"ShareModule"`) * `name`: Instance identifier or label (e.g., `"Mellow"`) * `version`: Numeric version for versioned slot separation **Returns:** * A `bytes32` value representing the computed storage slot **Example:** ```solidity theme={null} bytes32 slot = SlotLibrary.getSlot("FeeManager", "Mellow", 1); ``` # TransferLibrary Source: https://docs.mellow.finance/core-vaults/architecture/libraries/transferlibrary This utility abstracts away the differences between transferring native ETH and ERC20 tokens by introducing a unified interface for both sending and receiving assets. It also standardizes how native ETH is represented on-chain to simplify integration logic across different components. ### ETH Representation The constant `ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE` ([\*\*EIP-7528](https://ethereum-magicians.org/t/eip-7528-eth-native-asset-address-convention/15989))\*\* is used as a sentinel value to distinguish native ETH from ERC20 tokens. ### Errors * `InvalidValue()`: Thrown when the contract expects a specific `msg.value` (for native ETH transfers) but receives a different amount. ### Constants * `ETH`: Reserved address used to represent native Ether. When passed to `sendAssets` or `receiveAssets`, the function will process ETH instead of calling token functions. ### Functions ### `sendAssets(address asset, address to, uint256 assets)` Sends the specified asset (`assets` amount) to the recipient `to`. * If `asset == ETH`, the function uses `Address.sendValue` to transfer native ETH. * If `asset` is an ERC20 token, it uses `IERC20.safeTransfer`. **Parameters:** * `asset`: Address of the asset to transfer. Should be `ETH` for native Ether or the ERC20 token address. * `to`: Address to send the asset to. * `assets`: Amount of the asset to transfer. **Reverts if:** ETH transfer fails or ERC20 transfer fails via `SafeERC20`. ### `receiveAssets(address asset, address from, uint256 assets)` Receives assets from the caller (or a third-party) into the current contract. * If `asset == ETH`, verifies that `msg.value == assets`. * If `asset` is an ERC20 token, calls `IERC20.safeTransferFrom` from `from` to the current contract. **Parameters:** * `asset`: Address of the asset to receive. Use `ETH` for native Ether or an ERC20 token address. * `from`: Address sending the ERC20 tokens (ignored for ETH). * `assets`: Expected amount of the asset to receive. **Reverts if:** * The contract receives an incorrect `msg.value` for ETH. * The ERC20 transfer fails. > Calling `receiveAssets(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, from, assets)` multiple times within a single function call will result in incorrect asset accounting. > > **DO NOT** use this function in scenarios like the following: ```solidity theme={null} function func() external payable { TransferLibrary.receiveAssets(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, msg.sender, 1 ether); TransferLibrary.receiveAssets(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, msg.sender, 1 ether); ... } ``` # BasicShareManager Source: https://docs.mellow.finance/core-vaults/architecture/managers/basicsharemanager ### Overview `BasicShareManager` is a concrete implementation of the abstract `ShareManager`, designed to provide native ERC20-style share accounting within a modular vault system. It handles minting, burning, and tracking balances of vault shares directly through a local ERC20-compatible storage layout, without exposing standard ERC20 interfaces. This contract is intended for setups where shares are not tokenized on-chain as ERC20s but are still tracked internally using the ERC20Upgradeable storage schema. ### Key Features * Uses `ShareManager` for permissioning, allocation, and whitelisting logic. * Maintains balances and total supply using `ERC20Upgradeable.ERC20Storage`. * Internal mint/burn logic emits `IERC20.Transfer` events (for transparency or compatibility). * Fully decoupled from standard `ERC20` interface – share transfers are governed by vault queues and mint/burn logic only. ### Storage ERC20-style balances and supply are stored at a fixed storage slot allowing for migrations BasicShareManager ↔ TokenizedShareManager: ```solidity theme={null} bytes32 private constant ERC20StorageLocation = 0x52c6...ce00; ``` ### Initialization ```solidity theme={null} function initialize(bytes calldata data) external initializer ``` * Expects a single `bytes32 whitelistMerkleRoot` (used by `ShareManager`). ### View Functions * `activeShares()`: Returns `_totalSupply` from ERC20 storage. * `activeSharesOf(account)`: Returns balance of `account`. ### Internal Logic ### `_mintShares(address, uint256)` * Checks if minting is allowed via `updateChecks`. * Increments total supply and receiver's balance. * Emits `IERC20.Transfer(address(0), account, value)`. Reverts if: * `account == address(0)` * Minting is paused or restricted by lockup, whitelist, or blacklist ### `_burnShares(address, uint256)` * Checks if burning is allowed via `updateChecks`. * Decreases sender's balance and total supply. * Emits `IERC20.Transfer(account, address(0), value)`. Reverts if: * `account == address(0)` * `value > account balance` * Burning is paused or blocked ### Design Notes * This module deliberately avoids exposing the ERC20 interface, preventing any unintended external transfers or integrations. * It is intended for internal share accounting within vault systems, where shares are tracked but not tokenized onchain. * All permissioning logic, including minting, burning, whitelisting, and lockup enforcement, is delegated to the inherited `ShareManager`. * This implementation is ideal when the vault owner requires non-transferable shares for internal logic, without compliance to ERC20 or ERC4626 standards. * It is **not appropriate** for setups where shares must be externally transferable, interoperable with third-party protocols, or conform to token standards. # FeeManager Source: https://docs.mellow.finance/core-vaults/architecture/managers/feemanager **Modular, upgradeable fee management contract for vaults.** The `FeeManager` is responsible for managing and calculating various fee types in a vault system, including deposit, redemption, performance, and protocol fees. It uses a flexible architecture with deterministic storage slots (via `SlotLibrary`) and supports per-vault configurations. ### Key Responsibilities * Configures and stores global fee settings (in D6 precision). * Tracks vault-specific state (base asset, min price, timestamp). * Computes: * **Deposit Fee**: Fee charged on asset deposit. * **Redeem Fee**: Fee charged on share redemption. * **Performance Fee**: Fee based on drop in price (`assets * price = shares`) * **Protocol Fee**: Time-based fee accrued per vault. * Provides administrative controls to update fee settings and vault metadata. ### Storage Uses an isolated storage slot per deployment instance, computed deterministically using `SlotLibrary.getSlot("FeeManager", name, version)`, ensuring safety and upgradability. Each vault is associated with: * `baseAsset`: Reference token used for performance fee calculation. * `minPriceD18`: Minimum price recorded (used for performance fee calculation). * `timestamps`: Last update timestamp for time-based fee accrual. ### Fee Calculation Logic ### `calculateDepositFee(uint256 shares) → uint256` Computes a linear fee as `shares * depositFeeD6 / 1e6`. ### `calculateRedeemFee(uint256 shares) → uint256` Computes a linear fee as `shares * redeemFeeD6 / 1e6`. ### `calculateFee(...) → uint256 shares` Calculates the total fee to be charged based on: * Performance: If current `priceD18` below `minPriceD18`, applies `performanceFeeD6` as `(minPriceD18 - priceD18) * performanceFeeD6 * totalShares / 1e24`. * Protocol: Time-weighted fee based on `block.timestamp - timestamps[vault]`, computed as `totalShares * protocolFee * (block.timestamp - timestamps[vault]) / (365 * 24 * 3600 * 1e6)` All fees are paid in **shares** of the vault (not assets). ### Access Control * Only the `owner` (defined during initialization) can modify fee parameters or vault configurations. * Calls to `initialize(...)` must come from the factory and include all required setup parameters. ### Events * `Initialized(bytes data)`: Emitted after initialization. * `SetFeeRecipient(address feeRecipient)`: On recipient update. * `SetFees(...)`: On any fee change. * `SetBaseAsset(...)`: When base asset is configured per vault. * `UpdateState(...)`: On state update used for protocol/performance fees. ### Errors * `ZeroAddress()`: Thrown if an address input is zero. * `InvalidFees(...)`: When the combined fee rate exceeds 100% (`1e6` D6). * `BaseAssetAlreadySet(...)`: Prevents base asset override if already set. ### Lifecycle 1. **Constructor**: Sets storage slot based on name/version. 2. **Initialization** (via `initialize(bytes)`): Sets owner, recipient, and fees. 3. **Fee Updates**: Admin can update recipient and fee parameters. 4. **Vault Hooks**: * `updateState(asset, price)`: Vaults call this to refresh timestamp and min price. * `setBaseAsset`: Called once per vault to register its performance reference token. # Managers Source: https://docs.mellow.finance/core-vaults/architecture/managers/index In this directory, you will find a detailed per-contract overview of the "Managers" contract category, including the following contracts: [FeeManager](/core-vaults/architecture/managers/feemanager) [RiskManager](/core-vaults/architecture/managers/riskmanager) [ShareManager](/core-vaults/architecture/managers/sharemanager) [BasicShareManager](/core-vaults/architecture/managers/basicsharemanager) [TokenizedShareManager](/core-vaults/architecture/managers/tokenizedsharemanager) # RiskManager Source: https://docs.mellow.finance/core-vaults/architecture/managers/riskmanager **On-chain risk control and allocation policy manager for modular vaults.** The `RiskManager` contract defines and enforces asset deposit limits across a `Vault` and its associated `Subvaults`. It maintains internal accounting of balances, limits and allowed subvault assets. ### Purpose This contract is a centralized module responsible for: * Defining and enforcing **deposit limits** at vault and subvault levels. * Allowlisting or disallowing **specific assets** per subvault. * Tracking **pending balances** (deposits/withdrawals that are not yet finalized). * Validating risk assumptions through **oracle price reports**. ### Core Concepts * **Vault Limit**: Global cap across all assets managed by the vault (in shares). * **Subvault Limit**: Individual cap per subvault, enforced independently (in shares). * **Allowed Assets**: Only explicitly allowlisted assets are permitted in a given subvault. * **Pending Assets**: Temporarily tracked assets, e.g., during deposit queueing * **Shares Conversion**: All balances are internally tracked in shares, calculated using latest report in the `Oracle` contract. ### Storage Slot Utilizes a deterministic storage slot computed via `SlotLibrary.getSlot("RiskManager", name, version)` to ensure safe upgrades and modular deployment. ### Roles and Permissions The contract uses fine-grained access roles: * `SET_VAULT_LIMIT_ROLE`: Can modify global vault capacity. * `SET_SUBVAULT_LIMIT_ROLE`: Can change limits on individual subvaults. * `ALLOW_SUBVAULT_ASSETS_ROLE`: Can whitelist assets for specific subvaults. * `DISALLOW_SUBVAULT_ASSETS_ROLE`: Can revoke asset approval from subvaults. * `MODIFY_PENDING_ASSETS_ROLE`: Can manipulate pending balance delta. * `MODIFY_VAULT_BALANCE_ROLE`: Can update the vault's live balance. * `MODIFY_SUBVAULT_BALANCE_ROLE`: Can update a subvault’s internal balance. Roles are verified via the vault's ACL module (`IACLModule`) or allowed queues (`IShareModule`). ### Key Methods ### View * `vault()`: Returns the vault address. * `vaultState()`: Returns the global vault state (limit, balance). * `pendingBalance()`: Returns the pending share balance across all assets and deposit queues. * `subvaultState(address)`: Returns per-subvault state. * `pendingAssets(address)`: Returns currently pending asset amount. * `pendingShares(address)`: Returns share-equivalent of pending assets. * `allowedAssets(address)`: Count of allowed assets in subvault. * `allowedAssetAt(address, index)`: Indexed lookup of allowed asset. * `isAllowedAsset(address, asset)`: Checks asset permission for subvault. * `convertToShares(asset, value)`: Converts amount to share units using oracle. * `maxDeposit(subvault, asset)`: Calculates max deposit amount given limits and prices. ### Mutable * `initialize(bytes data)`: Initializes vault-wide limit. * `setVault(address)`: Assigns the vault address (one-time only). * `setVaultLimit(int256 limit)`: Updates vault's global limit. * `setSubvaultLimit(address, int256)`: Updates limit for a specific subvault. * `allowSubvaultAssets(address, address[])`: Adds assets to subvault's allowlist. * `disallowSubvaultAssets(address, address[])`: Removes assets from allowlist. * `modifyPendingAssets(address, int256)`: Adjusts pending assets and updates internal shares. * `modifyVaultBalance(address, int256)`: Applies a delta to vault's current balance (with limit checks). * `modifySubvaultBalance(address, asset, int256)`: Same as above, but scoped to a specific subvault. ### Internal Mechanics ### Conversion to Shares Conversion is done with oracle price data, where: ```solidity theme={null} shares = (value * priceD18) / 1e18 ``` ### Assumptions The system assumes that: * The vault and its subvaults operate exclusively with **correlated assets**, and * Protocol-level delegations performed by the curator **do not introduce extreme APR variance or significant principal loss**. Given this, all vault- and subvault-level limits are treated as **approximate** and are computed using the most recent oracle report available **at the time of the state update** (e.g., on pull/push or deposit/redeem operations). If actual balances deviate significantly from the stored `balance` values due to oracle drift, delayed execution, or protocol-side changes, a **trusted actor** can apply a ‘corrections’ to mitigate the difference: * `modifyVaultBalance` for the Vault, or * `modifySubvaultBalance` for individual Subvaults. Since the system is expected to hold only correlated assets, such manual adjustments are assumed to be **rare** under normal operating conditions. # ShareManager Source: https://docs.mellow.finance/core-vaults/architecture/managers/sharemanager ### Overview The `ShareManager` is an abstract upgradeable contract responsible for managing vault share issuance, allocation, whitelisting, permissions, and lockups in a modular vault system. It relies on a compact bitmask (`flags`) for enabling/disabling features and supports configurable per-account permissions. ### Key Responsibilities * Tracking total and active share supply * Managing global and per-account lockups * Verifying whitelist and transfer permissions * Enforcing mint/burn/transfer pauses * Allocating and claiming shares through queues * Emitting granular control events on state transitions ### Storage Storage is accessed via a deterministic slot generated by `SlotLibrary.getSlot("ShareManager", name, version)`. ```solidity theme={null} struct ShareManagerStorage { address vault; uint256 flags; // Encodes permissions and lockup durations uint256 allocatedShares; bytes32 whitelistMerkleRoot; mapping(address => AccountInfo) accounts; } ``` ### Permission System Controlled through roles defined as: * `SET_FLAGS_ROLE`: Allows changing global flags (e.g., mint pause, whitelist enforcement). * `SET_ACCOUNT_INFO_ROLE`: Grants permission to set per-account configuration. * `SET_WHITELIST_MERKLE_ROOT_ROLE`: Grants permission to set new whitelist merkle root. * All share-related actions are guarded via: * `onlyQueue()` * `onlyVaultOrQueue()` * `onlyRole(...)` ### View Functions * `vault()`: Returns the vault address. * `sharesOf(account)`: Total shares (active + claimable). * `activeSharesOf(account)`: Abstract; must be implemented by child. * `claimableSharesOf(account)`: Reads from the `IShareModule`. * `totalShares()`: `allocatedShares + activeShares()` * `accounts(account)`: Returns `AccountInfo` struct (deposit/transfer flags, lockups, blacklisting). * `flags()`: Decoded bitmask as `Flags` struct. * `whitelistMerkleRoot()`: Current root used for off-chain whitelist proof verification. * `isDepositorWhitelisted(account, proof)`: Verifies Merkle proof or checks local permission flags. * `updateChecks(from, to)`: Reverts on violations (paused actions, lockups, blacklisting, etc.). ### Mutable Functions * `setVault(...)`: One-time vault initialization. * `setFlags(...)`: Updates global bitmask configuration. * `setWhitelistMerkleRoot(…)`: Updates whitelist merkle root. * `setAccountInfo(...)`: Sets access rights for an individual address. * `claimShares(...)`: Claims shares from the vault’s `IShareModule`. * `allocateShares(...)`: Allocates shares for future minting (only callable by a queue). * `mintAllocatedShares(...)`: Mints shares from allocated pool to user. * `mint(...)`: Mints shares to a user with optional lockup. * `burn(...)`: Burns a user’s shares (only queue). * `__ShareManager_init(...)`: Sets Merkle root at construction or upgrade. ### Internal Hooks (Implemented by Child) ```solidity theme={null} function _mintShares(address account, uint256 value) internal virtual; function _burnShares(address account, uint256 value) internal virtual; ``` These abstract functions allow concrete implementations to define how share balances are recorded or tokenized. ### Bitmask-Controlled Features Controlled via `ShareManagerFlagLibrary`: * `hasMintPause` * `hasBurnPause` * `hasTransferPause` * `hasWhitelist` * `hasTransferWhitelist` * `globalLockup` * `targetedLockup` Lockups are enforced in `updateChecks`. # TokenizedShareManager Source: https://docs.mellow.finance/core-vaults/architecture/managers/tokenizedsharemanager ### Overview * This module extends `ShareManager` and `ERC20Upgradeable`, making vault shares externally transferable and fully compliant with the ERC20 standard. * It is intended for vaults that require tokenized shares usable across external protocols, wallets, or DeFi integrations. * Core share logic (minting, burning, whitelisting, lockups) is delegated to the inherited `ShareManager`, preserving consistent permission enforcement. * Whitelist enforcement, lockup mechanics, and share claim logic are integrated into the overridden `_update` hook, which ensures all token transfers pass necessary checks and call `claimShares` for non-zero actors. * Suitable for use cases where share liquidity, composability, or token standard compatibility (e.g., ERC20, ERC4626 wrappers) is required. # ACLModule Source: https://docs.mellow.finance/core-vaults/architecture/modules/aclmodule ### Overview Abstract module integrating role-based access control via `MellowACL`, providing permission management functionality. ## Internal Functions ### `__ACLModule_init` ```solidity theme={null} function __ACLModule_init(address admin_) internal onlyInitializing ``` ### Description Initializes the module with an admin address by assigning the `DEFAULT_ADMIN_ROLE`. This sets up the foundational RBAC structure. ### Parameters * `admin_`: Address to be granted the `DEFAULT_ADMIN_ROLE`. ### Requirements * `admin_` must not be the zero address. * Callable only during initialization (`onlyInitializing`). # BaseModule Source: https://docs.mellow.finance/core-vaults/architecture/modules/basemodule ## Overview `BaseModule` is an abstract contract that acts as a foundational layer for modules within the system. It integrates shared logic such as initializer protection, reentrancy guard, IERC721Receiver compliance and low level storage access. This module is intended to be inherited and extended by other functional modules. ## Constructor ```solidity theme={null} constructor() { _disableInitializers(); } ``` Prevents the contract from being initialized outside of proxy context. Ensures secure upgradeable deployments. ## Public & External Functions ### `getStorageAt(bytes32 slot)` ```solidity theme={null} function getStorageAt(bytes32 slot) external pure returns (StorageSlot.Bytes32Slot memory) ``` Returns a reference to a custom storage slot. Enables advanced access to shared storage across upgradeable modules using the `StorageSlot` pattern. **Parameters:** * `slot` — The `bytes32` identifier of the storage slot. **Returns:** * A `StorageSlot.Bytes32Slot` struct pointing to the slot. ### `onERC721Received(...)` ```solidity theme={null} function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) ``` ERC721 receiver hook implementation to support safe transfers of NFTs to the module. Returns the selector as required by `IERC721Receiver`. **Returns:** * `IERC721Receiver.onERC721Received.selector` — confirms compliance. ### `receive()` ```solidity theme={null} receive() external payable {} ``` Allows the contract to receive native ETH transfers. This is typically used for vaults handling native tokens directly. ## Internal Functions ### `__BaseModule_init()` ```solidity theme={null} function __BaseModule_init() internal onlyInitializing ``` Initializes internal dependencies and base upgradeable components. Should be called from derived contract initializers. **Side Effects:** * Calls `__ReentrancyGuard_init()` to initialize reentrancy protection. # CallModule Source: https://docs.mellow.finance/core-vaults/architecture/modules/callmodule ### Overview Abstract contract extending `VerifierModule`, implementing low-level contract calls with verification via a pluggable verifier. ### `call` ```solidity theme={null} function call( address where, uint256 value, bytes calldata data, IVerifier.VerificationPayload calldata payload ) external nonReentrant returns (bytes memory response) ``` ### Description Executes a low-level call to a target contract after validating the call parameters through an external `Verifier` contract. The verification logic is determined by the verification type specified in the `Verifier` contract. For details on available verification types, refer to the [Verifier specification](https://www.notion.so/Verifier-23002ad8627680cfbab5e96defcdbe31?pvs=21). ### Parameters * `where`: The address of the target contract. * `value`: The ETH value to send along with the call. * `data`: Calldata to pass to the target contract. * `payload`: Encoded verification payload used to authorize the call. ### Returns * `response`: The raw returned data from the target contract call. ### Requirements * All the provided parameters must be externally verified via `verifier().verifyCall`. * Reentrancy is prevented via `nonReentrant` modifier. # Modules Source: https://docs.mellow.finance/core-vaults/architecture/modules/index In this directory, you will find a detailed per-contract overview of the "Modules" contract category, including the following modules: [VerifierModule](/core-vaults/architecture/modules/verifiermodule) [ACLModule](/core-vaults/architecture/modules/aclmodule) [CallModule](/core-vaults/architecture/modules/callmodule) [ShareModule](/core-vaults/architecture/modules/sharemodule) [VaultModule](/core-vaults/architecture/modules/vaultmodule) [SubvaultModule](/core-vaults/architecture/modules/subvaultmodule) # ShareModule Source: https://docs.mellow.finance/core-vaults/architecture/modules/sharemodule ### Purpose `ShareModule` is a core module responsible for managing user interactions with a vault through structured deposit and redeem queues. It provides governance over queue creation, hook configurations, oracle-driven settlement, and fee accounting. ### Key Responsibilities * Tracks and validates all deposit/redeem queue operations. * Coordinates price reporting with oracle. * Facilitates dynamic queue configuration and lifecycle. * Integrates hooks for deposit/redeem processing customization. * Central hub for protocol and performance fee minting, share claim logic and report handling. ### Roles * `SET_HOOK_ROLE`: Grants the ability to modify per-queue and default hook addresses. * `CREATE_QUEUE_ROLE`: Allows creation of new deposit/redeem queues. * `SET_QUEUE_STATUS_ROLE`: Permits pausing/unpausing individual queues. * `SET_QUEUE_LIMIT_ROLE`: Enables setting the max number of total queues. * `REMOVE_QUEUE_ROLE`: Allows safe removal of queues with `canBeRemoved()` check. ### Core Storage Layout (`ShareModuleStorage`) * `shareManager`: Reference to contract handling share minting/burning. * `feeManager`: Reference to contract that calculates fees and stores fee-related data. * `oracle`: Oracle contract providing price data and asset support status. * `defaultDepositHook` / `defaultRedeemHook`: Global fallback hooks used by default in case custom hooks are not defined. * `customHooks`: Per-queue override for custom hook logic. * `queueCount`: Total existing queues. * `queueLimit`: Global max limit for queues. * `isDepositQueue`: Distinguishes deposit queues from redeem ones. * `isPausedQueue`: Tracks paused queues. * `queues`: Asset → queues mapping. * `assets`: Registry of all assets with registered queues. ### View Functions * `shareManager()`: Returns the `IShareManager` instance. * `feeManager()`: Returns the `IFeeManager` instance. * `oracle()`: Returns the `IOracle` instance. * `depositQueueFactory()` / `redeemQueueFactory()`: Queue factory contracts. * `queueLimit()`: Max allowed queues. * `claimableSharesOf(account)`: Sum of claimable shares across all deposit queues for the `account`. * `getLiquidAssets()`: Called by redeem queues to determine liquidity available for handling redemptions. * `defaultDepositHook()` / `defaultRedeemHook()`: Global default hooks. * `getHook(queue)`: Resolves hook for queue (custom or default fallback as a fallback). * Asset/Queue helpers: * `getAssetCount()`, `assetAt(index)`, `hasAsset(asset)` * `hasQueue(queue)`, `isDepositQueue(queue)`, `isPausedQueue(queue)` * `getQueueCount()` / `getQueueCount(asset)` * `queueAt(asset, index)` ### Mutable Functions * `claimShares(account)`: Claims all claimable shares from deposit queues for the specific account. * `callHook(assets)`: Calls the queue’s associated hook. Transfers assets to the queue if redeem. * `setCustomHook(queue, hook)`: Assigns per-queue hook. * `setDefaultDepositHook(hook)` / `setDefaultRedeemHook(hook)`: Sets global hooks. * `setQueueLimit(limit)`: Updates global queue cap. * `setQueueStatus(queue, isPaused)`: Pauses/unpauses a queue. * `createQueue(version, isDeposit, owner, asset, data)`: Deploys new queue for asset. * `removeQueue(queue)`: Removes a queue that passed `canBeRemoved()`. * `handleReport(asset, priceD18, depositTimestamp, redeemTimestamp)`: * Called by the oracle. * Distributes protocol fees. * Propagates price report to all queues and calls hooks. ### Events * `SharesClaimed(account)` * `CustomHookSet(queue, hook)` * `QueueCreated(queue, asset, isDepositQueue)` * `QueueRemoved(queue, asset)` * `HookCalled(queue, asset, assets, hook)` * `QueueLimitSet(limit)` * `SetQueueStatus(queue, isPaused)` * `DefaultHookSet(hook, isDepositHook)` * `ReportHandled(asset, priceD18, depositTimestamp, redeemTimestamp, fees)` # SubvaultModule Source: https://docs.mellow.finance/core-vaults/architecture/modules/subvaultmodule ### Purpose The `SubvaultModule` represents an isolated child vault within a modular vault system. It is tightly controlled by its parent vault (typically a `VaultModule`) and is responsible for securely holding and releasing assets upon authenticated requests. ### Responsibilities * Store and isolate a portion of vault assets * Allow trusted actor (curator) to delegate liquidity from the Subvault to external protocol based on the `Verifier` setup for this specific subvault * Respond to `pullAssets` calls from the parent vault only ### Storage Layout (`SubvaultModuleStorage`) ```solidity theme={null} struct SubvaultModuleStorage { address vault; } ``` * `vault`: Address of the root vault that controls this subvault. Only this address can request asset withdrawals. The layout is stored in a deterministic custom slot derived using: ```solidity theme={null} SlotLibrary.getSlot("SubvaultModule", name_, version_) ``` ### View Functions ### `vault() → address` Returns the address of the parent vault that instantiated this subvault. ### Mutable Functions ### `pullAssets(asset: address, value: uint256)` Allows the parent vault to withdraw a specified amount of an asset. * **Access Control**: Can only be called by the `vault()` address * **Reverts**: With `NotVault()` if the caller is not the vault * **Transfer Behavior**: Uses `TransferLibrary.sendAssets()` to forward tokens or native ETH to the `Vault.sol` address * **Emits**: `AssetsPulled(asset, vault, value)` ### Internal Initialization ### `__SubvaultModule_init(address vault_)` Internal setup method to be called during construction or proxy initialization. ### Events ### `event AssetsPulled(address indexed asset, address indexed to, uint256 value)` Triggered when assets are withdrawn by the parent vault. * `asset`: Address of the ERC20 token or native ETH * `to`: Always equals the `vault()` address * `value`: Amount of the asset transferred ### Error Handling * **`NotVault()`**: Raised when a non-vault caller attempts to call `pullAssets()` # VaultModule Source: https://docs.mellow.finance/core-vaults/architecture/modules/vaultmodule ### Purpose `VaultModule` is a core component of the modular vault architecture. It manages liquidity routing between the [`Vault`](https://www.notion.so/Vault-23002ad86276805a88a4c52c48b7f677?pvs=21) and its connected [`Subvaults`](https://www.notion.so/Subvault-23002ad8627680ef88ebe91c30b2d1b4?pvs=21), enabling flexible strategy composition and modular upgrades. It supports hot-swapping of subvault contracts and ensures robust control over asset movement. ### Responsibilities * Orchestrate liquidity push/pull operations between the vault and subvaults * Create, disconnect, and reconnect subvaults * Verify creations, removals and reconnections using external `Factory` contracts and local state * Track and update risk exposure via `RiskManager` ### Roles * `CREATE_SUBVAULT_ROLE`: Allows creation of new subvaults * `DISCONNECT_SUBVAULT_ROLE`: Allows disconnection of active subvaults * `RECONNECT_SUBVAULT_ROLE`: Allows reattachment of disconnected or new properly configured subvaults * `PULL_LIQUIDITY_ROLE`: Grants permission to pull assets from subvaults * `PUSH_LIQUIDITY_ROLE`: Grants permission to send assets to subvaults ### Storage Layout (`VaultModuleStorage`) ```solidity theme={null} struct VaultModuleStorage { address riskManager; EnumerableSet.AddressSet subvaults; } ``` * `riskManager`: Module used to track and limit exposure per asset/subvault * `subvaults`: Enumerable set of currently connected subvaults ### View Functions * `subvaultFactory()`: Returns `IFactory` used to deploy and check deployed subvaults * `verifierFactory()`: Returns `IFactory` used to deploy and check deployed verifiers * `subvaults()`: Returns the total number of connected subvaults * `subvaultAt(index)`: Returns the subvault address at a specific index * `hasSubvault(address)`: Checks if a given address is an active subvault * `riskManager()`: Returns the address of the risk manager ### Mutable Functions ### Subvault Management * `createSubvault(version, owner, verifier)`: * Deploys a new subvault via the `subvaultFactory` * Links it to the provided `verifier` * Adds it to the vault's subvault list * Emits `SubvaultCreated` * `disconnectSubvault(subvault)`: * Removes a subvault from the vault registry * Emits `SubvaultDisconnected` * Reverts with `NotConnected` if not already linked * `reconnectSubvault(subvault)`: * Re-adds a subvault to the vault registry * Validates via `subvaultFactory` and `verifierFactory` * Emits `SubvaultReconnected` * Reverts with `InvalidSubvault`, `NotEntity`, or `AlreadyConnected` if checks fail ### Liquidity Movement * `pushAssets(subvault, asset, value)`: * Transfers assets from vault to subvault * Updates internal risk manager state (adds exposure) * Emits `AssetsPushed` * `pullAssets(subvault, asset, value)`: * Retrieves assets from a subvault * Updates internal risk manager state (reduces exposure) * Emits `AssetsPulled` ### Internal Liquidity Hooks These can only be invoked by the vault itself (via hooks): * `hookPushAssets(subvault, asset, value)` * `hookPullAssets(subvault, asset, value)` ### Error Conditions * `AlreadyConnected(subvault)`: When attempting to reconnect an already connected subvault * `NotConnected(subvault)`: When attempting to disconnect a subvault that isn't connected * `NotEntity(address)`: Provided contract is not a valid `IFactory`deployed entity * `InvalidSubvault(address)`: Subvault fails verification (incorrect `subvault.vault()` address) * `ZeroAddress()`: Passed `RiskManager` address is zero (used in `__VaultModule_init`) * `Forbidden()`: Caller is not authorized (used in internal checks) ### Events * `SubvaultCreated(subvault, version, owner, verifier)` * `SubvaultDisconnected(subvault)` * `SubvaultReconnected(subvault, verifier)` * `AssetsPulled(asset, subvault, value)` * `AssetsPushed(asset, subvault, value)` ### Security Considerations * All critical functions gated by role-based ACL * Uses factory-verified deployments for submodules * Internal state (risk exposure) updated on every asset movement * Only the vault contract itself may invoke `hook*` liquidity functions ### Initialization ```solidity theme={null} function __VaultModule_init(address riskManager_) internal onlyInitializing ``` * Sets the `riskManager` address (must be non-zero) * Should be invoked during deployment or upgrade setup # VerifierModule Source: https://docs.mellow.finance/core-vaults/architecture/modules/verifiermodule ## Overview `VerifierModule` is an abstract extension of `BaseModule` designed to provide standardized access to a `Verifier` contract. It manages internal storage using a deterministic slot derived via `SlotLibrary`, supporting secure modular composition across multiple vault systems. ## Constructor ```solidity theme={null} constructor(string memory name_, uint256 version_) ``` Computes and stores the custom storage slot used for verifier configuration based on a unique `(name_, version_)` pair. **Parameters:** * `name_` — Unique identifier used to namespace the storage slot. * `version_` — Version number used for slot derivation. ## Public & External Functions ### `verifier()` ```solidity theme={null} function verifier() public view returns (IVerifier) ``` Returns the address of the configured `Verifier` contract. It is retrieved from internal storage using a fixed slot. **Returns:** * `IVerifier` — The verifier contract associated with the module. ## Internal Functions ### `__VerifierModule_init(address verifier_)` ```solidity theme={null} function __VerifierModule_init(address verifier_) internal onlyInitializing ``` Initializes the verifier module with the given verifier contract address. Validates non-zero address to prevent misconfiguration. **Parameters:** * `verifier_` — Address of the verifier contract. **Reverts:** * `ZeroAddress()` if verifier address is zero. ## Private Functions ### `_verifierModuleStorage()` ```solidity theme={null} function _verifierModuleStorage() private view returns (VerifierModuleStorage storage) ``` Internal function to access the `VerifierModuleStorage` struct using the precomputed custom slot. Utilizes inline assembly for direct storage access. **Returns:** * `VerifierModuleStorage` — Storage struct holding verifier address. # Oracle Source: https://docs.mellow.finance/core-vaults/architecture/oracle #### Overview The `Oracle` contract is responsible for secure and configurable **price reporting** for supported assets. It is tightly coupled with a vault module (implementing `IShareModule`) and provides **price validation**, **deviation tracking**, and **rate-limited report submission**. It enforces strong assumptions around **data integrity**, **report timing**, and **trust minimization** through roles and deviation thresholds. This oracle ensures consistent pricing across all queue, share, and vault-related computations. #### Key Responsibilities * **Report Submission**: Allows trusted accounts to submit price updates * **Deviation Analysis**: Compares new prices against the last report for suspicious behavior * **Timestamp-based Rate Limiting**: Prevents frequent or premature reports * **Asset Management**: Controls which tokens are supported by the oracle * **Oracle Price Validation**: Used by other modules (e.g., `SignatureQueue`) to verify incoming prices #### Roles | Role | Description | | ------------------------------ | --------------------------------------------- | | `SUBMIT_REPORTS_ROLE` | Permission to submit regular price reports | | `ACCEPT_REPORT_ROLE` | Permission to accept suspicious reports | | `SET_SECURITY_PARAMS_ROLE` | Can modify validation rules and intervals | | `ADD_SUPPORTED_ASSETS_ROLE` | Can whitelist new assets for reporting | | `REMOVE_SUPPORTED_ASSETS_ROLE` | Can remove assets and delete associated state | #### **`SecurityParameters`** ```solidity theme={null} struct SecurityParams { uint224 maxAbsoluteDeviation; uint224 suspiciousAbsoluteDeviation; uint64 maxRelativeDeviationD18; uint64 suspiciousRelativeDeviationD18; uint32 timeout; uint32 depositInterval; uint32 redeemInterval; } ``` * **Absolute Deviation**: Hard limits on price delta in price units * **Relative Deviation**: Tolerance as a percentage (e.g., 5% = 0.05e18) * **Timeout**: Minimum time between valid reports (ignored if the previous report is suspicious) * **depositInterval**: Minimum age required for a deposit to be processed * **redeemInterval**: Same, but for redemptions #### **`Reports`** ```solidity theme={null} struct Report { address asset; uint224 priceD18; } struct DetailedReport { uint224 priceD18; uint32 timestamp; bool isSuspicious; } ``` Used to validate asset prices and coordinate cross-queue processing. #### Key Functions #### View | Function | Description | | -------------------------------- | ---------------------------------------------------------------------- | | `vault()` | Returns the linked vault (must implement `IShareModule`) | | `securityParams()` | Current oracle thresholds and intervals | | `supportedAssets()` | Count of whitelisted tokens | | `supportedAssetAt(index)` | Token at a given index | | `isSupportedAsset(address)` | Whether an asset is valid for reporting | | `getReport(asset)` | Returns last report (price, timestamp, suspicious flag) | | `validatePrice(priceD18, asset)` | Validates a given price against the current report and security params | #### Mutable | Function | Description | | --------------------------------------- | ----------------------------------------------- | | `initialize(params)` | Initializes with assets and security settings | | `setVault(vault)` | Registers the vault for report consumption | | `submitReports(reports[])` | Batch-submits prices for multiple assets | | `acceptReport(asset, price, timestamp)` | Marks a previously suspicious report as trusted | | `setSecurityParams(params)` | Updates thresholds and timing rules | | `addSupportedAssets(assets[])` | Adds tokens to the supported set | | `removeSupportedAssets(assets[])` | Removes tokens and clears their reports | #### Reporting Logic When calling `submitReports(...)`: 1. Each asset is checked for support 2. The previous report is evaluated: * If `timeout` has not passed, and the report is not suspicious → **revert** `TooEarly` 3. Price is compared against previous: * Too far off → **revert** `InvalidPrice` * Moderately off → flagged `isSuspicious` 4. If the report is accepted: * Triggers `vault.handleReport(...)` with adjusted deposit and redeem timestamps * Emits `ReportsSubmitted` #### Validation Logic Prices are validated by: * Calculating **absolute deviation** * Calculating **relative deviation** * Comparing against `max` and `suspicious` thresholds A price is: * **Rejected** if either deviation exceeds max * **Accepted but suspicious** if above suspicious threshold * **Accepted as normal** if within all limits Used by: * `SignatureDepositQueue`, `SignatureRedeemQueue`, `DepositQueue` and `RedeemQueue` contracts * Vault's limit accounting (RiskManager) #### Events | Event | Purpose | | ----------------------------------------- | ---------------------------------- | | `ReportsSubmitted(Report[])` | Emitted when new prices are posted | | `ReportAccepted(asset, price, timestamp)` | Suspicious report accepted | | `SecurityParamsSet(params)` | Oracle thresholds changed | | `SupportedAssetsAdded(addresses[])` | New tokens added | | `SupportedAssetsRemoved(addresses[])` | Tokens delisted | | `SetVault(address)` | Vault set | #### Security Considerations * Only trusted roles can push prices * Suspicious reports cannot be accepted without explicit approval * No pricing logic oracles trust external feeds — price validation is local * Prevents manipulation by enforcing absolute & relative deviation constraints # BitmaskVerifier Source: https://docs.mellow.finance/core-vaults/architecture/permissions/bitmaskverifier #### Purpose The `BitmaskVerifier` is a customizable, low-level verifier module that enables **selective call authorization** using **bitmask-based hashing**. It allows a contract to validate whether a function call (defined by `who`, `where`, `value`, and `data`) conforms to a pre-authorized pattern. It supports: * Partial matching of calldata * Exact or wildcard matching on sender, target, or ETH value * Highly gas-efficient verification with minimal storage #### Core Concept: Bitmask-Based Hashing The `BitmaskVerifier` computes a hash over masked components of a transaction and compares it to a stored or expected hash. The verification succeeds if: ```solidity theme={null} calculateHash(bitmask, who, where, value, data) == expectedHash ``` #### Bitmask Format The bitmask is a byte array with the following structure: | Segment | Bytes | Targeted Field | Description | | -------- | ------------- | -------------- | -------------------------------------------------------------- | | \[0:32] | 32 bytes | `who` | Mask for the caller address (left-padded to 32 bytes) | | \[32:64] | 32 bytes | `where` | Mask for the target contract address (left-padded to 32 bytes) | | \[64:96] | 32 bytes | `value` | Mask for ETH value (uint256) | | \[96:] | `data.length` | `data` | One byte per calldata byte; used to mask calldata selectively | This structure allows the verifier to: * Fully match addresses and value * Partially match calldata (e.g. permit `approve(x, anyAmount)`) #### Function: `calculateHash` ```solidity theme={null} function calculateHash( bytes calldata bitmask, address who, address where, uint256 value, bytes calldata data ) public pure returns (bytes32) ``` #### Logic This function computes a `keccak256` hash over the masked versions of each input field: 1. `who`, masked by `bitmask[0:32]` 2. `where`, masked by `bitmask[32:64]` 3. `value`, masked by `bitmask[64:96]` 4. Each `data[i]` masked by `bitmask[96+i]` #### Example Use If a bitmask has `0xff` for a given byte, that byte is strictly matched. If `0x00`, the byte is ignored (wildcarded). Mixed values allow partial matching. #### Function: `verifyCall` ```solidity theme={null} function verifyCall( address who, address where, uint256 value, bytes calldata data, bytes calldata verificationData ) public pure returns (bool) ``` #### Input: `verificationData` This input must be ABI-encoded as: ```solidity theme={null} abi.encode(bytes32 expectedHash, bytes bitmask) ``` #### Logic 1. Parses `expectedHash` and `bitmask` from the calldata 2. Verifies that the bitmask length matches `96 + data.length` * 32 for `who`, 32 for `where`, 32 for `value`, and one byte per calldata byte 3. Calls `calculateHash()` and compares it to `expectedHash` Returns: * `true` if the masked call hash matches the expected hash * `false` otherwise #### Use Cases This verifier enables granular control over contract interactions, for example: * **Approvals to a specific contract**: Allow `approve(farmContract, anyAmount)` but block other approvals. * **Partial calldata authorization**: Authorize only the first 4 bytes (function selector) of a call. * **Curated access for specific addresses**: Allow only specific curators to call `delegate(address)` with known targets. * **Value-bound actions**: Authorize only zero-ETH transactions or enforce a cap on `value`. # Consensus Source: https://docs.mellow.finance/core-vaults/architecture/permissions/consensus #### Purpose The `Consensus` contract manages a permissioned set of signers and enforces **multi-signature validation logic** using either EIP-712 or EIP-1271 signatures. It is a lightweight module designed for verifying **offchain consensus** before executing critical actions such as deposit and redemptions via SignatureQueues. It supports: * Threshold-based consensus * Two signature modes: EIP712 (EOA) and EIP1271 (contract-based) * Dynamic signer set management * Stateless, reusable verification interface #### Core Concepts #### Threshold-Based Verification To validate an action, a set of authorized signers must collectively submit signatures. The number of valid signatures must be **greater than or equal to** the configured `threshold`. #### Signature Types Each signer is associated with a `SignatureType`: * `EIP712` – Used for externally owned accounts (standard `ECDSA.recover`) * `EIP1271` – Used for contract accounts (via `isValidSignature()`) #### Storage Layout ```solidity theme={null} struct ConsensusStorage { uint256 threshold; EnumerableMap.AddressToUintMap signers; } ``` * `threshold`: Minimum number of valid signatures required for verification to succeed. * `signers`: Mapping of signer addresses → their configured signature type. #### Initialization ```solidity theme={null} function initialize(bytes calldata data) ``` * Expects `abi.encode(owner)` as input. * Sets the initial owner using `OwnableUpgradeable`. #### Signature Verification #### checkSignatures ```solidity theme={null} function checkSignatures(bytes32 orderHash, Signature[] calldata signatures) public view returns (bool) ``` * Returns `true` if: * At least `threshold` signatures are present * Each signature is: * From an authorized signer * Valid according to the signer’s configured signature type * Returns `false` otherwise Signature validation behavior: * `EIP712`: Uses `ECDSA.recover(orderHash, sig)` and matches signer * `EIP1271`: Calls `isValidSignature(orderHash, sig)` on the contract #### requireValidSignatures ```solidity theme={null} function requireValidSignatures(bytes32 orderHash, Signature[] calldata signatures) external view ``` * Same logic as `checkSignatures`, but reverts with `InvalidSignatures` error if validation fails #### Signer Management (Owner-only) #### setThreshold ```solidity theme={null} function setThreshold(uint256 threshold_) external onlyOwner ``` * Sets a new threshold * Must be `> 0` and `≤ signers.length()` * Emits `ThresholdSet` #### addSigner ```solidity theme={null} function addSigner(address signer, uint256 threshold_, SignatureType sigType) external onlyOwner ``` * Adds a new signer with specified signature type * Updates threshold (as part of signer addition) * Reverts if: * `signer == address(0)` * Signer already exists * Emits `SignerAdded` and `ThresholdSet` #### removeSigner ```solidity theme={null} function removeSigner(address signer, uint256 threshold_) external onlyOwner ``` * Removes signer from consensus set * Updates threshold * Reverts if signer not found * Emits `SignerRemoved` and `ThresholdSet` #### View Functions | Function | Returns | | ------------------- | ----------------------------------------- | | `threshold()` | Current consensus threshold | | `signers()` | Total number of signers | | `signerAt(uint256)` | Signer address and type at index | | `isSigner(address)` | Boolean indicating if address is a signer | #### Events * `Initialized(bytes)` * `ThresholdSet(uint256)` * `SignerAdded(address signer, SignatureType)` * `SignerRemoved(address signer)` * `InvalidSignatures(bytes32 hash, Signature[] signatures)` (used in revert) #### Security Considerations * Only the owner (via `OwnableUpgradeable`) may update signer set or threshold * Signatures are stateless and externally verifiable * Replay protection (e.g., nonce checks) must be handled by upstream systems (nonces) * Signers using `EIP1271` are trusted for contract logic – contracts must not be mutable without governance # Permissions Source: https://docs.mellow.finance/core-vaults/architecture/permissions/index In this directory, you will find a detailed per-contract overview of the "Permissions" contract category, including the following modules: \ \ [MellowACL](/core-vaults/architecture/permissions/mellowacl) [Verifier](/core-vaults/architecture/permissions/verifier) [BitmaskVerifier](/core-vaults/architecture/permissions/bitmaskverifier) [Consensus](/core-vaults/architecture/permissions/consensus) [protocols](/core-vaults/architecture/permissions/protocols) # MellowACL Source: https://docs.mellow.finance/core-vaults/architecture/permissions/mellowacl ### Purpose `MellowACL` is a lightweight but extendable access control layer that wraps OpenZeppelin’s `AccessControlEnumerableUpgradeable`. It introduces automatic tracking and enumeration of *active roles* to improve governance transparency. This contract is intended to be inherited by modules that require dynamic role management and storage-isolated initialization. ### Responsibilities * Grant and revoke access control roles to addresses * Keep track of all active (i.e., assigned) roles in a dedicated set * Expose enumerable functions for external auditing of granted roles * Emit structured events when roles are added or fully revoked ### Storage Layout ```solidity theme={null} struct MellowACLStorage { EnumerableSet.Bytes32Set supportedRoles; } ``` * `supportedRoles`: A unique set of role identifiers (`bytes32`) currently assigned to any address * Uses a dedicated storage slot derived from: ```solidity theme={null} SlotLibrary.getSlot("MellowACL", name_, version_) ``` ### View Functions ### `supportedRoles() → uint256` Returns the number of currently active roles (i.e., roles with at least one member). ### `supportedRoleAt(index: uint256) → bytes32` Returns the role identifier at the specified index from the active role set. ### `hasSupportedRole(role: bytes32) → bool` Returns `true` if the role is currently active (i.e., assigned to at least one account). ### Internal Logic ### `_grantRole(role: bytes32, account: address) → bool` Grants the specified role to an account. If the role was not previously active, it is added to `supportedRoles`, and `RoleAdded` is emitted. * Inherits from `AccessControlUpgradeable._grantRole` * Emits: ```solidity theme={null} event RoleAdded(bytes32 indexed role) ``` ### `_revokeRole(role: bytes32, account: address) → bool` Revokes the specified role from an account. If the role has no remaining members afterward, it is removed from `supportedRoles`, and `RoleRemoved` is emitted. * Inherits from `AccessControlUpgradeable._revokeRole` * Emits: ```solidity theme={null} event RoleRemoved(bytes32 indexed role) ``` ### Constructor ```solidity theme={null} constructor(string memory name_, uint256 version_) ``` * Computes a deterministic storage slot using `SlotLibrary` * Disables initializer to prevent accidental direct deployment * Should be initialized later via proxy-aware module constructor ### Events * `event RoleAdded(bytes32 indexed role)` * Emitted when a new role is introduced into the system * `event RoleRemoved(bytes32 indexed role)` * Emitted when the last holder of a role is revoked and the role becomes inactive # EigenLayerVerifier Source: https://docs.mellow.finance/core-vaults/architecture/permissions/protocols/eigenlayerverifier ### Overview `EigenLayerVerifier` is a custom `ICustomVerifier` implementation tailored to securely authorize calls to **EigenLayer** contracts like `DelegationManager`, `StrategyManager`, and `RewardsCoordinator`. It uses strict role-based gating, exact calldata matching, and entity-specific validation to ensure that only authorized vaults and bots can interact with EigenLayer staking, delegation, withdrawal, and rewards workflows. ### Purpose This verifier protects EigenLayer operations by: * Ensuring only whitelisted entities (vaults, strategies, operators) can execute actions * Verifying target contracts and function selectors precisely * Enforcing exact calldata encoding to eliminate any ambiguity or abuse ### Role Definitions | Role Constant | Description | | ------------------- | --------------------------------------------------------------------- | | `CALLER_ROLE` | Address allowed to initiate EigenLayer calls (typically curators) | | `ASSET_ROLE` | Whitelisted ERC20 token allowed in strategy deposits or withdrawals | | `STRATEGY_ROLE` | Whitelisted EigenLayer strategy contracts | | `OPERATOR_ROLE` | Approved EigenLayer operator address for delegation | | `MELLOW_VAULT_ROLE` | Whitelisted vaults acting as stakers or earners (usually `Subvault` ) | | `RECEIVER_ROLE` | Authorized receivers for claimed rewards | ### Constructor ```solidity theme={null} constructor(address delegationManager_, address strategyManager_, address rewardsCoordinator_, string memory name_, uint256 version_) ``` Initializes the verifier by: * Setting immutable references to EigenLayer’s: * `DelegationManager` * `StrategyManager` * `RewardsCoordinator` * Inheriting access control via `OwnedCustomVerifier` ### `verifyCall` ```solidity theme={null} function verifyCall( address who, address where, uint256 value, bytes calldata callData, bytes calldata /* verificationData */ ) external view override returns (bool) ``` ### General Preconditions * `who` must have `CALLER_ROLE` * `value` must be 0 (no ETH allowed) * `callData.length >= 4` (valid selector) ### Validated Targets & Selectors ### 1. **StrategyManager** – `depositIntoStrategy` * `depositIntoStrategy(IStrategy, address asset, uint256 shares)` * Strategy must have `STRATEGY_ROLE` * Asset must have `ASSET_ROLE` * Shares must be non-zero * Calldata must match ### 2. **DelegationManager** * **`delegateTo(address operator, SignatureWithExpiry signature, bytes32 salt)`** * Operator must have `OPERATOR_ROLE` * Calldata must match * **`queueWithdrawals(QueuedWithdrawalParams[] params)`** * Only **one** **`params.length == 1`** allowed * Param must include: * One strategy with `STRATEGY_ROLE` * One deposit share > 0 * Calldata must match * **`completeQueuedWithdrawal(Withdrawal, address[] tokens, bool receiveAsTokens)`** * `receiveAsTokens` must be `true` * Withdrawal must: * Have only one strategy with `STRATEGY_ROLE` * Have `staker` with `MELLOW_VAULT_ROLE` * `tokens.length == 1` and token must have `ASSET_ROLE` * Calldata must match ### 3. **RewardsCoordinator** – `processClaim` * **Selector:** `processClaim(RewardsMerkleClaim claimData, address receiver)` * **Checks:** * `claimData.earnerLeaf.earner` must have `MELLOW_VAULT_ROLE` * `receiver` must have `RECEIVER_ROLE` * Calldata must match ### Security Properties * **Role enforcement:** Prevents unauthorized usage of EigenLayer functions * **Exact calldata match:** Avoids incorrect encoding or maliciously crafted data * **Zero ETH transfers:** Disallows unexpected native token usage * **Single param enforcement (withdrawals):** Minimizes complexity and risk surface # ERC20Verifier Source: https://docs.mellow.finance/core-vaults/architecture/permissions/protocols/erc20verifier ### Overview `ERC20Verifier` is a role-driven `ICustomVerifier` implementation that enforces strict, granular permissioning over ERC20 `approve` and `transfer` function calls. It builds upon `OwnedCustomVerifier`, using `MellowACL`-style roles to validate the **caller**, **target asset**, and **recipient** of each operation. This verifier is designed for use in **modular vaults** such as `SubVault` where only specific ERC20 operations should be allowed through a customizable permission matrix. ### Purpose To allow or deny ERC20 `approve` and `transfer` calls based on: * The **caller** (must have `CALLER_ROLE`) * The **asset address** (must have `ASSET_ROLE`) * The **recipient** (must have `RECIPIENT_ROLE`) * Additionally: * `transfer` must not be for zero amount * `approve` allows any amount * `value` sent with the call must be `0` * Only exact calldata is accepted (no encoding variation or garbage data) ### Roles Each permission check is mapped to a distinct `bytes32` role: | Role Constant | Purpose | | ---------------- | --------------------------------------------------------------------------------- | | `ASSET_ROLE` | Marks which ERC20 tokens are allowed to be interacted with | | `CALLER_ROLE` | Who is allowed to perform `approve` or `transfer` | | `RECIPIENT_ROLE` | Who is allowed to receive tokens (for `transfer`) or get approval (for `approve`) | These roles are expected to be configured via the `initialize()` function inherited from `OwnedCustomVerifier`. ### Contract Behavior ### Constructor ```solidity theme={null} constructor(string memory name_, uint256 version_) ``` * Passes initialization parameters to `OwnedCustomVerifier` and disables further initializers ### `verifyCall` Function ```solidity theme={null} function verifyCall( address who, address where, uint256 value, bytes calldata callData, bytes calldata /* verificationData */ ) external view override returns (bool) ``` ### Summary: Checks if a specific ERC20 call is authorized. ### Logic Steps: 1. **Pre-checks**: * Must be a zero-ETH call: `value == 0` * Calldata must be exactly 68 bytes: 4-byte selector + 32 bytes address + 32 bytes uint * `where` (the token address) must have `ASSET_ROLE` * `who` (the caller, usually curator) must have `CALLER_ROLE` 2. **Selector Validation**: * Accepts only two ERC20 functions: * `approve(address,uint256)` * `transfer(address,uint256)` 3. **Recipient & Amount Validation**: * `to` address must have `RECIPIENT_ROLE` * For `transfer`: * `amount` must not be zero * `to` must not be zero address in any case 4. **Exact Calldata Matching**: * Ensures call is not forged via alternate encodings: ```solidity theme={null} keccak256(abi.encodeWithSelector(selector, to, amount)) == keccak256(callData) ``` ### Returns: * `true` if all checks pass * `false` otherwise ### Security Considerations * Prevents misuse of `approve` and `transfer` by enforcing: * Strict role-based gating * Zero ETH payload enforcement * Calldata normalization to eliminate encoding ambiguity * Ensures no contract or address receives funds or allowances without being explicitly whitelisted # Protocols Source: https://docs.mellow.finance/core-vaults/architecture/permissions/protocols/index In this directory, you will find a detailed overview of the specific verifiers, including the following: \ \ [OwnedCustomVerifier](/core-vaults/architecture/permissions/protocols/ownedcustomverifier) [ERC20Verifier](/core-vaults/architecture/permissions/protocols/erc20verifier) [SymbioticVerifier](/core-vaults/architecture/permissions/protocols/symbioticverifier) [EigenLayerVerifier](/core-vaults/architecture/permissions/protocols/eigenlayerverifier) # OwnedCustomVerifier Source: https://docs.mellow.finance/core-vaults/architecture/permissions/protocols/ownedcustomverifier ### Overview `OwnedCustomVerifier` is an **abstract base contract** for implementing `ICustomVerifier`-compatible verifiers with configurable role-based access control. It integrates with `MellowACL` and provides a flexible initialization mechanism for dynamic permission setup. This verifier is designed to be used in **`Verifier.sol`** as a custom verifier, \*\*\*\*where specific calls must pass access control checks based on predefined roles. ### Key Components ### Inherits: * `ICustomVerifier`: Interface used by the `Verifier` contract for permission checks * `MellowACL`: Upgradeable, role-based access control module compatible with OpenZeppelin’s `AccessControl` ### Constructor ```solidity theme={null} constructor(string memory name_, uint256 version_) MellowACL(name_, version_) ``` * Initializes the underlying `MellowACL` module with `name_` and `version_` * Disables further initialization to prevent misuse in logic contracts (`_disableInitializers()`) ### Initialization ```solidity theme={null} function initialize(bytes calldata data) external initializer ``` * Initializes access control roles * Decodes input as: ```solidity theme={null} (address admin, address[] memory holders, bytes32[] memory roles) ``` * Logic: * Sets `admin` as the contract’s `DEFAULT_ADMIN_ROLE` * Grants each `roles[i]` to `holders[i]` * Reverts with `ZeroValue` if: * `admin == address(0)` * Any holder is zero address * Any role is `DEFAULT_ADMIN_ROLE` ### Usage Pattern This base contract does **not** implement the `verifyCall()` method itself. Instead, it is expected to be **inherited and extended** by a concrete verifier contract that implements the permission logic based on role membership (e.g., checking `hasRole(role, who)`). This allows teams to quickly implement custom verifiers that enforce arbitrary permissions (e.g., allow certain addresses to `approve`, `transfer`, or `delegate`) based on **assigned roles** instead of hardcoded logic. # SymbioticVerifier Source: https://docs.mellow.finance/core-vaults/architecture/permissions/protocols/symbioticverifier ### Overview `SymbioticVerifier` is a custom `ICustomVerifier` implementation used to authorize interactions with the Symbiotic protocol. It restricts access to `deposit`, `withdraw`, `claim`, and `claimRewards` calls across Symbiotic vaults and farm contracts. All permissions are tightly scoped using role-based access control via `MellowACL`. This verifier ensures that only allowed addresses (typically curators) can perform specific actions within the Symbiotic ecosystem. ### Purpose The verifier ensures that: * Only whitelisted vaults can act on behalf of themselves in Symbiotic vaults and farms * All interactions are strictly validated against exact calldata to prevent misuse or encoding variation * Only allowed selectors and targets can be used ### Role Definitions | Role Constant | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------ | | `CALLER_ROLE` | Who is allowed to initiate Symbiotic operations (typically curators) | | `MELLOW_VAULT_ROLE` | Addresses that are allowed to be the recipient of deposits, withdrawals, or claims (usually Subvaults) | | `SYMBIOTIC_VAULT_ROLE` | Contracts that are approved as Symbiotic vault | | `SYMBIOTIC_FARM_ROLE` | Contracts that are approved as Symbiotic farm | ### Constructor ```solidity theme={null} constructor(address vaultFactory_, address farmFactory_, string memory name_, uint256 version_) ``` ### `verifyCall` ```solidity theme={null} function verifyCall( address who, address where, uint256 value, bytes calldata callData, bytes calldata /* verificationData */ ) public view returns (bool) ``` ### High-Level Behavior * Verifies caller (`who`) has `CALLER_ROLE` * Matches target contract (`where`) with either a Symbiotic vault or farm * Validates exact function selector and arguments using full `keccak256(callData)` hash * Rejects any calls with non-zero ETH value ### Supported Calls | Target Type | Function | Signature | Additional Checks | | --------------- | -------------------------------------- | ----------------------------------------------- | --------------------------------------------------------- | | Symbiotic Vault | `deposit(onBehalfOf, amount)` | `ISymbioticVault.deposit.selector` | `onBehalfOf` must have `MELLOW_VAULT_ROLE`, `amount > 0` | | Symbiotic Vault | `withdraw(claimer, amount)` | `ISymbioticVault.withdraw.selector` | `claimer` must have `MELLOW_VAULT_ROLE`, `amount > 0` | | Symbiotic Vault | `claim(recipient, epoch)` | `ISymbioticVault.claim.selector` | `recipient` must have `MELLOW_VAULT_ROLE` | | Symbiotic Farm | `claimRewards(recipient, token, data)` | `ISymbioticStakerRewards.claimRewards.selector` | `recipient` must have `MELLOW_VAULT_ROLE`, `token != 0x0` | * For all calls, the calldata must exactly match the selector and parameters * All other selectors or targets are denied ### Security Properties * **Strict call gating**: Only explicitly allowed selectors, targets, and roles pass * **Calldata hash check**: Enforces strict encoding to avoid alternate ABI variants or garbage data * **Zero-value enforcement**: Prevents accidental ETH transfers * **Factory pattern compatibility**: Target contracts can be validated indirectly via registries # Verifier Source: https://docs.mellow.finance/core-vaults/architecture/permissions/verifier ### Purpose The `Verifier` contract is a multi-mode permissioning module for verifying and enforcing call-level access control across vault-connected modules. It supports: * On-chain allowlists using hashed shortened calls (`CompactCall`) * Merkle tree-based validation for compact merkle, extended merkle and custom verifier verification types * Delegated verification logic through external custom verifiers (`ICustomVerifier`) This contract enables secure and modular delegation of operational permissions ### Core Responsibilities * Validates function calls from external actors (e.g., operators, curators) or strategy contracts (strategies) * Grants or revokes execution rights using on-chain and off-chain mechanisms * Ensures that only whitelisted or merkle-authenticated calls are allowed * Integrates with vault-based role system via `IAccessControl` ### Roles and Access * `SET_MERKLE_ROOT_ROLE`: Role allowed to update the active Merkle root * `CALLER_ROLE`: Role required by initiators of authorized calls * `ALLOW_CALL_ROLE`: Grants ability to add compact calls to allowlist * `DISALLOW_CALL_ROLE`: Grants ability to remove compact calls from allowlist ### Storage Layout ```solidity theme={null} struct VerifierStorage { address vault; bytes32 merkleRoot; EnumerableSet.Bytes32Set compactCallHashes; mapping(bytes32 => CompactCall) compactCalls; } ``` * `vault`: Vault contract that owns the verifier (must support `IAccessControl`) * `merkleRoot`: Merkle root for off-chain verified call proofs * `compactCallHashes`: Set of hashes representing allowed compact calls * `compactCalls`: Optional mapping for reverse lookup of call metadata by hash ### Verification Types ```solidity theme={null} enum VerificationType { ONCHAIN_COMPACT, MERKLE_COMPACT, MERKLE_EXTENDED, CUSTOM_VERIFIER } ``` * **ONCHAIN\_COMPACT**: Checks `CompactCall` (who | where | selector) hash against internal set * **MERKLE\_COMPACT**: Verifies Merkle proof of `CompactCall` (who | where | selector) hash * **MERKLE\_EXTENDED**: Verifies Merkle proof of `ExtendedCall` (who | where | value | callData) hash * **CUSTOM\_VERIFIER:** Delegates full verification to an external verifier ### Call Structures ```solidity theme={null} struct CompactCall { address who; address where; bytes4 selector; } struct ExtendedCall { address who; address where; uint256 value; bytes data; } struct VerificationPayload { VerificationType verificationType; bytes verificationData; bytes32[] proof; } ``` * `CompactCall`: Encodes permissioned call using address and selector * `ExtendedCall`: Encodes full call (selector + calldata + ETH value) * `VerificationPayload`: Contains verification metadata and proof ### View Functions * `vault()`: Returns the associated vault contract * `merkleRoot()`: Returns current Merkle root * `allowedCalls()`: Returns number of compact calls in allowlist * `allowedCallAt(index)`: Returns `CompactCall` at index from internal set * `isAllowedCall(who, where, callData)`: Checks if `CompactCall` is explicitly allowed * `hashCall(CompactCall)`: Returns keccak256 hash of compact call * `hashCall(ExtendedCall)`: Returns keccak256 hash of extended call ### Verification Functions ### `verifyCall(...)` ```solidity theme={null} function verifyCall( address who, address where, uint256 value, bytes calldata data, VerificationPayload calldata payload ) external view; ``` * Validates call permissions using the chosen `VerificationType` * Reverts with `VerificationFailed` on failure ### `getVerificationResult(...) → bool` ```solidity theme={null} function getVerificationResult( address who, address where, uint256 value, bytes calldata data, VerificationPayload calldata payload ) external view returns (bool); ``` * Returns `true` if the verification succeeds, `false` otherwise Verification decision logic: * `ONCHAIN_COMPACT`: Validate hash against stored allowlist * `MERKLE_COMPACT`: Validate Merkle proof of compact hash * `MERKLE_EXTENDED`: Validate Merkle proof of full hash * `CUSTOM_VERIFIER`: Validate Merkle proot of the verification payload and delegate validation to external contract ### Mutable Functions * `initialize(bytes calldata initParams)`: * Accepts `abi.encode(address vault_, bytes32 merkleRoot_)` * Sets the vault address and initial Merkle root * `setMerkleRoot(bytes32 root)`: * Updates Merkle root (requires `SET_MERKLE_ROOT_ROLE`) * `allowCalls(CompactCall[] calldata calls)`: * Adds compact calls to allowlist * Reverts on duplicates (calls already allowed) * `disallowCalls(CompactCall[] calldata calls)`: * Removes calls from allowlist * Reverts if call is not found in allowlist ### Initialization ```solidity theme={null} function initialize(bytes calldata initParams) external initializer; ``` * `initParams` format: `abi.encode(address vault_, bytes32 merkleRoot_)` # DepositQueue Source: https://docs.mellow.finance/core-vaults/architecture/queues/depositqueue ### Overview The `DepositQueue` contract enables asynchronous asset deposits into vaults using a time-delayed, oracle-priced queuing mechanism. Deposits are not processed immediately. Instead, users submit requests that are later fulfilled when an external price oracle submits a valid report. This enables batching (based on Fenwick Tree data structure), mitigates front-running, and facilitates accurate share pricing. ### Deposit Lifecycle ### Step 1: **User Deposits** * A user submits a deposit request via `deposit(assets, referral, merkleProof)`. * The deposited amount is stored as a `(timestamp, value)` checkpoint under `requestOf[msg.sender]`. * Each account can have **only one pending request** at a time. * Deposits are validated via optional Merkle whitelist logic (using `merkleProof`) or onchain mapping (if `flags.hasWhitelist()` returns true). * If a previous request exists, it must be claimed or canceled before creating a new one. ### Step 2: **Oracle Report** * Oracle submits a report via the `handleReport(priceD18, timestamp)` method. * The queue validates the report: * It must be called by the vault. * The timestamp must be in the past. * Price must be non-zero. * The queue handles deposit requests that are pending for longer than `depositInterval` seconds (the interval is specified in the oracle’s security params). * The contract stores the price in `prices` and uses it to convert accumulated assets to shares. * Converted shares are allocated but not minted yet. * Events: `ReportHandled` is emitted. ### Step 3: **User Claims** * A user calls `claim(account)` to mint and receive previously allocated shares. * The number of shares is computed as: ```solidity theme={null} uint256 shares = (request.assets * reducedByDepositFeePriceD18) / 1e18; ``` * Shares are minted to the user via `mintAllocatedShares`. ### Cancellation * A user may cancel a pending request using `cancelDepositRequest()`. * Cancellation reverts if the request has already become claimable (i.e., processed by an oracle report). * Refund is issued in the original asset amount. * Event: `DepositRequestCanceled` is emitted. ### Query Methods * `claimableOf(address account)`: Returns how many shares are currently claimable for a given user. * `requestOf(address account)`: Returns the `(timestamp, amount)` tuple of a user’s current pending request. ### Internal Mechanics The system tracks all deposit requests and prices using the following structures: * `Checkpoints.Trace224 prices`: Stores historical oracle-reported prices keyed by timestamp. * `mapping(address => Checkpoints.Checkpoint224) requestOf`: Maps users to their active deposit requests. * `FenwickTreeLibrary.Tree requests`: A prefix-sum data structure tracking asset totals across compressed timestamps. * `uint256 handledIndices`: Tracks the last fully processed request index, ensuring each oracle report progresses the queue. ## Scalability Challenge Vaults may face **thousands of deposit requests daily**. Processing each one individually leads to significant gas costs or even OOG. To optimize: > A Fenwick Tree (Binary Indexed Tree) is used to efficiently manage aggregate deposit data by timestamp. ### Fenwick Tree Mechanics * **On Deposit**: When a user deposits an amount `A` at time `T`, the system performs: `fenwickTree[T] += A` * **On Cancellation**: If the user cancels the request: `fenwickTree[T] -= A` * **On Oracle Report**: During report at `reportTimestamp`, the system calculates: ```solidity theme={null} fenwickTree.getSum(latestHandledTimestamp + 1, reportTimestamp - depositInterval) ``` This determines the **total amount** eligible for conversion into vault shares at the reported price. ### Lazy Propagation of Shares Rather than eagerly updating each user balance during `handleReport`, the vault employs **lazy propagation**: * Each user’s **claimable shares** are finalized only during subsequent calling `claim()`. * This significantly reduces processing cost during batch report execution. ### Timestamp Compression To minimize storage writes and reads used by `FenwickTree.sol` the system uses coordinate compression, storing only timestamps where actual deposit requests occurred. This compression strategy ensures that the Fenwick Tree remains compact, even with high-frequency usage. ### Key **Invariants** 1. **Single Active Request** Each user may have at most one unprocessed deposit request, user can not do new deposit till previous one is not claimed. 2. **Delayed Execution** Deposit processing requires an oracle report submitted after a configured `depositInterval`. 3. **Lazy Claiming** Deposits are converted to shares during oracle processing, but users must call `claim()` to receive them. 4. **Whitelist Enforcement** Deposits may require Merkle proof for depositor whitelisting. ### Events * `DepositRequested(address account, address referral, uint224 assets, uint32 timestamp)`: Emitted on new deposit submission. * `DepositRequestCanceled(address account, uint256 assets, uint32 timestamp)`: Emitted when a request is canceled and assets refunded. * `DepositRequestClaimed(address account, uint256 shares, uint32 timestamp)`: Emitted when deposit shares are successfully claimed. * `ReportHandled(uint224 priceD18, uint32 timestamp)`: Emitted when an oracle report is processed. ### Errors * `DepositNotAllowed()`: Depositor not whitelisted. * `PendingRequestExists()`: Existing request not yet processed or claimed. * `ClaimableRequestExists()`: Attempting to cancel after request has become claimable. * `NoPendingRequest()`: No existing request to cancel. * `ZeroValue()`: Input value is zero. * `InvalidReport()`: Oracle report failed validation. * `Forbidden()`: Unauthorized caller. * `QueuePaused()`: Deposits disabled via vault pause mechanism. # Queues Source: https://docs.mellow.finance/core-vaults/architecture/queues/index In this directory, you will find a detailed per-contract overview of the "Queues" contract category, including the following queues: [Queue](/core-vaults/architecture/queues/queue) [DepositQueue](/core-vaults/architecture/queues/depositqueue) [RedeemQueue](/core-vaults/architecture/queues/redeemqueue) [SignatureQueue](/core-vaults/architecture/queues/signaturequeue) [SignatureDepositQueue](/core-vaults/architecture/queues/signaturedepositqueue) [SignatureRedeemQueue](/core-vaults/architecture/queues/signatureredeemqueue) # Queue Source: https://docs.mellow.finance/core-vaults/architecture/queues/queue ### Overview The `Queue` contract provides a shared foundation for **time-gated asset processing** in systems like `DepositQueue` and `RedeemQueue`. It tracks user requests via timestamped checkpoints and processes them using oracle-based pricing. This abstract module is **not directly deployable** but is designed to be extended by concrete implementations, which define the behavior of `_handleReport` and allowed user actions (deposit / redeem functions). ### Purpose * Serves as a **modular base** for deposit/redeem queues * Enforces **Vault request processing initially triggered by Oracle** * Stores **timestamped user action traces** via `Checkpoints.Trace224` * Ensures **vault-controlled access** and proper **report validation** ### Derived contracts * `DepositQueue`: handles queued deposits after a delay * `RedeemQueue`: handles redemption requests similarly ### Use Cases * Prevents manipulation by requiring **delayed processing** relative to price updates ### Storage Structure ```solidity theme={null} struct QueueStorage { address asset; // Token/ETH managed by this queue address vault; // Vault that owns this queue Checkpoints.Trace224 timestamps; // Timeline of requests } ``` * **asset**: token used for this queue (ERC20 or native ETH) * **vault**: only this address can call `handleReport(...)` * **timestamps**: request history used in the implementations ### Initialization ```solidity theme={null} function __Queue_init(address asset_, address vault_) internal ``` * Must be called by child contracts * Initializes asset, vault, and creates a starting checkpoint ### Oracle Integration ```solidity theme={null} function handleReport(uint224 priceD18, uint32 timestamp) external ``` * Called by the vault when an oracle report is available * Verifies: * Caller is the `vault` * Price is non-zero * Timestamp is in the past (timestamp \< block.timestamp) * Internally delegates to `_handleReport(...)` (must be implemented by child) ### Abstract Hook ```solidity theme={null} function _handleReport(uint224 priceD18, uint32 timestamp) internal virtual ``` Must be implemented by child classes to: * Read and process requests from `_timestamps()` * Apply pricing logic to convert shares↔assets * Mint/burn shares, transfer tokens, etc. ### View Functions | Function | Description | | ---------------- | -------------------------------------------------- | | `vault()` | Returns the controlling vault address | | `asset()` | Returns the ERC20/native token used by the queue | | `canBeRemoved()` | Not implemented in `Queue` (optional for children) | ### Internal Helpers | Function | Description | | ------------------- | ----------------------------------------------------- | | `_timestamps()` | Returns the internal `Checkpoints.Trace224` structure | | `_queueStorage()` | Loads queue storage using custom storage slot | | `_queueStorageSlot` | Computed using SlotLibrary to prevent conflicts | ### Events ```solidity theme={null} event ReportHandled(uint224 priceD18, uint32 timestamp) ``` * Emitted when `handleReport()` completes * Signals that all eligible requests up to `timestamp` were processed ### Errors | Error | Reason | | ----------------- | ---------------------------------------------------- | | `ZeroValue()` | Called with `0` address or value | | `Forbidden()` | Caller not authorized (e.g., not the vault) | | `InvalidReport()` | Oracle report is zero-priced or timestamp is invalid | | `QueuePaused()` | Reserved for future ACL/pause integration | # RedeemQueue Source: https://docs.mellow.finance/core-vaults/architecture/queues/redeemqueue ### Purpose The `RedeemQueue` contract enables delayed, batched redemptions of vault shares into underlying assets. Redemptions are processed in two phases: 1. **Oracle pricing** – Shares are priced via a trusted price report. 2. **Liquidity settlement** – Vault liquidity is allocated to fulfill priced requests. This separation supports asynchronous liquidity management, gas efficiency, and protection against griefing. ### Overview The `RedeemQueue` enables users to convert their vault shares into underlying assets, introducing a **time delay** enforced by an oracle-defined `redeemInterval`. It maintains the following core invariants: 1. **Request Format**: Each request is structured as a `(shares, timestamp)` pair. 2. **Non-Cancellable**: Redemption requests **cannot** be cancelled to prevent griefing (e.g., submitting and canceling after unstaking starts). 3. **Multiple Requests Allowed**: Users may submit multiple independent redemption requests. Upon receiving an oracle report at `reportTimestamp`, the system processes all requests with: ```solidity theme={null} timestamp <= reportTimestamp - redeemInterval ``` On the next step these vault shares are converted to assets at the price specified in the oracle report in this step. ### Liquidity Processing (Two-Stage) To enable flexible and asynchronous liquidity management, redemption is handled in **two distinct phases**: 1. **Post-Request**: Vault curators monitor and, if needed, pull liquidity from external protocols. 2. **Post-Oracle Report**: * Once a valid report is submitted and sufficient liquidity is available, * The vault curator (or any other trusted actor) invokes `handleBatches(n)` on the `RedeemQueue`, * This action triggers the movement of required assets from the vault (and associated subvaults) to process redemption requests. ### Scalability Approach Unlike deposits, **redemption requests are never cancelled**, which allows for a simplified and gas-efficient tracking model: A prefix sum array is used to efficiently manage cumulative share redemptions over time. ### Redemption Processing Logic * **On Redemption**: When a user redeems `amount` of shares at time `T`, the system logs: `prefixSum[T] += amount` * **On Oracle Report**: At `reportTimestamp`, all requests with: ```solidity theme={null} timestamp <= reportTimestamp - redeemInterval ``` are marked as processed. * **Post-Processing**: * The curator ensures the necessary asset liquidity is available, * Then calls `handleBatches()` to finalize processing. * **User Claim**: After requests are processed, users can call: ```solidity theme={null} claim(receiver, timestamps[]) ``` to claim assets for their requested vault shares corresponding to each processed timestamp. ### Storage Layout All internal state is maintained in `RedeemQueueStorage`, including: | Field | Description | | -------------------- | ----------------------------------------------------------------------- | | `handledIndices` | Tracks number of oracle checkpoints that have been priced | | `batchIterator` | Index of the next unfulfilled batch | | `totalDemandAssets` | Total asset amount needed to fulfill pending batches | | `totalPendingShares` | Total shares in requests that are not yet claimable | | `requestsOf` | Maps `address → (timestamp → shares)` for pending user requests | | `prefixSum` | Maps `timestamp index → shares` for batch creation and summation | | `batches` | Array of `Batch` structs; each batch tracks fulfilled assets and shares | | `prices` | Oracle-reported price checkpoints, indexed by timestamp | ### Structs ### `Request` Represents a single redemption request from a user: * `timestamp`: When the request was submitted. * `shares`: Amount of vault shares being redeemed. * `isClaimable`: Set to true after batch is fulfilled. * `assets`: Amount of assets claimable for this request (set after pricing). ### `Batch` Represents a priced redemption batch: * `assets`: Total value fulfilled for the batch (via oracle `shares / report.price`). * `shares`: Total shares matched in this batch. ### View Functions ### `requestsOf(account, offset, limit)` Returns paginated redemption request data for the specified account. Each request includes: * Timestamp * Shares * Claimable status * Asset amount ### `batchAt(index)` Returns the `(assets, shares)` for a given redemption batch. ### `getState()` Returns core system state: * Current `batchIterator` (next unfulfilled batch index) * Total `batches` * Total `demandedAssets` still awaiting liquidity * Total `pendingShares` that are not yet claimable ### State Transition Guarantees 1. **Non-Cancellable Requests** * Prevents griefing where a user requests redemption, causing curator to pull liquidity, then cancels. 2. **Price Separation** * Oracle reports must be delayed by at least`redeemInterval` seconds from the original request. 3. **Asynchronous Fulfillment** * Liquidity can be managed independently of oracle report submission. ### Events * `RedeemRequested(account, shares, timestamp)` * `RedeemRequestClaimed(account, receiver, assets, timestamp)` * `RedeemRequestsHandled(counter, demand)` # SignatureDepositQueue Source: https://docs.mellow.finance/core-vaults/architecture/queues/signaturedepositqueue ### Purpose `SignatureDepositQueue` extends `SignatureQueue` to enable **instant deposit** of assets into a vault, bypassing the standard on-chain `DepositQueue` mechanism. It leverages **off-chain approvals** signed by a trusted consensus group, using **EIP-712** or **EIP-1271**-compliant signatures, to authorize asset inflows and minting of vault shares. This contract is optimized for high-trust environments requiring immediate asset onboarding while maintaining on-chain price safety guarantees. ### Key Features * **Instant deposit execution** with no queuing delay * **EIP-712 signed orders** with nonce-based replay protection * **Vault share minting** at off-chain pre-agreed price * **Fully integrated with vault accounting and share manager** * **No deposit fee** applied (unlike possible fees in `DepositQueue`) ### Workflow 1. A consensus group signs an `Order` authorizing a user deposit: * Includes asset amount (`ordered`) and shares to mint (`requested`) * Binds the request to a specific queue and vault * Includes a nonce and expiration timestamp 2. User submits the order on-chain by calling `deposit` function: * The contract validates the order using signatures and price logic * Receives tokens from the user * Transfers these tokens to the vault * Mints shares to the specified recipient * Updates vault internal balance and executes post-deposit hook ### Function: `deposit` ```solidity theme={null} function deposit(Order calldata order, IConsensus.Signature[] calldata signatures) external payable nonReentrant ``` ### Parameters: * `order`: A signed `Order` struct including deposit parameters * `signatures`: Signatures from the off-chain consensus validating the order ### Steps: 1. `validateOrder(...)`: * Confirms order is not expired * Confirms order is intended for this queue * Confirms correct asset, caller, and nonce * Validates off-chain signatures * Computes and validates asset/share price using vault oracle 2. Increments the caller’s nonce to prevent replay 3. Transfers `order.ordered` assets from the caller to this contract 4. Transfers these assets into the vault 5. Calls `vault.callHook(...)` for any optional strategy logic 6. Notifies the vault's `RiskManager` of the new deposit 7. Mints `order.requested` shares to `order.recipient` 8. Emits `OrderExecuted` event ### Security Considerations * **Consensus signatures** are required to prevent unauthorized deposits * **Oracle validation** ensures price sanity even in trusted setups * **Replay protection** enforced using per-user nonces * No deposit can proceed if: * Asset or queue mismatch * Caller mismatch * Nonce is reused * Off-chain price is out of oracle bounds # SignatureQueue Source: https://docs.mellow.finance/core-vaults/architecture/queues/signaturequeue ### Purpose The `SignatureQueue` enables **instant** user deposits or redemptions using **off-chain signed approvals** from a trusted consensus group. This queue type bypasses the normal time-delayed queuing mechanism (e.g., `DepositQueue`, `RedeemQueue`) by verifying orders via **EIP-712** or **EIP-1271** signatures, offering fast-lane access for users while preserving oracle-bound price safety. ### Key Features * **Instant execution** without waiting for oracle price reports * **Nonce-based signature protection** to prevent replay attacks * **EIP-712/EIP-1271 compatible** signed orders * **Oracle price validation** enforced on-chain * **Stateless and removable** (i.e., does not accumulate shares or process claims) * **Fee-bypassed**: No `depositFee` or `redeemFee` is charged for actions via this queue ### High-Level Workflow 1. Off-chain consensus actors (e.g., operators, curators, admins) generate signed `Order` messages. 2. A user submits this order to the `SignatureQueue` contract for execution. 3. The queue: * Verifies the order signature * Validates nonce, queue address, asset match, deadline, and caller * Computes the implied asset/share price and checks it against the vault's oracle 4. If all checks pass, the order is executed atomically. ### Order Structure Each order encapsulates all the necessary metadata for verification: ```solidity theme={null} struct Order { uint256 orderId; // Off-chain tracking ID address queue; // Must match queue address address asset; // Token involved in deposit/redeem address caller; // Must match msg.sender address recipient; // Recipient of assets or shares uint256 ordered; // Assets in (deposit) or shares out (redeem) uint256 requested; // Shares out (deposit) or assets in (redeem) uint256 deadline; // Expiration timestamp uint256 nonce; // Unique per caller } ``` ### Signature Validation * Orders are signed by a quorum of validators registered in the `Consensus` contract. * Signatures can conform to: * **EIP-712** (structured message hashing) * **EIP-1271** (smart contract-based signature schemes) The order hash is computed via: ```solidity theme={null} keccak256( abi.encode( ORDER_TYPEHASH, order.orderId, order.queue, order.asset, order.caller, order.recipient, order.ordered, order.requested, order.deadline, order.nonce ) ); ``` ### Price Safety Check After signature verification, `SignatureQueue` uses the vault’s `Oracle` to validate the price: * **Deposits**: `price = requestedShares / depositedAssets` * **Redemptions**: `price = burnedShares / redeemedAssets` * Oracle must confirm: * Price is within allowed bounds * Price is not marked as suspicious Otherwise, the operation is rejected with `InvalidPrice`. ### Storage Layout ```solidity theme={null} struct SignatureQueueStorage { address consensus; // Signature validator contract address vault; // Parent vault (Vault.sol) address asset; // Supported ERC20 token or native ETH mapping(address => uint256) nonces; // Per-user nonces } ``` ### Interface Compatibility Despite not using claimable balances, `SignatureQueue` implements stub methods for compatibility with the `IQueue` interface: * `claimableOf(...) → 0` * `claim(...) → false` * `handleReport(...)`: no-op * `canBeRemoved() → true`: confirms it has no persistent state ### Events ```solidity theme={null} event OrderExecuted(Order order, IConsensus.Signature[] signatures); ``` Emitted after a signed order is successfully executed. ### Security Assumptions * Only **trusted off-chain actors** (consensus group) are authorized to sign orders. * Price quotes must match oracle-defined asset/share rates. * Users cannot reuse old signatures due to nonce tracking. * Orders must be executed before `deadline`. ### Use Cases * **Instant UX**: bypassing delays in `DepositQueue` or `RedeemQueue` * **Institutional integrations**: where trusted relayers or coordinators pre-sign valid actions * **Fallback mechanism**: during oracle lags or downtime ### Limitations * Does not charge fees (unlike time-delayed queues) * Requires trusted off-chain actors * Less decentralized if consensus actors are not well-audited or rotated # SignatureRedeemQueue Source: https://docs.mellow.finance/core-vaults/architecture/queues/signatureredeemqueue ### Purpose `SignatureRedeemQueue` extends `SignatureQueue` to enable **instant share redemption** from a vault without the usual delay of on-chain oracle processing. It leverages **off-chain consensus signatures** conforming to EIP-712 or EIP-1271 to authorize redemptions, allowing trusted users to convert shares to assets in a fast and secure manner. This module provides a **low-latency redemption path** under stronger trust assumptions, useful in environments where responsiveness is critical and participants are whitelisted by a governance consensus. ### Key Features * Off-chain authorized redemptions using signed `Order` messages * Oracle-bound price validation to prevent manipulation * EIP-712 structured data signature verification * Direct burning of shares and asset pulling from the vault and payout * Nonce-based replay protection * Vault hook execution and balance tracking * **No redeem fee** applied (unlike possible fees in `RedeemQueue`) ### Function: `redeem` ```solidity theme={null} function redeem(Order calldata order, IConsensus.Signature[] calldata signatures) external payable nonReentrant ``` ### Parameters: * `order`: A signed redemption `Order` struct specifying asset amount and recipient * `signatures`: Validator signatures from the consensus group ### Workflow 1. **Validation** via `validateOrder(...)`: * Signature freshness (`deadline`) * Queue and asset correctness * Caller authenticity and correct nonce * Signatures validated by registered `Consensus` contract * Price computed from `ordered` and `requested` values and verified via oracle 2. **Nonce incremented** for the caller to prevent signature reuse 3. **Vault liquid asset check**: * Ensures enough liquidity is available for the redemption * Reverts with `InsufficientAssets` if funds are lacking 4. **Redemption Processing**: * Burns `order.ordered` shares from the user via `shareManager` * Calls `vault.callHook(...)` for any strategy exit logic * Transfers `order.requested` assets to the user * Updates internal vault balance via the `RiskManager` 5. **Event Emitted**: * `OrderExecuted(order, signatures)` confirms successful execution ### Error: `InsufficientAssets` ```solidity theme={null} error InsufficientAssets(uint256 requested, uint256 available); ``` Thrown when the vault does not have enough liquid assets to fulfill the request. Ensures safety during instantaneous exits. # Vaults Source: https://docs.mellow.finance/core-vaults/architecture/vaults/index In this directory, you will find a detailed per-contract overview of the "Vaults" contract category, including the following contracts: [Vault](/core-vaults/architecture/vaults/vault) [Subvault](/core-vaults/architecture/vaults/subvault) [VaultConfigurator](/core-vaults/architecture/vaults/vaultconfigurator) # Subvault Source: https://docs.mellow.finance/core-vaults/architecture/vaults/subvault ## Overview The `Subvault` contract represents a modular, permissioned vault component designed to manage delegated asset strategies within a parent `Vault`. It enables curated logic for permissioned calls and asset management without exposing external deposit or redemption interfaces. This contract combines callable and verifiable logic to serve as a secure, controlled execution unit within a system. ## Inheritance Structure ```solidity theme={null} contract Subvault is IFactoryEntity, CallModule, SubvaultModule ``` The `Subvault` inherits: * `CallModule`: Enables arbitrary low-level calls to external contracts (used by curator of the vault), and verification through a verifier module. * `SubvaultModule`: Handles vault linkage and liquidity handling. * `IFactoryEntity`: Standard initialization interface for factory deployment compatibility. The constructor explicitly calls: ```solidity theme={null} VerifierModule(name_, version_) SubvaultModule(name_, version_) ``` This indicates that both modules rely on deterministic storage and versioned deployment identifiers via `SlotLibrary`. ## Constructor ```solidity theme={null} constructor(string memory name_, uint256 version_) ``` ### Parameters: * `name_`: A unique string identifier for the deployment (e.g., “Mellow”). * `version_`: A version number used to derive storage slots and allow upgradeable logic. ### Behavior: Passes the `name_` and `version_` arguments into the constructors of `VerifierModule` and `SubvaultModule`. ## External Functions ### `initialize` ```solidity theme={null} function initialize(bytes calldata initParams) external initializer ``` Initializes the subvault contract. This function can only be called once due to the `initializer` modifier. ### Parameters: * `initParams`: ABI-encoded as `(address verifier_, address vault_)` ### Initialization Steps: 1. Decodes `verifier_` and `vault_` from the calldata. 2. Calls `__VerifierModule_init(verifier_)` to link the external verifier (used for strategy proof or access control). 3. Calls `__SubvaultModule_init(vault_)` to register this subvault with the parent vault. 4. Emits the `Initialized(initParams)` event for transparency. ## Design Notes * **Modular Strategy Execution**: The `CallModule` enables arbitrary external calls, useful for delegating assets into other protocols or yield strategies. * **Trust-Minimized Calls**: External strategy actions are gated via a `VerifierModule`, which can enforce logic like off-chain signatures or time-based constraints. * **Parent Vault Registration**: Initialization ensures the `vault` address is securely set once and governs access and lifecycle. * **Upgradeable Architecture**: Follows the shared pattern of using deterministic storage slots (via `SlotLibrary`) to remain safely upgradeable and composable. ## Events ### `Initialized(bytes data)` Emitted once after a successful `initialize` call. Contains the raw ABI-encoded input for auditing or debugging. # Vault Source: https://docs.mellow.finance/core-vaults/architecture/vaults/vault ## Overview The `Vault` contract is the central entry point in the Flexible Vault system. It composes three foundational modules: * `ACLModule`: Role-based access control. * `ShareModule`: Management of user-facing shares, including deposit and redemption processes. * `VaultModule`: Subvault management. This contract allows secure, extensible, and upgradeable vault implementations by coordinating all external and internal interactions within the system. It is typically instantiated through `Factory` or `VaultConfigurator` and initialized with all the required components and role assignments in a single atomic transaction. ## Inheritance Structure ```solidity theme={null} contract Vault is IFactoryEntity, VaultModule, ShareModule, ACLModule ``` The contract inherits three modules: * `ACLModule`: Admin and permission management * `ShareModule`: Deposits, redemptions, share management * `VaultModule`: Subvault delegation control It also implements the `IFactoryEntity` interface for standard factory-based deployment patterns. ## Constructor ```solidity theme={null} constructor( string memory name_, uint256 version_, address depositQueueFactory_, address redeemQueueFactory_, address subvaultFactory_, address verifierFactory_ ) ``` ### Parameters: * `name_`: Unique name identifier for the vault instance * `version_`: Configuration version of the vault * `depositQueueFactory_`: Address of the factory used to deploy deposit queues * `redeemQueueFactory_`: Address of the factory used to deploy redemption queues * `subvaultFactory_`: Address of the factory used to deploy subvaults * `verifierFactory_`: Address of the factory for deploying verifier contracts ### Behavior: Passes these arguments to the parent module constructors: * `ACLModule(name_, version_)` * `ShareModule(name_, version_, depositQueueFactory_, redeemQueueFactory_)` * `VaultModule(name_, version_, subvaultFactory_, verifierFactory_)` ## Structs ### `RoleHolder` ```solidity theme={null} struct RoleHolder { bytes32 role; address holder; } ``` Used to batch-assign multiple roles during initialization. Each entry maps a role identifier to a designated address. ## External Functions ### `initialize` ```solidity theme={null} function initialize(bytes calldata initParams) external initializer ``` Initializes the vault instance. Can only be called once due to the `initializer` modifier. ### `initParams` structure (ABI-encoded): ```solidity theme={null} ( address admin_, address shareManager_, address feeManager_, address riskManager_, address oracle_, address defaultDepositHook_, address defaultRedeemHook_, uint256 queueLimit_, RoleHolder[] roleHolders ) ``` ### Initialization Logic: * Calls `__ACLModule_init(admin_)` to configure the default admin. * Calls `__ShareModule_init(...)` to link share management and hook modules. * Calls `__VaultModule_init(riskManager_)` to initialize risk management. * Iterates over `roleHolders` and grants each role using `_grantRole(...)`. * Emits `Initialized(initParams)`. ## Design Notes * **Modular Composition**: The vault is composed by inheriting three upgradeable modules, enabling reuse and flexible configuration. * **Factory-Compatible**: The contract is factory-deployable and supports atomic configuration during creation. * **Centralized Control Layer**: Acts as a trusted coordinator for hooks, queues, shares, and strategy logic. * **Role Assignment**: Enables full delegation of operational control via batched `RoleHolder` entries. * **Upgradeable and Isolated**: Each module manages its own storage via deterministic slots (`SlotLibrary`) to support safe upgrades. ## Events ### `Initialized(bytes data)` Emitted after successful initialization. Includes all parameters passed for transparency. # VaultConfigurator Source: https://docs.mellow.finance/core-vaults/architecture/vaults/vaultconfigurator ## Overview The `VaultConfigurator` contract provides a streamlined and modular deployment mechanism for setting up a new `Vault` instance and its associated managers. It orchestrates the creation and initialization of the following components: * `Vault` * `ShareManager` * `FeeManager` * `RiskManager` * `Oracle` It ensures that all components are correctly wired together by setting appropriate references between them. ## Purpose This contract is designed to be used by an actor that need to deploy and configure fully functional vaults in a deterministic and upgradeable way, using versioned module factories. ## Contract Structure ### State Variables ```solidity theme={null} IFactory public immutable shareManagerFactory; IFactory public immutable feeManagerFactory; IFactory public immutable riskManagerFactory; IFactory public immutable oracleFactory; IFactory public immutable vaultFactory; ``` Each of these holds a reference to a factory contract responsible for creating a specific type of contract. ### Constructor ```solidity theme={null} constructor( address shareManagerFactory_, address feeManagerFactory_, address riskManagerFactory_, address oracleFactory_, address vaultFactory_ ) ``` Initializes the configurator with references to module factories. ## InitParams Struct ```solidity theme={null} struct InitParams { uint256 version; address proxyAdmin; address vaultAdmin; uint256 shareManagerVersion; bytes shareManagerParams; uint256 feeManagerVersion; bytes feeManagerParams; uint256 riskManagerVersion; bytes riskManagerParams; uint256 oracleVersion; bytes oracleParams; address defaultDepositHook; address defaultRedeemHook; uint256 queueLimit; Vault.RoleHolder[] roleHolders; } ``` ### Fields: * `version`: Version of the `Vault` implementation to deploy. * `proxyAdmin`: Address to be set as `ProxyAdmin` for upgradeable proxies. * `vaultAdmin`: Address to be set as the vault's owner (admin). * `_Version`: Specific implementation version to use for each module (used in the corresponding factory). * `_Params`: ABI-encoded initialization parameters for each module. * `defaultDepositHook`: Address of the default deposit hook to attach to queues. * `defaultRedeemHook`: Address of the default redeem hook to attach to queues. * `queueLimit`: Maximum number of queued operations per deposit/redeem queue. * `roleHolders`: List of role assignments for vault-level access control. ## External Functions ### `create` ```solidity theme={null} function create(InitParams calldata params) external returns ( address shareManager, address feeManager, address riskManager, address oracle, address vault ) ``` ### Description: Creates and initializes a new vault instance along with all dependent modules using the provided factory addresses and parameters. ### Steps: 1. **Deploy ShareManager**: * Uses `shareManagerFactory` to deploy a versioned `ShareManager` proxy. 2. **Deploy FeeManager**: * Uses `feeManagerFactory` to deploy a versioned `FeeManager`. 3. **Deploy RiskManager**: * Uses `riskManagerFactory` to deploy a versioned `RiskManager`. 4. **Deploy Oracle**: * Uses `oracleFactory` to deploy a versioned `Oracle`. 5. **Deploy Vault**: * Prepares encoded initialization calldata and calls `vaultFactory.create()` with the version and proxy admin. 6. **Post-deployment Wiring**: * Sets the `vault` address in each of the deployed components using: * `IShareManager(shareManager).setVault(vault)` * `IRiskManager(riskManager).setVault(vault)` * `IOracle(oracle).setVault(vault)` ### Returns: * `shareManager`: Address of the deployed share manager contract * `feeManager`: Address of the deployed fee manager contract * `riskManager`: Address of the deployed risk manager contract * `oracle`: Address of the deployed oracle contract * `vault`: Address of the newly created vault # Core Deployments Source: https://docs.mellow.finance/core-vaults/core-deployments Contract addresses for all Core Vault implementations and factories across all chains with implementations and factories. | Factory implementation | [0x0000000397b71C8f3182Fd40D247330D218fdC72](https://etherscan.io/address/0x0000000397b71C8f3182Fd40D247330D218fdC72) | | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | | Factory factory | [0x0000000f9686896836C39cf721141922Ce42639f](https://etherscan.io/address/0x0000000f9686896836C39cf721141922Ce42639f) | | Consensus factory | [0xaEEB06CBd91A18b51a2D30b61477eAeE3a9633C3](https://etherscan.io/address/0xaEEB06CBd91A18b51a2D30b61477eAeE3a9633C3) | | Consensus implementation | [0x0000000167598d2C78E2313fD5328E16bD9A0b13](https://etherscan.io/address/0x0000000167598d2C78E2313fD5328E16bD9A0b13) | | DepositQueue factory | [0xBB92A7B9695750e1234BaB18F83b73686dd09854](https://etherscan.io/address/0xBB92A7B9695750e1234BaB18F83b73686dd09854) | | DepositQueue implementation | [0x00000006dA9f179BFE250Dd1c51cD2d3581930c8](https://etherscan.io/address/0x00000006dA9f179BFE250Dd1c51cD2d3581930c8) | | SyncDepositQueue implementation | [0x000000000b98f77a017b5d3468400c5C597a3Bde](https://etherscan.io/address/0x000000000b98f77a017b5d3468400c5C597a3Bde) | | SignatureDepositQueue implementation | [0x00000003887dfBCEbD1e4097Ad89B690de7eFbf9](https://etherscan.io/address/0x00000003887dfBCEbD1e4097Ad89B690de7eFbf9) | | FeeManager factory | [0xF7223356819Ea48f25880b6c2ab3e907CC336D45](https://etherscan.io/address/0xF7223356819Ea48f25880b6c2ab3e907CC336D45) | | FeeManager implementation | [0x0000000dE74e5D51651326E0A3e1ACA94bEAF6E1](https://etherscan.io/address/0x0000000dE74e5D51651326E0A3e1ACA94bEAF6E1) | | Oracle factory | [0x0CdFf250C7a071fdc72340D820C5C8e29507Aaad](https://etherscan.io/address/0x0CdFf250C7a071fdc72340D820C5C8e29507Aaad) | | Oracle implementation | [0x0000000F0d3D1c31b72368366A4049C05E291D58](https://etherscan.io/address/0x0000000F0d3D1c31b72368366A4049C05E291D58) | | RedeemQueue factory | [0xfe76b5fd238553D65Ce6dd0A572C0fda629F8421](https://etherscan.io/address/0xfe76b5fd238553D65Ce6dd0A572C0fda629F8421) | | RedeemQueue implementation | [0x000000000c139266BA06170Ed1DeacA6d11903c1](https://etherscan.io/address/0x000000000c139266BA06170Ed1DeacA6d11903c1) | | SignatureRedeemQueue implementation | [0x0000000b2082667589A16c4cF18e9f923781c471](https://etherscan.io/address/0x0000000b2082667589A16c4cF18e9f923781c471) | | RiskManager factory | [0xa51E4FA916b939Fa451520D2B7600c740d86E5A0](https://etherscan.io/address/0xa51E4FA916b939Fa451520D2B7600c740d86E5A0) | | RiskManager implementation | [0x0000000714cf2851baC1AE2f41871862e9D216fD](https://etherscan.io/address/0x0000000714cf2851baC1AE2f41871862e9D216fD) | | ShareManager factory | [0x952f39AA62E94db3Ad0d1C7D1E43C1a8519E45D8](https://etherscan.io/address/0x952f39AA62E94db3Ad0d1C7D1E43C1a8519E45D8) | | TokenizedShareManager implementation | [0x0000000E8eb7173fA1a3ba60eCA325bcB6aaf378](https://etherscan.io/address/0x0000000E8eb7173fA1a3ba60eCA325bcB6aaf378) | | BasicShareManager implementation | [0x00000005564AAE40D88e2F08dA71CBe156767977](https://etherscan.io/address/0x00000005564AAE40D88e2F08dA71CBe156767977) | | Subvault factory | [0x75FE0d73d3C64cdC1C6449D9F977Be6857c4d011](https://etherscan.io/address/0x75FE0d73d3C64cdC1C6449D9F977Be6857c4d011) | | Subvault implementation | [0x0000000E535B4E063f8372933A55470e67910a66](https://etherscan.io/address/0x0000000E535B4E063f8372933A55470e67910a66) | | Verifier factory | [0x04B30b1e98950e6A13550d84e991bE0d734C2c61](https://etherscan.io/address/0x04B30b1e98950e6A13550d84e991bE0d734C2c61) | | Verifier implementation | [0x000000047Fc878662006E78D5174FB4285637966](https://etherscan.io/address/0x000000047Fc878662006E78D5174FB4285637966) | | Vault factory | [0x4E38F679e46B3216f0bd4B314E9C429AFfB1dEE3](https://etherscan.io/address/0x4E38F679e46B3216f0bd4B314E9C429AFfB1dEE3) | | Vault implementation | [0x0000000615B2771511dAa693aC07BE5622869E01](https://etherscan.io/address/0x0000000615B2771511dAa693aC07BE5622869E01) | | SwapModule Factory | [0xE3575055a24d8642DFA3a51ec766Ef2db2671659](https://etherscan.io/address/0xE3575055a24d8642DFA3a51ec766Ef2db2671659) | | SwapModule implementation | [0x00000000d681E85e5783588f87A9573Cb97Eda01](https://etherscan.io/address/0x00000000d681E85e5783588f87A9573Cb97Eda01) | | BitmaskVerifier | [0x0000000263Fb29C3D6B0C5837883519eF05ea20A](https://etherscan.io/address/0x0000000263Fb29C3D6B0C5837883519eF05ea20A) | | EigenLayerVerifier factory | [0x77A83AcBf7A6df20f1D681b4810437d74AE790F8](https://etherscan.io/address/0x77A83AcBf7A6df20f1D681b4810437d74AE790F8) | | EigenLayerVerifier | [0x00000003F82051A8B2F020B79e94C3DC94E89B81](https://etherscan.io/address/0x00000003F82051A8B2F020B79e94C3DC94E89B81) | | ERC20Verifier factory | [0x2e234F4E1b7934d5F4bEAE3fF2FDC109f5C42F1d](https://etherscan.io/address/0x2e234F4E1b7934d5F4bEAE3fF2FDC109f5C42F1d) | | ERC20Verifier | [0x00000009207D366cBB8549837F8Ae4bf800Af2D6](https://etherscan.io/address/0x00000009207D366cBB8549837F8Ae4bf800Af2D6) | | SymbioticVerifier factory | [0x41C443F10a92D597e6c9E271140BC94c10f5159F](https://etherscan.io/address/0x41C443F10a92D597e6c9E271140BC94c10f5159F) | | SymbioticVerifier | [0x00000000cBC6f5d4348496FfA22Cf014b9DA394B](https://etherscan.io/address/0x00000000cBC6f5d4348496FfA22Cf014b9DA394B) | | VaultConfigurator | [0x000000028be48f9E62E13403480B60C4822C5aa5](https://etherscan.io/address/0x000000028be48f9E62E13403480B60C4822C5aa5) | | BasicRedeeHook | [0x0000000637f1b1ccDA4Af2dB6CDDf5e5Ec45fd93](https://etherscan.io/address/0x0000000637f1b1ccDA4Af2dB6CDDf5e5Ec45fd93) | | RedirectingDepositHook | [0x00000004d3B17e5391eb571dDb8fDF95646ca827](https://etherscan.io/address/0x00000004d3B17e5391eb571dDb8fDF95646ca827) | | LidoDepositHook | [0x000000065d1A7bD71f52886910aaBE6555b7317c](https://etherscan.io/address/0x000000065d1A7bD71f52886910aaBE6555b7317c) | | OracleHelper | [0x000000005F543c38d5ea6D0bF10A50974Eb55E35](https://etherscan.io/address/0x000000005F543c38d5ea6D0bF10A50974Eb55E35) | | BurnableTokenizedShareManager | [0x000000000c79D2B5cD58AE545afc83030233D7B6](https://etherscan.io/address/0x000000000c79D2B5cD58AE545afc83030233D7B6) | | Contract Address | Address | | ----------------------------- | -------------------------------------------- | | Factory | `0x0000000072BAfCeAff1AD0237Ea58f06cfc4467F` | | FactoryFactory | `0x00000000741292C88f9fF5050b07051C4f592EBf` | | ConsensusFactory | `0xAfef40968b5304135677f0C89203948e1A145105` | | DepositQueueFactory | `0xF429ba2a8437E7de85078CF7481E8Ad52df7E58c` | | RedeemQueueFactory | `0xe08dc488bD6756323F8bf478869529D03db627ef` | | OracleFactory | `0x727c295b5D99b15280Ca8736b6F97ABA6aEd0E88` | | FeeManagerFactory | `0x52d56c20B0C8d403888880d0A1610e5ed17addA8` | | RiskManagerFactory | `0x9885215ef8DB25C87466E73018061e532784D716` | | ShareManagerFactory | `0xDA2a7aE07B6803feF9d95E47Ab83c8a5A09929F0` | | SubvaultFactory | `0xA64e324DFF04e3C0613ff0706867868C7b370a45` | | VaultFactory | `0xBBCD2aC50aF2EA12Cc9cb7B16dBDa85859BeB3da` | | VerifierFactory | `0x9fBAF5AEB9F52bA57E1cC1D3050eac6d75Df8ae7` | | ERC20VerifierFactory | `0x711F6236e325634AA8c1F692b5312bfF3A8558D0` | | AccountFactory | `0x870DB41df0905cc5a790f6582a3dA99A4A33F923` | | SwapModuleFactory | `0xC5a52E4bB718Dfe86938e5cB967362EdA1E62698` | | Consensus | `0x000000007e6b679B9196a1609e5Bc2405eDFd6Aa` | | DepositQueue | `0x00000000B2d2373aAF1C370cFE4e1Ee8BDE7C546` | | SignatureDepositQueue | `0x000000000Af33501e5BDAF9B481Ad2712a024727` | | SyncDepositQueue | `0x000000001CC8c3E40856E956db870095EF6C98bd` | | FeeManager | `0x00000000C18039E1F415fe07C33A316232238648` | | Oracle | `0x000000009adE4dAE1f868775A3f087945983f062` | | RedeemQueue | `0x0000000045d70ee8145135f08309fF5B1A63d43F` | | SignatureRedeemQueue | `0x000000008D14Ef3658805765107d9F12776f4138` | | RiskManager | `0x00000000CC26BC741E75B181738Ac2B16156179b` | | TokenizedShareManager | `0x00000000861e8B90B81f35C18cA14858Cc91d1Df` | | BasicShareManager | `0x00000000e5F0cddA56447b2a29e2847A52c8725D` | | BurnableTokenizedShareManager | `0x00000000C534B8680e3aa7165DeDc3Ab8781f602` | | Subvault | `0x00000000CA30010B8417f791250AE221FdaD5920` | | Verifier | `0x000000007e86a96e279662108cc19bA4c32EdE3C` | | ERC20Verifier | `0x00000000ACD80376E999Af8c424e5e33BD224A08` | | MellowAccountV1 | `0x00000000860913f37fab81ce8ce4E5BD1f664482` | | SwapModule | `0x00000000015fa996bCA8c842AFEdC334616F283A` | | Vault | `0x0000000070f44289ec5ea3E5972f058f75B29801` | | BitmaskVerifier | `0x0000000009E9368ad21fc19DCE1cFcf9Af6dE339` | | VaultConfigurator | `0x0000000005a67199ABE0f9C995EAB9DaDfA31Ccd` | | BasicRedeemHook | `0x00000000176dD23550c3845746b2036E90DC5912` | | RedirectingDepositHook | `0x0000000024ABbd08686Abb2987831dEa88eF1180` | | OracleHelper | `0x000000007d2552AD746Af5c13f91B5e72f97c2B7` | | Contract Address | Address | | ----------------------------- | ------------------------------------------ | | Factory | 0x0000000072BAfCeAff1AD0237Ea58f06cfc4467F | | FactoryFactory | 0x00000000741292C88f9fF5050b07051C4f592EBf | | ConsensusFactory | 0xAfef40968b5304135677f0C89203948e1A145105 | | DepositQueueFactory | 0xF429ba2a8437E7de85078CF7481E8Ad52df7E58c | | RedeemQueueFactory | 0xe08dc488bD6756323F8bf478869529D03db627ef | | OracleFactory | 0x727c295b5D99b15280Ca8736b6F97ABA6aEd0E88 | | FeeManagerFactory | 0x52d56c20B0C8d403888880d0A1610e5ed17addA8 | | RiskManagerFactory | 0x9885215ef8DB25C87466E73018061e532784D716 | | ShareManagerFactory | 0xDA2a7aE07B6803feF9d95E47Ab83c8a5A09929F0 | | SubvaultFactory | 0xA64e324DFF04e3C0613ff0706867868C7b370a45 | | VaultFactory | 0xBBCD2aC50aF2EA12Cc9cb7B16dBDa85859BeB3da | | VerifierFactory | 0x9fBAF5AEB9F52bA57E1cC1D3050eac6d75Df8ae7 | | ERC20VerifierFactory | 0x711F6236e325634AA8c1F692b5312bfF3A8558D0 | | AccountFactory | 0x870DB41df0905cc5a790f6582a3dA99A4A33F923 | | SwapModuleFactory | 0xC5a52E4bB718Dfe86938e5cB967362EdA1E62698 | | Consensus | 0x000000007e6b679B9196a1609e5Bc2405eDFd6Aa | | DepositQueue | 0x00000000B2d2373aAF1C370cFE4e1Ee8BDE7C546 | | SignatureDepositQueue | 0x000000000Af33501e5BDAF9B481Ad2712a024727 | | SyncDepositQueue | 0x000000001CC8c3E40856E956db870095EF6C98bd | | FeeManager | 0x00000000C18039E1F415fe07C33A316232238648 | | Oracle | 0x000000009adE4dAE1f868775A3f087945983f062 | | RedeemQueue | 0x0000000045d70ee8145135f08309fF5B1A63d43F | | SignatureRedeemQueue | 0x000000008D14Ef3658805765107d9F12776f4138 | | RiskManager | 0x00000000CC26BC741E75B181738Ac2B16156179b | | TokenizedShareManager | 0x00000000861e8B90B81f35C18cA14858Cc91d1Df | | BasicShareManager | 0x00000000e5F0cddA56447b2a29e2847A52c8725D | | BurnableTokenizedShareManager | 0x00000000C534B8680e3aa7165DeDc3Ab8781f602 | | Subvault | 0x00000000CA30010B8417f791250AE221FdaD5920 | | Verifier | 0x000000007e86a96e279662108cc19bA4c32EdE3C | | ERC20Verifier | 0x00000000ACD80376E999Af8c424e5e33BD224A08 | | MellowAccountV1 | 0x00000000860913f37fab81ce8ce4E5BD1f664482 | | SwapModule | 0x00000000c324E2d11EcCB03A061F69B5FE123645 | | Vault | 0x0000000070f44289ec5ea3E5972f058f75B29801 | | BitmaskVerifier | 0x0000000009E9368ad21fc19DCE1cFcf9Af6dE339 | | VaultConfigurator | 0x0000000005a67199ABE0f9C995EAB9DaDfA31Ccd | | BasicRedeemHook | 0x00000000176dD23550c3845746b2036E90DC5912 | | RedirectingDepositHook | 0x0000000024ABbd08686Abb2987831dEa88eF1180 | | OracleHelper | 0x000000007d2552AD746Af5c13f91B5e72f97c2B7 | | Contract | Address | | ----------------------------- | ------------------------------------------ | | Factory | 0x0000000072BAfCeAff1AD0237Ea58f06cfc4467F | | FactoryFactory | 0x00000000741292C88f9fF5050b07051C4f592EBf | | ConsensusFactory | 0xAfef40968b5304135677f0C89203948e1A145105 | | DepositQueueFactory | 0xF429ba2a8437E7de85078CF7481E8Ad52df7E58c | | RedeemQueueFactory | 0xe08dc488bD6756323F8bf478869529D03db627ef | | OracleFactory | 0x727c295b5D99b15280Ca8736b6F97ABA6aEd0E88 | | FeeManagerFactory | 0x52d56c20B0C8d403888880d0A1610e5ed17addA8 | | RiskManagerFactory | 0x9885215ef8DB25C87466E73018061e532784D716 | | ShareManagerFactory | 0xDA2a7aE07B6803feF9d95E47Ab83c8a5A09929F0 | | SubvaultFactory | 0xA64e324DFF04e3C0613ff0706867868C7b370a45 | | VaultFactory | 0xBBCD2aC50aF2EA12Cc9cb7B16dBDa85859BeB3da | | VerifierFactory | 0x9fBAF5AEB9F52bA57E1cC1D3050eac6d75Df8ae7 | | ERC20VerifierFactory | 0x711F6236e325634AA8c1F692b5312bfF3A8558D0 | | AccountFactory | 0x870DB41df0905cc5a790f6582a3dA99A4A33F923 | | SwapModuleFactory | 0xC5a52E4bB718Dfe86938e5cB967362EdA1E62698 | | Consensus | 0x000000007e6b679B9196a1609e5Bc2405eDFd6Aa | | DepositQueue | 0x00000000B2d2373aAF1C370cFE4e1Ee8BDE7C546 | | SignatureDepositQueue | 0x000000000Af33501e5BDAF9B481Ad2712a024727 | | SyncDepositQueue | 0x000000001CC8c3E40856E956db870095EF6C98bd | | FeeManager | 0x00000000C18039E1F415fe07C33A316232238648 | | Oracle | 0x000000009adE4dAE1f868775A3f087945983f062 | | RedeemQueue | 0x0000000045d70ee8145135f08309fF5B1A63d43F | | SignatureRedeemQueue | 0x000000008D14Ef3658805765107d9F12776f4138 | | RiskManager | 0x00000000CC26BC741E75B181738Ac2B16156179b | | TokenizedShareManager | 0x00000000861e8B90B81f35C18cA14858Cc91d1Df | | BasicShareManager | 0x00000000e5F0cddA56447b2a29e2847A52c8725D | | BurnableTokenizedShareManager | 0x00000000C534B8680e3aa7165DeDc3Ab8781f602 | | Subvault | 0x00000000CA30010B8417f791250AE221FdaD5920 | | Verifier | 0x000000007e86a96e279662108cc19bA4c32EdE3C | | ERC20Verifier | 0x00000000ACD80376E999Af8c424e5e33BD224A08 | | MellowAccountV1 | 0x00000000860913f37fab81ce8ce4E5BD1f664482 | | SwapModule | 0x0000000079d3FAb70077e5B920Ce067f11676351 | | Vault | 0x0000000070f44289ec5ea3E5972f058f75B29801 | | BitmaskVerifier | 0x0000000009E9368ad21fc19DCE1cFcf9Af6dE339 | | VaultConfigurator | 0x0000000005a67199ABE0f9C995EAB9DaDfA31Ccd | | BasicRedeemHook | 0x00000000176dD23550c3845746b2036E90DC5912 | | RedirectingDepositHook | 0x0000000024ABbd08686Abb2987831dEa88eF1180 | | OracleHelper | 0x000000007d2552AD746Af5c13f91B5e72f97c2B7 | | Smart Contract | Address | | ----------------------------- | ------------------------------------------ | | Factory | 0x0000000072BAfCeAff1AD0237Ea58f06cfc4467F | | FactoryFactory | 0x00000000741292C88f9fF5050b07051C4f592EBf | | ConsensusFactory | 0xAfef40968b5304135677f0C89203948e1A145105 | | DepositQueueFactory | 0xF429ba2a8437E7de85078CF7481E8Ad52df7E58c | | RedeemQueueFactory | 0xe08dc488bD6756323F8bf478869529D03db627ef | | OracleFactory | 0x727c295b5D99b15280Ca8736b6F97ABA6aEd0E88 | | FeeManagerFactory | 0x52d56c20B0C8d403888880d0A1610e5ed17addA8 | | RiskManagerFactory | 0x9885215ef8DB25C87466E73018061e532784D716 | | ShareManagerFactory | 0xDA2a7aE07B6803feF9d95E47Ab83c8a5A09929F0 | | SubvaultFactory | 0xA64e324DFF04e3C0613ff0706867868C7b370a45 | | VaultFactory | 0xBBCD2aC50aF2EA12Cc9cb7B16dBDa85859BeB3da | | VerifierFactory | 0x9fBAF5AEB9F52bA57E1cC1D3050eac6d75Df8ae7 | | ERC20VerifierFactory | 0x711F6236e325634AA8c1F692b5312bfF3A8558D0 | | AccountFactory | 0x870DB41df0905cc5a790f6582a3dA99A4A33F923 | | SwapModuleFactory | 0xC5a52E4bB718Dfe86938e5cB967362EdA1E62698 | | Consensus | 0x000000007e6b679B9196a1609e5Bc2405eDFd6Aa | | DepositQueue | 0x00000000B2d2373aAF1C370cFE4e1Ee8BDE7C546 | | SignatureDepositQueue | 0x000000000Af33501e5BDAF9B481Ad2712a024727 | | SyncDepositQueue | 0x000000001CC8c3E40856E956db870095EF6C98bd | | FeeManager | 0x00000000C18039E1F415fe07C33A316232238648 | | Oracle | 0x000000009adE4dAE1f868775A3f087945983f062 | | RedeemQueue | 0x0000000045d70ee8145135f08309fF5B1A63d43F | | SignatureRedeemQueue | 0x000000008D14Ef3658805765107d9F12776f4138 | | RiskManager | 0x00000000CC26BC741E75B181738Ac2B16156179b | | TokenizedShareManager | 0x00000000861e8B90B81f35C18cA14858Cc91d1Df | | BasicShareManager | 0x00000000e5F0cddA56447b2a29e2847A52c8725D | | BurnableTokenizedShareManager | 0x00000000C534B8680e3aa7165DeDc3Ab8781f602 | | Subvault | 0x00000000CA30010B8417f791250AE221FdaD5920 | | Verifier | 0x000000007e86a96e279662108cc19bA4c32EdE3C | | ERC20Verifier | 0x00000000ACD80376E999Af8c424e5e33BD224A08 | | MellowAccountV1 | 0x00000000860913f37fab81ce8ce4E5BD1f664482 | | SwapModule | 0x0000000042E248f84Df6BA1E768F878b1f2Bae9f | | Vault | 0x0000000070f44289ec5ea3E5972f058f75B29801 | | BitmaskVerifier | 0x0000000009E9368ad21fc19DCE1cFcf9Af6dE339 | | VaultConfigurator | 0x0000000005a67199ABE0f9C995EAB9DaDfA31Ccd | | BasicRedeemHook | 0x00000000176dD23550c3845746b2036E90DC5912 | | RedirectingDepositHook | 0x0000000024ABbd08686Abb2987831dEa88eF1180 | | OracleHelper | 0x000000007d2552AD746Af5c13f91B5e72f97c2B7 | | Smart Contract | Address | | ----------------------------- | ------------------------------------------ | | Factory | 0x0000000072BAfCeAff1AD0237Ea58f06cfc4467F | | FactoryFactory | 0x00000000741292C88f9fF5050b07051C4f592EBf | | ConsensusFactory | 0xAfef40968b5304135677f0C89203948e1A145105 | | DepositQueueFactory | 0xF429ba2a8437E7de85078CF7481E8Ad52df7E58c | | RedeemQueueFactory | 0xe08dc488bD6756323F8bf478869529D03db627ef | | OracleFactory | 0x727c295b5D99b15280Ca8736b6F97ABA6aEd0E88 | | FeeManagerFactory | 0x52d56c20B0C8d403888880d0A1610e5ed17addA8 | | RiskManagerFactory | 0x9885215ef8DB25C87466E73018061e532784D716 | | ShareManagerFactory | 0xDA2a7aE07B6803feF9d95E47Ab83c8a5A09929F0 | | SubvaultFactory | 0xA64e324DFF04e3C0613ff0706867868C7b370a45 | | VaultFactory | 0xBBCD2aC50aF2EA12Cc9cb7B16dBDa85859BeB3da | | VerifierFactory | 0x9fBAF5AEB9F52bA57E1cC1D3050eac6d75Df8ae7 | | ERC20VerifierFactory | 0x711F6236e325634AA8c1F692b5312bfF3A8558D0 | | AccountFactory | 0x870DB41df0905cc5a790f6582a3dA99A4A33F923 | | SwapModuleFactory | 0xC5a52E4bB718Dfe86938e5cB967362EdA1E62698 | | Consensus | 0x000000007e6b679B9196a1609e5Bc2405eDFd6Aa | | DepositQueue | 0x00000000B2d2373aAF1C370cFE4e1Ee8BDE7C546 | | SignatureDepositQueue | 0x000000000Af33501e5BDAF9B481Ad2712a024727 | | SyncDepositQueue | 0x000000001CC8c3E40856E956db870095EF6C98bd | | FeeManager | 0x00000000C18039E1F415fe07C33A316232238648 | | Oracle | 0x000000009adE4dAE1f868775A3f087945983f062 | | RedeemQueue | 0x0000000045d70ee8145135f08309fF5B1A63d43F | | SignatureRedeemQueue | 0x000000008D14Ef3658805765107d9F12776f4138 | | RiskManager | 0x00000000CC26BC741E75B181738Ac2B16156179b | | TokenizedShareManager | 0x00000000861e8B90B81f35C18cA14858Cc91d1Df | | BasicShareManager | 0x00000000e5F0cddA56447b2a29e2847A52c8725D | | BurnableTokenizedShareManager | 0x00000000C534B8680e3aa7165DeDc3Ab8781f602 | | Subvault | 0x00000000CA30010B8417f791250AE221FdaD5920 | | Verifier | 0x000000007e86a96e279662108cc19bA4c32EdE3C | | ERC20Verifier | 0x00000000ACD80376E999Af8c424e5e33BD224A08 | | MellowAccountV1 | 0x00000000860913f37fab81ce8ce4E5BD1f664482 | | SwapModule | 0x0000000022B540Fe06d7a9c32d81163971b583D6 | | Vault | 0x0000000070f44289ec5ea3E5972f058f75B29801 | | BitmaskVerifier | 0x0000000009E9368ad21fc19DCE1cFcf9Af6dE339 | | VaultConfigurator | 0x0000000005a67199ABE0f9C995EAB9DaDfA31Ccd | | BasicRedeemHook | 0x00000000176dD23550c3845746b2036E90DC5912 | | RedirectingDepositHook | 0x0000000024ABbd08686Abb2987831dEa88eF1180 | | OracleHelper | 0x000000007d2552AD746Af5c13f91B5e72f97c2B7 | | Smart Contract | Address | | ------------------------------------ | ------------------------------------------ | | Factory Implementation | 0x000000092C4e111CBA592380b258d94B37038B63 | | Factory Factory | 0x00000003cEe0DbFb61dD598CD7978993A37f8F8C | | Consensus Factory | 0x4C496F31a4D46044E57214f282420b8b078edf56 | | Consensus Implementation | 0x00000008086A535Febd23fBd8C8F7d9D987930B7 | | DepositQueue Factory | 0x66B1a68F8CE628d508290d5C1d74Bc50416BDF90 | | DepositQueue Implementation | 0x0000000A151048f4f01996a9Cd35a982F5830251 | | SignatureDepositQueue Implementation | 0x00000003e7D5d1EDF85b03b974aAc374d0FCB8A1 | | SyncDepositQueue Implementation | 0x00000007d43702d556707a63132d42BcDf47E7dD | | FeeManager Factory | 0x71a4D9739A35B4e86118F3a45bae662Bcc9357FA | | FeeManager Implementation | 0x00000003bf6bEC83fA8ff147b04176B82F591497 | | Oracle Factory | 0xe4E2b5Db061A731D96b9267464c17Ba282326Ce7 | | Oracle Implementation | 0x00000001bdbaFbE0Fb55b7d74a6dB74D1DA6047E | | RedeemQueue Factory | 0x0a69B47c3E0bD7e5B1E1Db95d6C0b2914607e19f | | RedeemQueue Implementation | 0x00000002F8d3f0D03E9Ce461791F6A0a9d28D0f6 | | SignatureRedeemQueue Implementation | 0x000000047f8812704050cB86E549Fe8f28512A2D | | RiskManager Factory | 0x95ff5434A51f3E42fCeD2Cae36548d95e56bAb10 | | RiskManager Implementation | 0x0000000a0d139B4B7add54D70e2a4ED3c81C513C | | ShareManager Factory | 0x3755c140b90dC4E6b1A6361279B2C2eCc0358689 | | TokenizedShareManager Implementation | 0x0000000f2a485f26efd108144cCBFc46b18cB3e0 | | BasicShareManager Implementation | 0x0000000454d68af6Faf344e8acAa372f136749c5 | | Subvault Factory | 0x72244F91242244E62Af3417294B828E262EbdfE7 | | Subvault Implementation | 0x00000008c8A185371Ab8eB28bbdb875cd526B69C | | Verifier Factory | 0x40383d404e570D95fF68945d2a334fb2f5ecE0f6 | | Verifier Implementation | 0x00000007eEEbCA71f6b261061136BaFA666218A5 | | Vault Factory | 0xC7332ab052350Bbb9075f1160cc7073428981638 | | Vault Implementation | 0x00000002334dBFa3B92467eA9Eb970ec1e067377 | | BitmaskVerifier | 0x0000000f7DA5A9480262Ac3D654b1F4aA9F604B8 | | ERC20Verifier Factory | 0x7025132709b3B01D663D97e56eae37988471c75a | | ERC20Verifier | 0x000000010849B881DA846FFEb1078A433284F8D0 | | Account Factory | 0xCda7916AA830B4dAb8295FBa92953d5251f5FDFa | | MellowAccountV1 | 0x00000001f879d5dAE0066E714867014Ec265F4ab | | VaultConfigurator | 0x00000003986f4F63CdBAB0f5d78fff57495fee85 | | BasicRedeeHook | 0x000000033FDa28b7025Fb16D53F81c3C1F78d572 | | RedirectingDepositHook | 0x0000000Cbd64305e1668dB5F8a542c2c7EC61640 | | OracleHelper | 0x00000005dc87A3230E0F3195C5e9220DCFF1E182 | | OracleSubmitterFactory | 0x0000000dB76510D5B4D99df16160469bF782B227 | | DeployVaultFactoryRegistry | 0x00000008656A21E6f690d40BAc97736f62E54853 | | DeployVaultFactory | 0x00000005b5Dda102b4F9104fE8c537f816ac76D4 | | Component | Address | | ------------------------------------ | ------------------------------------------ | | Factory Implementation | 0x00000004C12438f4593bb6C4047998020e60Fca8 | | Factory Factory | 0x0000000013eC5b779Ee3997A005088BCecDa551D | | Consensus Factory | 0xc52C25a06d2c7fbd349C2AD838544A1cF953b8eb | | Consensus Implementation | 0x0000000Ee53D9707851626b0E8485A8599bE95E7 | | DepositQueue Factory | 0x4176D4BD30a4AEF3F7ffcDD5Fe8997De807409a4 | | DepositQueue Implementation | 0x0000000eED98Aca517473d134Cc1a79c5a23b591 | | SignatureDepositQueue Implementation | 0x00000009A6488c99272A1ae297b7f364A348ba55 | | SyncDepositQueue Implementation | 0x0000000D38799D88008747b874822f55f0D35F6a | | FeeManager Factory | 0xcf41afeD6DE8A38F69235d414BB686f2847C19E0 | | FeeManager Implementation | 0x0000000852CF76C1c3dd8e74c817c442667f59D3 | | Oracle Factory | 0x03D4FfC7fB7bfec79EfF121201eB567A4d8E3AbA | | Oracle Implementation | 0x00000000fd75e0935c7101432F07E7D949a3709A | | RedeemQueue Factory | 0x3C2190540f6Cea0CD81f94e84F91f51644603238 | | RedeemQueue Implementation | 0x0000000A37A76557eAf5FF84D537C19aefb61c69 | | SignatureRedeemQueue Implementation | 0x00000009839691F13A8B2Bfb48a02338d5BB4282 | | RiskManager Factory | 0x6e72a6F11D0aCA5Cb21F94Dd78727Feb6b78224b | | RiskManager Implementation | 0x00000009BC5616c655EB3931d15553645F79e163 | | ShareManager Factory | 0xEd7d5E840c1567589f7354E278fcE3D549AC5a89 | | TokenizedShareManager Implementation | 0x0000000Ef763C2e0Fd309DaB48Bb4d5502ebe9F2 | | BasicShareManager Implementation | 0x00000000DAf16b90ee413672d0C7E51201A444a2 | | Subvault Factory | 0x2b49B9158640f576d50803868cB39C39c111b236 | | Subvault Implementation | 0x0000000A9671be5CA72833D21A5A048Bb59140A7 | | Verifier Factory | 0x318aec5cBf813eE085c7F5bc9285945d0cF97064 | | Verifier Implementation | 0x00000008d3117169514077a7d3e5e8B7cf76d4EA | | Vault Factory | 0xf1d7BE794A16767CA4485ec419984779c3221680 | | Vault Implementation | 0x0000000B84D4B6c47f975996CEdd67c475840CB0 | | BitmaskVerifier | 0x0000000022c92AC77562374F5e4617BF5fF7C2b5 | | ERC20Verifier Factory | 0x6D2416bc3A15EfF424577faD7914381DAA3172DE | | ERC20Verifier | 0x0000000dAb3d1f8724d96F8BECb864381a89C9C7 | | VaultConfigurator | 0x0000000D0e993ACc4ba4B8EaEC809866C068A3C2 | | BasicRedeeHook | 0x00000007A95AcE65df0d6F71660152c196f6330d | | RedirectingDepositHook | 0x0000000b0bFE39B38d95be646921D9E3756D27ee | | OracleHelper | 0x0000000e1a96d9abAb10F19b966F960efc8Ca989 | | Contract address | Address | | ------------------------------------ | ------------------------------------------ | | Factory implementation | 0x00000008E7c244Fb6FA6Fc1fB5EC53Ec71c34386 | | Factory factory | 0x000000071a219faa713E719F2DfB458b10dbAED1 | | Consensus factory | 0xE4Db00dCc29966368E8aA966ac75B6FE5B4113D7 | | Consensus implementation | 0x000000083a1bE8Aa2Aa5fB244c84A6E410e6ce24 | | DepositQueue factory | 0x664B70AE0a01D9beF57c2Eb64664B0CFB055A461 | | DepositQueue implementation | 0x0000000518eC830D8C3da6056b34A0dfBF9e924d | | SignatureDepositQueue implementation | 0x00000006A03A937E4B316F02a5130e4FB0B22Dea | | SyncDepositQueue implementation | 0x0000000B813e85943D42c5187efAb487E12e1485 | | FeeManager factory | 0x4b2b12a33e260ef35e84687860785d263EfF5172 | | FeeManager implementation | 0x0000000df5Cff487723b8D8c58eD5C336d8a2317 | | Oracle factory | 0xbb7e4da67Fe8E66AB4AE8EAb2999e731DA364492 | | Oracle implementation | 0x0000000C705B7C7485F62Bc1DF7554fD6EB6C602 | | RedeemQueue factory | 0x7f2F6B155B41F2DD8A8Ea0FbA3b4EA1ceDc6260e | | RedeemQueue implementation | 0x00000000d06959064b28a46970497923f8834B16 | | SignatureRedeemQueue implementation | 0x000000004a3F4ff856e7cb47A0ae8aDe6d133cFB | | RiskManager factory | 0x902266fE38DD8Eee290987490bD57537c82007a1 | | RiskManager implementation | 0x0000000A7a10ea335C54E03220cfAe92310b2465 | | ShareManager factory | 0x6C3BB5478fD189DCf35Fb3a4b56015163392AA35 | | TokenizedShareManager implementation | 0x000000071F09E877c469749c093d09FB17896D6c | | BasicShareManager implementation | 0x00000008be96121073931e2b6Da8f5711a52097d | | Subvault factory | 0x3B62CaA341fA0535a479B394c2f6EA28ee8fA449 | | Subvault implementation | 0x0000000585bE8a415f9edCdC3C56472625BB2E02 | | Verifier factory | 0xbc1468D587DaEE3023E2b41Cc642643AF3221178 | | Verifier implementation | 0x000000097bD869258523A17D1e9836E71Ef8aB2A | | Vault factory | 0x5310AD84B0cd3Af376C751EAb81ceE414bD442d8 | | Vault implementation | 0x0000000B39b91D795b9975219E228bCb4D33A6A3 | | BitmaskVerifier | 0x0000000819BA998E0Dfe0DAfdd6B23dBf103314D | | ERC20Verifier factory | 0x0d634B6e35368b8954C53b38aDF72716a16667FA | | ERC20Verifier | 0x000000038Cd2281fe3C651A8B9C2380Ea15f2c87 | | VaultConfigurator | 0x00000000f731118c52AeA768c1ac22CEcA7e3b8D | | BasicRedeeHook | 0x0000000887657b16F0dc7EFbb2be9EA77cEDF16c | | RedirectingDepositHook | 0x0000000B77FC23f6F0f4c51238D6e1c76DefBFdb | | OracleHelper | 0x00000002FC616d31133ab9AD626E43a94674D5B6 | | OracleSubmitterFactory | 0x00000007AA9Bd15F538a2d1D68A2aCFE8D09BFd0 | | DeployVaultFactoryRegistry | 0x000000020893B447c2c13E4A8e5abCF5E7c09AeA | | DeployVaultFactory | 0x0000000bd67D6538614668EFe27aF3f17A3031dd |
Instances | Vault | [0x277C6A642564A91ff78b008022D65683cEE5CCC5](https://etherscan.io/address/0x277C6A642564A91ff78b008022D65683cEE5CCC5) | | --------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | DepositQueue(ETH) | [0xE707321B887b9da133AC5fCc5eDB78Ab177a152D](https://etherscan.io/address/0xE707321B887b9da133AC5fCc5eDB78Ab177a152D) | | DepositQueue(WETH) | [0x2eA268f1018a4767bF5da42D531Ea9e943942A36](https://etherscan.io/address/0x2eA268f1018a4767bF5da42D531Ea9e943942A36) | | DepositQueue(WSTETH) | [0x614cb9E9D13712781DfD15aDC9F3DAde60E4eFAb](https://etherscan.io/address/0x614cb9E9D13712781DfD15aDC9F3DAde60E4eFAb) | | SyncDepositQueue(ETH) | [0x3b261535055c9faf248fe5b23a743184b6d4beed](https://etherscan.io/address/0x3b261535055c9faf248fe5b23a743184b6d4beed) | | SyncDepositQueue(WETH) | [0x8a89f2c30673520a45839353c32c30bbe59fe616](https://etherscan.io/address/0x8a89f2c30673520a45839353c32c30bbe59fe616) | | SyncDepositQueue(WSTETH) | [0x6c9abc05cdb7d1c339dcc359f40e8bde65c1bd99](https://etherscan.io/address/0x6c9abc05cdb7d1c339dcc359f40e8bde65c1bd99) | | SyncDepositQueue(DVV) | [0xbcdbf4d3b3b3345f8348c6aa557949deaa6bc57b](0xbcdbf4d3b3b3345f8348c6aa557949deaa6bc57b) | | RedeemQueue(WSTETH) | [0x1ae8C006b5C97707aa074AaeD42BecAD2CF80Da2](https://etherscan.io/address/0x1ae8C006b5C97707aa074AaeD42BecAD2CF80Da2) | | Oracle | [0x8a78e6b7E15C4Ae3aeAeE3bf0DE4F2de4078c1cD](https://etherscan.io/address/0x8a78e6b7E15C4Ae3aeAeE3bf0DE4F2de4078c1cD) | | ShareManager | [0xcd3c0F51798D1daA92Fb192E57844Ae6cEE8a6c7](https://etherscan.io/address/0xcd3c0F51798D1daA92Fb192E57844Ae6cEE8a6c7) | | FeeManager | [0x24FD64EB4766d91FD79a4D5e8086b2460dEBcaE7](https://etherscan.io/address/0x24FD64EB4766d91FD79a4D5e8086b2460dEBcaE7) | | RiskManager | [0x4f6bc03537C6F74E250f57a9a7238087caBF1c6D](https://etherscan.io/address/0x4f6bc03537C6F74E250f57a9a7238087caBF1c6D) | | Subvault0 | [0x90c983DC732e65DB6177638f0125914787b8Cb78](https://etherscan.io/address/0x90c983DC732e65DB6177638f0125914787b8Cb78) | | Verifier0 | [0xF4eA276361348b301Ba2296dB909a7c973A15451](https://etherscan.io/address/0xF4eA276361348b301Ba2296dB909a7c973A15451) | | Subvault1 | [0x893aa69FBAA1ee81B536f0FbE3A3453e86290080](https://etherscan.io/address/0x893aa69FBAA1ee81B536f0FbE3A3453e86290080) | | Verifier1 | [0x02e1C91C4D82af454D892FBE2c5De2c4504b2675](https://etherscan.io/address/0x02e1C91C4D82af454D892FBE2c5De2c4504b2675) | | Subvault2 | [0x181cB55f872450D16aE858D532B4e35e50eaA76D](https://etherscan.io/address/0x181cB55f872450D16aE858D532B4e35e50eaA76D) | | Verifier2 | [0x1616d39a201D246cbD1B3B145234638f7719b53A](https://etherscan.io/address/0x1616d39a201D246cbD1B3B145234638f7719b53A) | | Subvault3 | [0x9938A09FeA37bA681A1Bd53D33ddDE2dEBEc1dA0](https://etherscan.io/address/0x9938A09FeA37bA681A1Bd53D33ddDE2dEBEc1dA0) | | Verifier3 | [0xd662dF7C0FAF0Fe6446638651b05C287806AD1AE](https://etherscan.io/address/0xd662dF7C0FAF0Fe6446638651b05C287806AD1AE) | | Subvault 4 | [0x3883d8CdCdda03784908cFa2F34ED2cF1604e4d7](https://etherscan.io/address/0x3883d8CdCdda03784908cFa2F34ED2cF1604e4d7) | | Verifier 4 | [0x58C4B6B0d6CFf1d684E4B8Ee899550F4B68A1031](https://etherscan.io/address/0x58C4B6B0d6CFf1d684E4B8Ee899550F4B68A1031) | | Subvault 5 | [0xECf3BDE9f50F71edE67E05050123b64b519DF55C](https://etherscan.io/address/0xECf3BDE9f50F71edE67E05050123b64b519DF55C) | | Verifier 5 | [0xb0d19Eef486b4807Ab1fe20AB4cfaCB074592Ea5](https://etherscan.io/address/0xb0d19Eef486b4807Ab1fe20AB4cfaCB074592Ea5) | | Subvault 6 | [0xCDfA7EfE670869c6b6be4375654E0b206eF49c89](https://etherscan.io/address/0xCDfA7EfE670869c6b6be4375654E0b206eF49c89) | | Verifier 6 | [0x9C5D826e1BcdF67f0596725CbB931dC02132D88d](https://etherscan.io/address/0x9C5D826e1BcdF67f0596725CbB931dC02132D88d) | | Subvault 7 | [0x888d2A3E9B600F360a3386c9D2fEdFa658E7fA29](https://etherscan.io/address/0x888d2A3E9B600F360a3386c9D2fEdFa658E7fA29) | | Verifier 7 | [0xDF96d59d3688C56ca29aED045FE67C84bbc38461](https://etherscan.io/address/0xDF96d59d3688C56ca29aED045FE67C84bbc38461) | | Vault Arbitrum | [0x40330720039352B309c70A5028322D1481F496d1](https://arbiscan.io/address/0x40330720039352B309c70A5028322D1481F496d1) | | Subvault Arbitrum | [0x222fa99C485a088564eb43fAA50Bc10b2497CDB2](https://arbiscan.io/address/0x222fa99C485a088564eb43fAA50Bc10b2497CDB2) | | Verifier Arbitrum | [0x022a33293aeD00E59E93D354D3810249Fa33d7D4](https://arbiscan.io/address/0x022a33293aeD00E59E93D354D3810249Fa33d7D4) | | TimelockController Arbitrum | [0xda1674c1135eA98A311a3b4aa11865266ab52A7C](https://arbiscan.io/address/0xda1674c1135eA98A311a3b4aa11865266ab52A7C) | | Vault Plasma | [0x841e213864046111E43d237703d71FaBe91Ef9e0](https://plasmascan.to/address/0x841e213864046111E43d237703d71FaBe91Ef9e0) | | Subvault 0 Plasma | [0xbbF9400C09B0F649F3156989F1CCb9c016f943bb](https://plasmascan.to/address/0xbbF9400C09B0F649F3156989F1CCb9c016f943bb) | | Verifier 0 Plasma | [0xC724D3bA28e24e243f653c626F8BEA44113b3a0b](https://plasmascan.to/address/0xC724D3bA28e24e243f653c626F8BEA44113b3a0b) | | Subvault 1 Plasma | [0x7696731721dddeC1502F35C52d9c83a768227daE](https://plasmascan.to/address/0x7696731721dddeC1502F35C52d9c83a768227daE) | | Verifier 1 Plasma | [0xad1b3ea06027df987d1320e1f4d0f1b58230250c](https://plasmascan.to/address/0xad1b3ea06027df987d1320e1f4d0f1b58230250c) | | TimelockController Plasma | [0x3169036c3F79c03C14a7496DD2016f5B059e17D8](https://plasmascan.to/address/0x3169036c3F79c03C14a7496DD2016f5B059e17D8) | | SwapModule 0 | [0x25091725982e83f6aFDf6A17705FeECA5866B864](https://etherscan.io/address/0x25091725982e83f6aFDf6A17705FeECA5866B864) | | SwapModule 1 | [0x7a57d62b1217Ce5685E26C333741e61b99233E65](https://etherscan.io/address/0x7a57d62b1217Ce5685E26C333741e61b99233E65) | | SwapModule 3 | [0xc95b806aC073Df930014ac476d26c8ad918f14e0](https://etherscan.io/address/0xc95b806aC073Df930014ac476d26c8ad918f14e0) | | SwapModule 4 | [0x35D482D0BBbb1c2F25d9b12F234883f3224f3198](https://etherscan.io/address/0x35D482D0BBbb1c2F25d9b12F234883f3224f3198) | | SwapModule 5 | [0xE3B023d3FF076E35448c936dA5e8F6adA6130ca4](https://etherscan.io/address/0xE3B023d3FF076E35448c936dA5e8F6adA6130ca4) | | SwapModule 6 | [0x2a166aE48F9F1FC27685582a61250011fd5363D8](https://etherscan.io/address/0x2a166aE48F9F1FC27685582a61250011fd5363D8) | | SwapModule 7 | [0xb6451d4EaEC79fD22b69086a5B760a166bd28C52](https://etherscan.io/address/0xb6451d4EaEC79fD22b69086a5B760a166bd28C52) | | Plasma SwapModule 0 | [0x0A974551c45cFb9e002D06B2AB82ae20e800D000](https://plasmascan.to/address/0x0A974551c45cFb9e002D06B2AB82ae20e800D000) | | Plasma SwapModule 1 | [0xa330Cc14988321160fd26D9F202cBd845328B6e2](https://plasmascan.to/address/0xa330Cc14988321160fd26D9F202cBd845328B6e2) | | Timelockcontroller | [0x8D8b65727729Fb484CB6dc1452D61608a5758596](https://etherscan.io/address/0x8D8b65727729Fb484CB6dc1452D61608a5758596) | | Lazyadmin | [0xAbE20D266Ae54b9Ae30492dEa6B6407bF18fEeb5](https://etherscan.io/address/0xAbE20D266Ae54b9Ae30492dEa6B6407bF18fEeb5) | | Activeadmin | [0xeb1CaFBcC8923eCbc243ff251C385C201A6c734a](https://etherscan.io/address/0xeb1CaFBcC8923eCbc243ff251C385C201A6c734a) | | Oracleupdater | [0xd27fFB15Dd00D5E52aC2BFE6d5AFD36caE850081](https://etherscan.io/address/0xd27fFB15Dd00D5E52aC2BFE6d5AFD36caE850081) | | OracleSubmitter | [0x00000000df0088bd598df1e4ae57943dc481907a](https://etherscan.io/address/0x00000000df0088bd598df1e4ae57943dc481907a) | | Curator | [0x5Dbf9287787A5825beCb0321A276C9c92d570a75](https://etherscan.io/address/0x5Dbf9287787A5825beCb0321A276C9c92d570a75) | | Mellowsrtethtreasury | [0xb1E5a8F26C43d019f2883378548a350ecdD1423B](https://etherscan.io/address/0xb1E5a8F26C43d019f2883378548a350ecdD1423B) | | Emergencypauser | [0xa6278B726d4AA09D14f9E820D7785FAd82E7196](https://etherscan.io/address/0xa6278B726d4AA09D14f9E820D7785FAd82E7196F) | | Vault | [0xDbC81B33A23375A90c8Ba4039d5738CB6f56fE8d](https://etherscan.io/address/0xDbC81B33A23375A90c8Ba4039d5738CB6f56fE8d) | | --------------------- | ---------------------------------------------------------------------------------------------------------------------- | | DepositQueue (ETH) | [0xfA428FDBC73AD52C12724096e73e213FCd307141](https://etherscan.io/address/0xfA428FDBC73AD52C12724096e73e213FCd307141) | | DepositQueue (WETH) | [0x729f0fE9c1BE8e4a814695277d7B56AD2427487e](https://etherscan.io/address/0x729f0fE9c1BE8e4a814695277d7B56AD2427487e) | | DepositQueue (WSTETH) | [0x34b46C8e622730C7115bB71c720F939274F73bEc](https://etherscan.io/address/0x34b46C8e622730C7115bB71c720F939274F73bEc) | | RedeemQueue (ETH) | [0xa1CE84069E5AC305075B0d54EBc12389BF674d18](https://etherscan.io/address/0xa1CE84069E5AC305075B0d54EBc12389BF674d18) | | RedeemQueue (WETH) | [0x5490729bBB5821C56F8598b856ae353435b9E993](https://etherscan.io/address/0x5490729bBB5821C56F8598b856ae353435b9E993) | | RedeemQueue (WSTETH) | [0x52135C49e6d734866AB37e127de03B479559A018](https://etherscan.io/address/0x52135C49e6d734866AB37e127de03B479559A018) | | Oracle | [0x7AD1D268Ab39612345CDACbC53bD7FFe806a3919](https://etherscan.io/address/0x7AD1D268Ab39612345CDACbC53bD7FFe806a3919) | | ShareManager | [0x4076D217fAA2813165235b4f0D9C03B67bfF9355](https://etherscan.io/address/0x4076D217fAA2813165235b4f0D9C03B67bfF9355) | | FeeManager | [0x3fEA00B3f535fCd2A77840563b48EDFee71B0E85](https://etherscan.io/address/0x3fEA00B3f535fCd2A77840563b48EDFee71B0E85) | | RiskManager | [0x22D047637D6aCD0B5525b5bD1449AE9f9575FEc5](https://etherscan.io/address/0x22D047637D6aCD0B5525b5bD1449AE9f9575FEc5) | | Subvault 0 | [0x2e2b73616e67c1C3660111baE981483a126e8F7a](https://etherscan.io/address/0x2e2b73616e67c1C3660111baE981483a126e8F7a) | | Verifier 0 | [0x6AF924f6450b2B5FdE62160bdD3eD076Ad92BCa1](https://etherscan.io/address/0x6AF924f6450b2B5FdE62160bdD3eD076Ad92BCa1) | | Subvault 1 | [0x8dCEEfBD6E3A9D1Af10C3e3c757Cb32FeB8b3e2a](https://etherscan.io/address/0x8dCEEfBD6E3A9D1Af10C3e3c757Cb32FeB8b3e2a) | | Verifier 1 | [0x2c862fD2D1802a4B7186f80cA36F6eA6bECf8613](https://etherscan.io/address/0x2c862fD2D1802a4B7186f80cA36F6eA6bECf8613) | | Timelock controller | [0xd2Be05F489202A1ADb20d200c89624AcbF403ae7](https://etherscan.io/address/0xd2Be05F489202A1ADb20d200c89624AcbF403ae7) | | ProxyAdmin | [0x55d9ecEB5733F72A48C544e20D49859eC92Fba5F](https://etherscan.io/address/0x55d9ecEB5733F72A48C544e20D49859eC92Fba5F) | | LazyAdmin | [0x8907D6089fC71AA6a9a7bb9EC5b1170e92489ebf](https://etherscan.io/address/0x8907D6089fC71AA6a9a7bb9EC5b1170e92489ebf) | | ActiveAdmin | [0xD95cb50F204B8B84606751F262b407C08528c85 ](https://etherscan.io/address/0xD95cb50F204B8B84606751F262b407C08528c85) | | OracleUpdater | [0xe5Bc509b277f83F2bF771D0dcB16949D4e175f09 ](https://etherscan.io/address/0xe5Bc509b277f83F2bF771D0dcB16949D4e175f09) | | Curator | [0xcca5BafEa783B0Ed8D11FD6D9F97c155332A16b8](https://etherscan.io/address/0xcca5BafEa783B0Ed8D11FD6D9F97c155332A16b8) | | EmergencyPauser | [0x8907D6089fC71AA6a9a7bb9EC5b1170e92489ebf](https://etherscan.io/address/0x8907D6089fC71AA6a9a7bb9EC5b1170e92489ebf) | | tqETH Treasury | [0x8907D6089fC71AA6a9a7bb9EC5b1170e92489ebf](https://etherscan.io/address/0x8907D6089fC71AA6a9a7bb9EC5b1170e92489ebf) | | Contract | Address | | ------------------- | --------------------------------------------------------------------------------------------------------------------- | | Vault | [0x7207595E4c18a9A829B9dc868F11F3ADd8FCF626](https://etherscan.io/address/0x7207595E4c18a9A829B9dc868F11F3ADd8FCF626) | | DepositQueue (USDC) | [0xA9e5120dD134E42BE7EBA828C5eA857dc9c75D83](https://etherscan.io/address/0xA9e5120dD134E42BE7EBA828C5eA857dc9c75D83) | | DepositQueue (USDT) | [0xeB9257C278ab03F30Fe889D98074c0eBDFB6779a](https://etherscan.io/address/0xeB9257C278ab03F30Fe889D98074c0eBDFB6779a) | | DepositQueue (MUSD) | [0x5c5C00D7bD4b6AC55466284c2963b3a0D3568Ad3](https://etherscan.io/address/0x5c5C00D7bD4b6AC55466284c2963b3a0D3568Ad3) | | RedeemQueue (USDC) | [0xE1fC4025c4C62d0e3876296E1cE86EBB1b6dceC2](https://etherscan.io/address/0xE1fC4025c4C62d0e3876296E1cE86EBB1b6dceC2) | | RedeemQueue (USDT) | [0x57543a4F671829B3F11241C9EC1432d258810Da1](https://etherscan.io/address/0x57543a4F671829B3F11241C9EC1432d258810Da1) | | RedeemQueue (MUSD) | [0x7545F100D91a335187ADB57a0F02356408D2DCba](https://etherscan.io/address/0x7545F100D91a335187ADB57a0F02356408D2DCba) | | Oracle | [0xccB10707cc3105178CBef8ee5b7DC84D5d1b277F](https://etherscan.io/address/0xccB10707cc3105178CBef8ee5b7DC84D5d1b277F) | | ShareManager | [0xe4741d6901C77Da80FAEeD7E2fE10c8b348Bcc84](https://etherscan.io/address/0xe4741d6901C77Da80FAEeD7E2fE10c8b348Bcc84) | | FeeManager | [0x7D3074904e3ccB61671fd3716dD4acE565B18320](https://etherscan.io/address/0x7D3074904e3ccB61671fd3716dD4acE565B18320) | | RiskManager | [0xc73EFdb68b839932933bf9eB4dB908fcA33677B3](https://etherscan.io/address/0xc73EFdb68b839932933bf9eB4dB908fcA33677B3) | | Timelock controller | [0xb3Ffd1a8C0cBA695DfA32603851BF99dDB6F7354](https://etherscan.io/address/0xb3Ffd1a8C0cBA695DfA32603851BF99dDB6F7354) | | Contract | Address | | ------------------ | -------------------------------------------------------------------------------------------------------------------------- | | Vault | [0x07AFFA6754458f88db83A72859948d9b794E131b](https://explorer.mezo.org/address/0x07AFFA6754458f88db83A72859948d9b794E131b) | | Subvault #0 | [0x6F05747CdFe61b998f928CE509547CB630A981a1](https://explorer.mezo.org/address/0x6F05747CdFe61b998f928CE509547CB630A981a1) | | Verifier | [0x955fbdD0D65d719A2F815ED55401997c863E12b4](https://explorer.mezo.org/address/0x955fbdD0D65d719A2F815ED55401997c863E12b4) | | Oracle | [0xe3FDB2436c0F6B16F6b6ed903B90bE8BF0D0Cf85](https://explorer.mezo.org/address/0xe3FDB2436c0F6B16F6b6ed903B90bE8BF0D0Cf85) | | ShareManager | [0xc5834dc9EDe2b1d6aE7e52150e95Ccfd12df0999](https://explorer.mezo.org/address/0xc5834dc9EDe2b1d6aE7e52150e95Ccfd12df0999) | | FeeManager | [0x8B01DC1D71bB1270622139565C1Ead0FaD875F29](https://explorer.mezo.org/address/0x8B01DC1D71bB1270622139565C1Ead0FaD875F29) | | RiskManager | [0x3ab2603982F120F27EE8C40d3299B8446a7bDc88](https://explorer.mezo.org/address/0x3ab2603982F120F27EE8C40d3299B8446a7bDc88) | | TimelockController | [0x8FBc3241b37C8Ab39Bd805D3233F63643c879C6C](https://explorer.mezo.org/address/0x8FBc3241b37C8Ab39Bd805D3233F63643c879C6C) | | Contract | Address | | -------------------- | --------------------------------------------------------------------------------------------------------------------- | | Vault | [0x63a76a4a94cAB1DD49fcf0d7E3FC53a78AC8Ec5C](https://etherscan.io/address/0x63a76a4a94cAB1DD49fcf0d7E3FC53a78AC8Ec5C) | | Subvault #0 | [0x699d09f862e0d3D093b522562c42e9eFBcEE207f](https://etherscan.io/address/0x699d09f862e0d3D093b522562c42e9eFBcEE207f) | | Verifier | [0xECD83240Bdfbb51F17C6c6D0F5805959C3438AC7](https://etherscan.io/address/0xECD83240Bdfbb51F17C6c6D0F5805959C3438AC7) | | Oracle | [0xf40bC75d53eA7a015f2452F557e3353D20c2f419](https://etherscan.io/address/0xf40bC75d53eA7a015f2452F557e3353D20c2f419) | | ShareManager | [0x171b8E43bB751A558b2b1f3C814d3c96D36cCf2B](https://etherscan.io/address/0x171b8E43bB751A558b2b1f3C814d3c96D36cCf2B) | | FeeManager | [0xa93DD91a799Ca5015fa182514959A2bB4772B4e6](https://etherscan.io/address/0xa93DD91a799Ca5015fa182514959A2bB4772B4e6) | | RiskManager | [0x73ea494Ea0eca57b686169203a85F64505457a2e](https://etherscan.io/address/0x73ea494Ea0eca57b686169203a85F64505457a2e) | | TimelockController | [0x3279Ef5414a97dDFa6DA89308AeD54e6a0D75Df2](https://etherscan.io/address/0x3279Ef5414a97dDFa6DA89308AeD54e6a0D75Df2) | | DepositQueue (cbBTC) | [0x0ee16cF1a0CA0D1cA070a6FEC1595888B22Dd02f](https://etherscan.io/address/0x0ee16cF1a0CA0D1cA070a6FEC1595888B22Dd02f) | | Vault | [0x06ED1E2167AA7FBf2476c5A2D220Bf702559Dcf8](https://explorer.mezo.org/address/0x06ED1E2167AA7FBf2476c5A2D220Bf702559Dcf8) | | --------------------- | -------------------------------------------------------------------------------------------------------------------------- | | Subvault #0 | [0x694066A256DcbAC4301843d17C863A52380B6316](https://explorer.mezo.org/address/0x694066A256DcbAC4301843d17C863A52380B6316) | | Verifier | [0xbbEd9cF82B7531aa542e32Be4E00c477aa8760E2](https://explorer.mezo.org/address/0xbbEd9cF82B7531aa542e32Be4E00c477aa8760E2) | | Oracle | [0x06886f9F737999c592e306Aa0090F2d8C2a01C13](https://explorer.mezo.org/address/0x06886f9F737999c592e306Aa0090F2d8C2a01C13) | | ShareManager | [0x8FB0EB4BB6CA5cf3883E83734BD5bD77a87CC20E](https://explorer.mezo.org/address/0x8FB0EB4BB6CA5cf3883E83734BD5bD77a87CC20E) | | FeeManager | [0xb69FF5f7528391E18D91f448f79498575df66146](https://explorer.mezo.org/address/0xb69FF5f7528391E18D91f448f79498575df66146) | | RiskManager | [0x8982d843F25b357720C0dbbEfd03dbE9824d0174](https://explorer.mezo.org/address/0x8982d843F25b357720C0dbbEfd03dbE9824d0174) | | TimelockController | [0xE7A5363B869D4794544E4fF9Fd9ee1A6F038b517](https://explorer.mezo.org/address/0xE7A5363B869D4794544E4fF9Fd9ee1A6F038b517) | | DepositQueue (mcbBTC) | [0x28413A9D4b25BFff5Cf3322F1013aB3849bf89Ad](https://explorer.mezo.org/address/0x28413A9D4b25BFff5Cf3322F1013aB3849bf89Ad) | | Contract | Address | | ------------------- | --------------------------------------------------------------------------------------------------------------------- | | Vault | [0xa8A3De0c5594A09d0cD4C8abc4e3AaB9BaE03F36](https://etherscan.io/address/0xa8A3De0c5594A09d0cD4C8abc4e3AaB9BaE03F36) | | Subvault #0 | [0xC22642ad548183aFbe389dc667d698C60f3D9a22](https://etherscan.io/address/0xC22642ad548183aFbe389dc667d698C60f3D9a22) | | Verifier | [0x6e9B4381900f19054916d5DBf238B99ED017F49d](https://etherscan.io/address/0x6e9B4381900f19054916d5DBf238B99ED017F49d) | | Oracle | [0x1786e893dB43aBe03517Bd99985aEEdC3EE4848F](https://etherscan.io/address/0x1786e893dB43aBe03517Bd99985aEEdC3EE4848F) | | ShareManager | [0x43f084bdBC99409c637319dD7c544D565165A162](https://etherscan.io/address/0x43f084bdBC99409c637319dD7c544D565165A162) | | FeeManager | [0x95B46F24cf5425b117Ac24E498852ADbD705Feb7](https://etherscan.io/address/0x95B46F24cf5425b117Ac24E498852ADbD705Feb7) | | RiskManager | [0x8626d581C16FE972beC234BC041cEce47CAb9c55](https://etherscan.io/address/0x8626d581C16FE972beC234BC041cEce47CAb9c55) | | TimelockController | [0x972Ae54BF6950FDe7539a803CC5cD71B0F2F0CB2](https://etherscan.io/address/0x972Ae54BF6950FDe7539a803CC5cD71B0F2F0CB2) | | DepositQueue (tBTC) | [0x0823b68c7e00B327f97b1Bf48eD44ef9CD11fb74](https://etherscan.io/address/0x0823b68c7e00B327f97b1Bf48eD44ef9CD11fb74) | | DepositQueue (WBTC) | [0xb10feC1df8FCFF2bf06fbd1AeAc34B87EA4e9AC2](https://etherscan.io/address/0xb10feC1df8FCFF2bf06fbd1AeAc34B87EA4e9AC2) | | Contract | Address | | ------------------ | -------------------------------------------------------------------------------------------------------------------------- | | Vault | [0x807D4778abA870e4222904f5b528F68B350cE0E0](https://explorer.mezo.org/address/0x807D4778abA870e4222904f5b528F68B350cE0E0) | | Subvault #0 | [0x26310E42d8DE572a27Acc6C8D77946968baC5E79](https://explorer.mezo.org/address/0x26310E42d8DE572a27Acc6C8D77946968baC5E79) | | Verifier | [0xe179A0B3e741ED19DE305dd1aA401268c9b34D24](https://explorer.mezo.org/address/0xe179A0B3e741ED19DE305dd1aA401268c9b34D24) | | Oracle | [0xd65b33625Bb2214ee18545207Ad16CB3342A748a](https://explorer.mezo.org/address/0xd65b33625Bb2214ee18545207Ad16CB3342A748a) | | ShareManager | [0xE2232789D4cF5bb1ffaDA1a105Cc59B18d639318](https://explorer.mezo.org/address/0xE2232789D4cF5bb1ffaDA1a105Cc59B18d639318) | | FeeManager | [0xC047F4C95Dd253A8369C49Ef963236611730Fd00](https://explorer.mezo.org/address/0xC047F4C95Dd253A8369C49Ef963236611730Fd00) | | RiskManager | [0xcA3eD47184bD56B3eD4Ae9eACdc3D20c05FbCb0A](https://explorer.mezo.org/address/0xcA3eD47184bD56B3eD4Ae9eACdc3D20c05FbCb0A) | | TimelockController | [0x0be428356914e587cEB97DBddFBC894d7a594656](https://explorer.mezo.org/address/0x0be428356914e587cEB97DBddFBC894d7a594656) | | DepositQueue (BTC) | [0x88e2641C6d6dACdc451F5d91787d7a58cbc6B6B1](https://explorer.mezo.org/address/0x88e2641C6d6dACdc451F5d91787d7a58cbc6B6B1) | Smart contracts of earnETH | Component | Address | | -------------------------- | ------------------------------------------ | | Vault | 0x6a37725ca7f4CE81c004c955f7280d5C704a249e | | DepositQueue (ETH) | 0x1db7094Ef0D994B0b62f6Cd67dB801ad194999A8 | | SyncDepositQueue (ETH) | 0xb99394f8b95d426Cb2F013B857C74aCC924b20D5 | | DepositQueue (WETH) | 0x3Fc48660d02e59fBedD0a5Cc18a5580D1f8dD6A4 | | SyncDepositQueue (WETH) | 0xCe6C2505fEF74d2dE10FCF1d534cB73eCc837976 | | DepositQueue (wstETH) | 0xe39EED9A454C4918F8d0682062777cB251cd513F | | SyncDepositQueue (wstETH) | 0xECD2Bfe725fa14f5Ed86e9bDcc0eA4b34A4ed522 | | RedeemQueue (wstETH) | 0x095bFAca9f1c6F2B063Cd67C6d6bfcd0c3aaB7b4 | | DepositQueue (GG) | 0x411172F1E5310d03b38128F2a294F2e33c691B30 | | SyncDepositQueue (GG) | 0x2792004b709E3E88b8FCCb06c3C5e1A6dff0EC2B | | DepositQueue (strETH) | 0x268ea1cc674cdaE200c4609E7b09d03Dc618E663 | | SyncDepositQueue (strETH) | 0xA4F23f56442C01a478af20fe06b9F5f8f05aDD96 | | DepositQueue (DVstETH) | 0x4bDd2Ea1E20acb13f2758190c92a84175107A86f | | SyncDepositQueue (DVstETH) | 0xA80f247b92C79740b0610b754403D5cb0bf216b5 | | Oracle | 0xAda1f4c24603aB2fe5aBd35BCD12370e98A20358 | | ShareManager | 0xBBFC8683C8fE8cF73777feDE7ab9574935fea0A4 | | FeeManager | 0xed4Fac879eE86F3aB0101993A3713e7cAA0488E1 | | RiskManager | 0xa2a4C4ecE27229aF51c546844AB752824Ccb557e | | Subvault 0 | 0xC5901C2481ca9C26398A9Da258b13717894bfebF | | Verifier 0 | 0xBc46B79d79fCac1F4232D4Da1BA31aCED0AABFE0 | | Subvault 1 | 0x7F515C80fA4C1FCFF34F0329141A9C3b20468FE5 | | Verifier 1 | 0xc0FC0B74923A80Af21B1E49633cAA309f432140F | | Timelock controller | 0x363Ba8843d06BA5968f55C26aB055162eDd62189 | | OracleSubmitter | 0xFbD83f7C531D35D99392a5A20bb5F1e75E97076e | Smart contracts of earnUSD
| Component | Address | | ----------------------- | ------------------------------------------ | | Vault | 0x014e6DA8F283C4aF65B2AA0f201438680A004452 | | DepositQueue (USDC) | 0xC75E7E73B25fEa8bB23EB55CC48BA55067b5be76 | | SyncDepositQueue (USDC) | 0xf6AFAf6afcAe116dD37A779D50fE6c5fa6f8C8f5 | | RedeemQueue (USDC) | 0x9e36A74FE278906a76e7615263e46a83fC40c47F | | DepositQueue (USDT) | 0xEeC5041c47Cba1e31321AC6941Bf09Ad60645B73 | | SyncDepositQueue (USDT) | 0x534d0bEb82C47cf703BFb9E959297658b65Ec8E9 | | Oracle | 0x827044735c9708a2cf850e7Ea37EBa43bc786028 | | ShareManager | 0x4Ce1ac8F43E0E5BD7A346A98aF777bF8fbeA1981 | | FeeManager | 0x72fa23f40e08eB9E45953233b2Dd9665E347e8Dc | | RiskManager | 0x7b1e06C46d4510277FC37a37bBeF65F3794fdDE4 | | Subvault 0 | 0x77B9441d5Cb89fca435190A9B6D108ad4B00ccFd | | Verifier 0 | 0xB65A8E0937c77a76C3f4F86A1110f81A299CB481 | | Timelock Controller | 0xdA6Da82DFF8cD29D828e4775Cc003f504A968845 | | OracleSubmitter | 0xB105DaEeFEb1390ce49172c99E3e12C607367156 |
### **Actors** | Actor | Address | | -------------- | ------------------------------------------ | | ProxyAdmin | 0x81698f87C6482bF1ce9bFcfC0F103C4A0Adf0Af0 | | LazyAdmin | 0x0Dd73341d6158a72b4D224541f1094188f57076E | | ActiveAdmin | 0x982aB69785f5329BB59c36B19CBd4865353fEf10 | | Curator | 0x9745F161b0160a99924845BeFCE1d7b9Daee6899 | | OracleUpdater | 0x93a797643d74fC81e7A51F3f84a9D78F930435D1 | | OracleAccepter | 0x0Dd73341d6158a72b4D224541f1094188f57076E | | Treasury | 0xcCf2daba8Bb04a232a2fDA0D01010D4EF6C69B85 | | LidoPauser | 0xA916fD5252160A7E56A6405741De76dc0Da5A0Cd | | MellowPauser | 0x6E887aF318c6b29CEE42Ea28953Bd0BAdb3cE638 | ### **Addresses** | Component | Address | | ----------------------- | ------------------------------------------ | | SwapModule 0 | 0x28c4c26b4d4eA70434906C270cF26B995583c08C | | Subvault 0 | 0xD6D3a0f4dd3d48bF3653c0549aac6b3516dD933B | | Verifier 0 | 0x1e87c6fba77966A5Ff6BF83FAc4Ea76D978A733f | | Vault | 0xDF0fb76Df2c21F79798949A4E886cd22D1C085d7 | | SyncDepositQueue (USDC) | 0xEdcd9C257719799435B05C28ff9a8a34e6872bE0 | | RedeemQueue (USDC) | 0x59fC26AFFF725eBb77Db8E1de14572e5eA9e87EB | | SyncDepositQueue (USDT) | 0xeC3EE7F7669b7ce0aC91c4638e4b89c9F40E179F | | Oracle | 0x8d229B565A0c6Bf2d693C343bea0Ec96103dEF5f | | ShareManager | 0xd9543AfF8A859F6B34f80A9A230B277c89ACdda4 | | FeeManager | 0x31325D52B763B1a43d5564114FA4A4ce62148716 | | RiskManager | 0x24d5300b6503a7358581EE5bb3651b5bC3F6f835 | | Timelock controller | 0xD0e9094E7E26ff133C349ACd9993743DCc15cA5c | | OracleSubmitter | 0x03852b7138c6704F8F46e87768399616D31Cf733 | #### Actors | Role | Address | | -------------- | ------------------------------------------ | | ProxyAdmin | 0x81698f87C6482bF1ce9bFcfC0F103C4A0Adf0Af0 | | LazyAdmin | 0x0Dd73341d6158a72b4D224541f1094188f57076E | | ActiveAdmin | 0x982aB69785f5329BB59c36B19CBd4865353fEf10 | | Curator | 0x9745F161b0160a99924845BeFCE1d7b9Daee6899 | | OracleUpdater | 0x93a797643d74fC81e7A51F3f84a9D78F930435D1 | | OracleAccepter | 0x0Dd73341d6158a72b4D224541f1094188f57076E | | Treasury | 0xcCf2daba8Bb04a232a2fDA0D01010D4EF6C69B85 | | LidoPauser | 0xA916fD5252160A7E56A6405741De76dc0Da5A0Cd | | MellowPauser | 0x6E887aF318c6b29CEE42Ea28953Bd0BAdb3cE638 | #### Addresses | Component | Address | | ------------------- | ------------------------------------------ | | Subvault 0 | 0x6CD300d6D848cb315EfaE2d874b2Ec8Ad4897977 | | Verifier 0 | 0x67276a558638073D43068CbD91e2E6b478963742 | | Vault | 0xb7651cae9c8de82B188990CFD44aB728dC2Fa061 | | Oracle | 0x0C7E836EB1d30E2f2f02D0F85e64449D355ecFd0 | | ShareManager | 0xcfe8DbC6a2df2d8eb7F0a4d7e915C253A78B0754 | | FeeManager | 0x25958e9965B76f0B3a7809FcCc934066Aa80A540 | | RiskManager | 0xc93f1B04CDEFB5C7F86f7F2f3df4CA26c5a098Ce | | Timelock Controller | 0x0555306F5063f62a3A7896A9eaBA0754c1185a67 | | OracleSubmitter | 0x8A6a1648A39C7F3dE64282e8bF2fcD783CCF08b0 | | Contract Address | Address | | ------------------- | ------------------------------------------ | | Deployer | 0x4d551d74e851Bd93Ce44D5F588Ba14623249CDda | | Vault | 0x97Bb1d1b9FaA1091406A005F0B4a1658e5b542Eb | | DepositQueue (RBTC) | 0x0f04AE0D8Ebf40b2D3DDB51d9dba279Fe9C7F220 | | RedeemQueue (RBTC) | 0x2177f2b5A9C669BfBc1e68A07DBa52908Cd01efC | | Oracle | 0x4652207b2df6F3Bcee350702E66A2C17a96dBdDB | | OracleSubmitter | 0xf00790d7F8Db7AD26FB3e84fe1Cc7f084D01484c | | ShareManager | 0x7011cb71431abFB4ACcDfCF1Ee5291b898487229 | | FeeManager | 0xAFB0CF92B5fC25daA04db870064CFA12ECAe48C5 | | RiskManager | 0x3075FA2e4305172EAB8c26106057ADE96f3F4E37 | | Subvault 0 | 0x4eA6234e9bce5F5957427eC612451A347CEc03db | | Verifier 0 | 0x02CAf23746Ff8076e4b4AFa21fa4E2790d297dd5 | | Timelock controller | 0x3FF89E7Cf116462950165a31cEd1D5c8A946FcB0 |
# Overview Source: https://docs.mellow.finance/core-vaults/overview What Core Vaults are, why they exist, how they encode strategy constraints onchain, and the curated model separating depositors from curators. Core Vaults are Mellow’s primary vault architecture for deploying curated onchain structured products. They exist to address a specific class of problems that emerge once vaults move beyond simple asset routing: the need for explicit risk boundaries, predictable execution surfaces, and stable interfaces that remain valid as strategies evolve. Core Vaults formalize these requirements at the vault level. Core Vaults encode strategy constraints directly into the vault configuration. This includes which integrations can be used, which actions are permitted, and how capital can move between execution paths. Enforcement happens onchain, making strategy behavior inspectable and resistant to off-policy execution. Core Vaults are designed to be extensible without requiring protocol-specific adapters for every new integration. Instead of hard-coding execution paths per protocol, Core Vaults expose a flexible execution surface that allows curators to route capital to new DeFi venues within the bounds of existing vault constraints. This significantly reduces integration overhead and shortens time-to-deployment, enabling strategies to incorporate new protocols or shift execution paths as market conditions change. As a result, Core Vaults can adapt strategy behavior without requiring redeployment of the vault itself, while still preserving onchain enforcement of permissions and risk limits. Core Vaults operate under a curated model. Depositors supply capital to the vault, while curators operate strategy logic within predefined guardrails. Curators cannot arbitrarily change strategy behavior, all actions are bounded by the vault’s permissions, limits, and execution rules. This separation of roles allows strategies to remain actively managed while keeping risk parameters explicit and enforceable. # DVV Deployment Source: https://docs.mellow.finance/dvsteth-vault/dvv-deployment DVstETH contract addresses: vault, asset, admin, proxy admin, and curator on Ethereum mainnet. | Parameter | Value | | --------------- | -------------------------------------------------------------------------------------------------------------------------- | | Type | dvv | | Vault | [0x5E362eb2c0706Bd1d134689eC75176018385430B](http://eth.blockscout.com/address/0x5E362eb2c0706Bd1d134689eC75176018385430B) | | Asset | [0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0](http://eth.blockscout.com/address/0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0) | | VaultAdmin | [0x9437B2a8cF3b69D782a61f9814baAbc172f72003](http://eth.blockscout.com/address/0x9437B2a8cF3b69D782a61f9814baAbc172f72003) | | VaultProxyAdmin | [0x81698f87C6482bF1ce9bFcfC0F103C4A0Adf0Af0](http://eth.blockscout.com/address/0x81698f87C6482bF1ce9bFcfC0F103C4A0Adf0Af0) | | ProxyAdmin | [0x8e6c80c41450d3fa7b1fd0196676b99bfb34bf48](http://eth.blockscout.com/address/0x8e6c80c41450d3fa7b1fd0196676b99bfb34bf48) | | Curator | [0x9437B2a8cF3b69D782a61f9814baAbc172f72003](http://eth.blockscout.com/address/0x9437B2a8cF3b69D782a61f9814baAbc172f72003) | | Name | Decentralized Validator Token | | Symbol | DVstETH | # Overview Source: https://docs.mellow.finance/dvsteth-vault/overview Wrapped liquid staking token powered by Lido wstETH with DVT incentive layer. Covers Obol and SSV incentive calculation, claiming process, withdrawal mechanics, and FAQ. DVstETH is a wrapped Liquid Staking Token powered by the Lido protocol’s wstETH, allowing vault depositors to reuse their staking receipts across the DeFi ecosystem. Distributed Validator Technology (DVT) enables splitting of a validator's private key into multiple key shards, which are distributed among an independent cluster of nodes. This means no single node has access to the entire key, greatly enhancing validator key security. DVstETH users receive token incentives from the DVT providers whose validators are active in the Lido Protocol via the Simple DVT and Community Staking modules. As more DVT based validators are activated via Lido, the vault will accrue an increasing amount of incentives. ### FAQ **How is the total number of incentives for Obol and SSV Network calculated?** Users will receive incentives by depositing (W)ETH into the Vault, with the number of incentives calculated based on how much stake they have in the Vault in relation to total stake in the Vault and taking into account how long the user’s stake has been in the Vault. Incentives calculations also take into account the Distribution Rules mentioned below. Incentives for both Obol and SSV are calculated based on the validator performance for their respective validators within the Lido protocol on a daily basis. Vault stakers that follow the Distribution Rules explained above will receive an allocation of incentives based on the amount of ETH staked via the Vault and time ETH remains staked within the Vault. More info on SSV and Obol incentives [here](../points/overview). **How do I claim the incentives accrued to my vault position?** SSV incentives are claimable monthly, in line with their existing [Incentivized Mainnet Program](https://ssv.network/incentivized-mainnet/). Users will be able to claim their incentives via [SSV Rewards](https://www.ssvrewards.com/) following the distribution of incentives as noted on the [SSV Governance Forum](https://forum.ssv.network/t/incentivized-mainnet-program-distributions/1256/30). Obol launched a community airdrop in January 2025 that provided a 7.5% community allocation. Recently, Obol has launched their [Incentives Program](https://obol.org/incentives), with 2.5% of the Obol token supply eligible each year. Obol incentives are currently being accrued to users, however are not yet claimable. Additional information will be provided when users can claim incentives.