For the complete documentation index, see llms.txt. This page is also available as Markdown.

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.

Redemption Lifecycle

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.

4. TypeScript Interfaces & Constants

5. ABIs

Only the functions and events relevant to integrations are shown here.

5.1 Deposit Queue ABI

5.2 Redeem Queue ABI

5.3 ERC20 ABI (subset — for approval)

5.4 Vault ABI (subset — for share balance lookup)

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)

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.

Steps

1

Pick a deposit queue

Pick a deposit queue from vault.deposit_queues. You can match by queue.asset address and queue.type deposit type ("sync" | "async").

2

Check whether the queue is paused

Check queue.is_paused === false. Throw early if paused.

3

Check whitelist requirements

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.

4

Resolve token metadata

Find the matching Token in vault.deposit_tokens for queue.asset.

5

Parse and validate the amount

Parse the human-readable amount with parseUnits(amount, token.decimals).

Validate that:

  • parsedAmount > 0n

  • parsedAmount <= UINT224_MAX

6

Check for an existing pending request

Only one pending deposit request per user is allowed per queue. For async queues, call requestOf(userAddress) and check timestamp === 0n before depositing.

7

Handle native ETH deposits

If the asset is native ETH (queue.asset === NATIVE_ETH_ADDRESS), check the user's ETH balance, then call deposit() with value = parsedAmount.

8

Handle ERC20 deposits

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.

9

Wait for async claimability

For async queues: do not expect shares immediately. Poll claimableOf or listen for DepositRequestClaimed events, then call claim.

Code Example

Referral address: Pass zeroAddress (0x0000000000000000000000000000000000000000) unless you have been issued a referral address by the Mellow team.

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

1

Check for a pending request

Call requestOf(userAddress) on the deposit queue. Check timestamp > 0n — if zero, there is no pending request.

2

Check whether it is already claimable

Call claimableOf(userAddress). If > 0n, the oracle has already processed the request — you must claim it, not cancel it.

3

Cancel the request

Call cancelDepositRequest(). This function takes no arguments — it cancels the caller's own request.

Code Example

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

1

Check whether there is anything to claim

Call claimableOf(userAddress). If > 0n, shares are ready to claim.

2

Check whether the request is still pending

If claimableOf returns 0n, call requestOf(userAddress). If timestamp > 0n, the oracle has not yet processed the request — wait and retry later.

3

Claim the shares

Call claim(userAddress). Returns true when shares are successfully transferred.

Code Example

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.

Steps

1

Pick a redeem queue

Pick a redeem queue from vault.redeem_queues.

2

Check whether the queue is paused

Check queue.is_paused === false.

3

Fetch the user's redeemable share balance

  • 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.

4

Parse the share amount with vault decimals

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.

5

Validate the amount

Validate parsedShares > 0n and parsedShares <= activeShareBalance.

6

Submit the redeem request

Call redeem(parsedShares). No ETH value, no ERC20 approval — the vault contract locks shares directly from the caller.

7

Wait for settlement and claim later

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.

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

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

1

Paginate redemption requests

Paginate requestsOf(userAddress, offset, 100):

  • Start with offset = 0.

  • Increment by 100 each iteration.

  • Stop when a page returns fewer than 100 items.

2

Filter claimable requests

Filter to requests where isClaimable === true.

3

Extract timestamps

Extract the timestamp field from each claimable request. Cast to number — timestamps are uint32 values, safely representable as JavaScript numbers until year 2106.

4

Batch-claim the requests

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

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

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.

Last updated