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.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)
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 aDepositQueue 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
- Pick a deposit queue from
vault.deposit_queues. You can match byqueue.assetaddress andqueue.typedeposit type (“sync” | “async”). - Check
queue.is_paused === false. Throw early if paused. - Whitelist check (permissioned vaults only): Read
shareManager.flags(). Ifflags.hasWhitelist === true, callshareManager.isDepositorWhitelisted(userAddress, merkleProof). If it returnsfalse, the deposit will revert withDepositNotAllowed. For public vaults (hasWhitelist === false), pass[]as the proof. - Find the matching
Tokeninvault.deposit_tokensforqueue.asset. - Parse the human-readable amount:
parseUnits(amount, token.decimals). - Validate
parsedAmount > 0nandparsedAmount <= UINT224_MAX. - Only one pending deposit request per user is allowed per queue. For async queues, call
requestOf(userAddress)and checktimestamp === 0nbefore depositing. - If the asset is native ETH (
queue.asset === NATIVE_ETH_ADDRESS): check the user’s ETH balance, then calldeposit()withvalue = parsedAmount. - If the asset is an ERC20:
- Read
allowance(userAddress, queueAddress). - If
currentAllowance > 0n, sendapprove(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)withvalue = 0n.
- Read
- For async queues: do not expect shares immediately. Poll
claimableOfor listen forDepositRequestClaimedevents, then callclaim.
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 - callclaim instead.
Steps
- Call
requestOf(userAddress)on the deposit queue. Checktimestamp > 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
8. Claim Deposit (async)
Overview
After the oracle processes an async deposit request, vault shares are held in the queue contract ready for collection. Callclaim to transfer them to the user.
Steps
- Call
claimableOf(userAddress). If> 0n, shares are ready to claim. - If
claimableOfreturns0n, callrequestOf(userAddress). Iftimestamp > 0n, the oracle has not yet processed the request - wait and retry later. - Call
claim(userAddress). Returnstruewhen 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
- Pick a redeem queue from
vault.redeem_queues. - Check
queue.is_paused === false. - 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. UsesharesOfif you want the total including locked shares.
- Call
- 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 > 0nandparsedShares <= 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 callclaimRedeem(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. TherequestsOf 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
100each iteration. - Stop when a page returns fewer than
100items.
- Start with
- Filter to requests where
isClaimable === true. - Extract the
timestampfield from each claimable request. Cast tonumber- timestamps areuint32values, safely representable as JavaScript numbers until year 2106. - Call
claim(userAddress, timestamps). Returns the totalassetstransferred.
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. TheFeeManager 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 |