Crypto Training

ERC-7540 in Practice: Standards, Implementations, Security Pitfalls, and Alternatives

A code-first analysis of ERC-7540, ERC-7575, and ERC-7887: what the standards require, how production systems actually implement async flows, where designs diverge, recurring vulnerability patterns, and how Mellow compares as an async vault architecture outside strict ERC-7540.

Crypto Training2025-12-1413 min read

Asynchronous vaults have moved from edge-case design to mainstream infrastructure for RWAs, delayed-liquidity strategies, and controlled redemption systems.

The core standard in this space is ERC-7540, usually paired with ERC-7575, and increasingly extended with ERC-7887.

This post is a code-level comparison of those standards and real implementations, including where teams deviate intentionally.

Primary references:

Why async vaults exist#

Synchronous ERC-4626 assumes deposits/redeems can clear immediately against currently available liquidity.

Many vaults cannot guarantee that:

  • RWA and credit strategies settle on delayed schedules.
  • Exit liquidity can be batched or epoch-based.
  • External unwind can require multiple transactions and time windows.
  • Strategy NAV may be finalized periodically rather than per block.

ERC-7540 formalizes this with request-based state transitions.

flowchart LR U["User"] --> R["Request"] R --> P["Pending"] P --> C["Claimable"] C --> X["Claimed"] O["Operator or Vault Logic"] --> P O --> C

Standards map: 4626, 7540, 7575, 7887#

ERC-7540#

ERC-7540 adds request/claim lifecycle for deposits and redeems while still requiring ERC-4626 interfaces.

Core request methods:

  • requestDeposit(assets, controller, owner)
  • requestRedeem(shares, controller, owner)
  • pending*Request(requestId, controller)
  • claimable*Request(requestId, controller)

Operator model:

  • setOperator(operator, approved)
  • isOperator(controller, operator)

Key rule from the spec: async flows must use pull-based two-step claim semantics, not push settlement.

ERC-7575#

ERC-7575 externalizes share() and enables multi-asset entry points over a shared vault-share system.

This is why many ERC-7540 implementations also expose share() and ERC-165 interface support.

ERC-7887#

ERC-7887 adds cancellation state machines on top of ERC-7540:

  • cancel request
  • pending cancel
  • claimable cancel
  • cancel claim

It also requires blocking new same-side requests while cancellation is pending.

flowchart TB A["ERC-4626"] --> B["ERC-7540"] B --> C["Async requests"] B --> D["Controller and operator model"] B --> E["Pending and claimable views"] F["ERC-7575"] --> G["share() externalization"] F --> H["Multi-asset vault entry points"] B --> I["ERC-7887 extension"] I --> J["Cancel request lifecycle"] I --> K["Block new requests during pending cancel"]

The selector and semantics tension with ERC-4626#

One recurring friction point is that ERC-7540 reuses familiar ERC-4626 function names (deposit, mint, withdraw, redeem) but changes behavior in async contexts.

EIP-7540 explicitly requires reverts in preview paths depending on async mode:

  • async deposit vaults: previewDeposit and previewMint must revert.
  • async redeem vaults: previewRedeem and previewWithdraw must revert.

That practical friction appears in the OpenZeppelin discussion thread as maintainers and integrators debate how cleanly this composes with a generic ERC-4626 library model: issue #4761.

sequenceDiagram participant User participant Vault participant Queue User->>Vault: requestRedeem(shares, controller, owner) Vault->>Queue: lock or burn shares Queue-->>Vault: mark Pending Note over User,Vault: Time passes, settlement happens User->>Vault: redeem(shares, receiver, controller) Vault-->>User: transfer assets from Claimable state

Reference implementation behavior (ERC-7540-Reference)#

The reference repository is useful for understanding intended behaviors and tradeoffs.

Repository: ERC4626-Alliance/ERC-7540-Reference

Notable contracts:

Design choices worth noting:

  • Request aggregation by requestId == 0 pattern is explicitly supported.
  • Operator model is first-class.
  • Preview reverts are enforced in async modes.
  • Multiple variants show how fulfillment logic is implementation-specific.
stateDiagram-v2 [*] --> NoRequest NoRequest --> Pending: requestDeposit or requestRedeem Pending --> Claimable: internal fulfill Claimable --> Claimed: deposit or mint or redeem or withdraw Claimed --> NoRequest

Real-world implementations: what is standard, what is custom#

A) Code-confirmed implementations in reviewed repos#

ProjectEvidence of 7540 semantics in codeNotes
CentrifugeAsyncVault.sol, BaseVaults.sol, IAsyncVault.solFull async vault path plus cancellation support pathways aligned with 7887-style interfaces.
Lagoonsrc/v0.5.0/ERC7540.sol, src/v0.5.0/Vault.solEpoch-oriented async flows with additional vault-state and whitelist layers.
Superform SuperVaultSuperVault.sol, ISuperVault.solHybrid model: sync deposits, async redeems, explicit cancel-redeem path.
Amphor async vaultAsyncSynthVault.sol, IERC7540.solContest-era implementation with multiple high-impact async-state bugs found.
NashPointNode.sol, IERC7540.solNode architecture with router layer and async redeem behavior.
UltraYieldBaseControlledAsyncRedeem.sol, IBaseVault.solAsync redeem-focused design with cancellation variants and custom custody assumptions.
maxAPY MetaVaultMetaVault.sol, lib/ERC7540.solERC-7540-oriented interfaces wrapped around cross-chain strategy engine decisions.
ERC-7540 ReferencesrcReference, not production by itself, but foundational for many implementations.

B) Publicly claimed implementations or adjacent designs#

ProjectPublic signalStatus from this code review
CovePublic mention in OZ issue thread with source links: issue #4761 commentsClaimed ERC-7540 implementation; not deeply audited in this post.
MORE MarketsPublic docs and external discussions claim ERC-7540 facet style supportLocal snapshot did not provide direct 7540 markers in scanned contracts; treat as unverified here.

The big pattern: almost everyone keeps the high-level request lifecycle, but fulfillment math, batching granularity, cancellation semantics, and custody models diverge significantly.

flowchart LR subgraph StandardCore["Common 7540 core"] S1["Request functions"] S2["Pending and claimable views"] S3["Claim via 4626 methods"] S4["Operator model"] end subgraph CustomLayer["Implementation-specific layer"] C1["Epoch batch logic"] C2["NAV and pricing source"] C3["Escrow and custody model"] C4["Cancellation extensions"] C5["Compliance and transfer gates"] end S1 --> C1 S2 --> C2 S3 --> C3 S4 --> C4 S4 --> C5

Lagoon as a concrete epoch-style implementation#

Lagoon is useful as a practical epoch-7540 architecture.

Code and docs:

Lagoon-specific behaviors beyond bare standard:

  • Epoch snapshots and explicit close/open workflow.
  • Single-request constraints in specific paths (OnlyOneRequestAllowed) to simplify state transitions.
  • Whitelisting and role-based controls around who can interact.
  • Operational controls to pause/cancel request paths.
sequenceDiagram participant User participant Vault participant Epoch User->>Vault: requestDeposit or requestRedeem Vault->>Epoch: record balances in epoch bucket Note over Vault,Epoch: curator processes settlement Vault->>Epoch: snapshot assets and supply Epoch-->>Vault: conversion context User->>Vault: claimDeposit or claimRedeem Vault-->>User: shares or assets from settled epoch

Superform as hybrid async design#

Superform's v2 periphery states the SuperVault model as sync deposits plus async redeems.

References:

Interesting implementation detail:

  • previewWithdraw and previewRedeem are explicitly not implemented for async redeem path.
  • requestRedeem moves shares to escrow, then strategy handles async operations.
  • Cancellation path is built into vault interfaces.
flowchart TB U["User"] --> D["deposit sync"] D --> ST["Strategy handles accounting and fees"] ST --> M["Mint shares now"] U --> RQ["requestRedeem"] RQ --> E["Escrow shares"] E --> STR["Strategy async pipeline"] STR --> CL["claim via withdraw or redeem"]

Centrifuge as full async plus cancellation stack#

Centrifuge's vault module is one of the clearest examples of complete async architecture with manager separation.

References:

Notable architecture decisions:

  • Request managers split by responsibilities.
  • Explicit cancellation flows.
  • Router and manager boundaries for operational and integration clarity.
flowchart LR U["User"] --> V["AsyncVault"] V --> ARM["AsyncRequestManager"] ARM --> BRM["BatchRequestManager"] BRM --> HUB["Hub or epoch executor"] HUB --> BRM BRM --> ARM ARM --> V V --> U

Common design dimensions where implementations differ#

The standards intentionally leave room for different policy choices.

1) Fulfillment granularity#

  • Per-user manual fulfillment
  • Batch-based fulfillment
  • Time-delay auto-claimable fulfillment

2) Pricing point#

  • Price at request
  • Price at settlement
  • Price at claim

3) State model strictness#

  • Strict pending to claimable to claimed
  • Partial claimable balances across multiple rounds

4) Cancellation model#

  • No cancellation
  • Cancellation only for one side
  • Full 7887-style cancellation for both sides
flowchart TD A["Async vault design"] --> B["Fulfillment policy"] A --> C["Pricing policy"] A --> D["Request indexing"] A --> E["Cancellation semantics"] A --> F["Escrow or burn model"] B --> B1["Per user"] B --> B2["Epoch batch"] B --> B3["Delay based"] C --> C1["At request"] C --> C2["At settlement"] C --> C3["At claim"] D --> D1["requestId zero aggregate"] D --> D2["requestId per epoch"] D --> D3["requestId per position"]

Security: recurring vulnerabilities in async vaults#

The highest-value section in async-vault audits is almost always state-machine consistency.

Below are common bug classes observed in judged findings and audited codebases, with examples grounded in real contest findings from the Amphor async vault corpus and related implementations.

1) Claim before valid settlement context#

Pattern:

  • Claim path executes while request is still in current unsettled epoch.
  • Conversion function reads uninitialized snapshot context.
  • User claim clears pending state but receives zero or wrong output.

Observed in judged findings against Amphor async design.

2) Controller-owner-account mismatch#

Pattern:

  • Request created on behalf of one account while internal indexes update another.
  • Claim lookup uses mismatched key.
  • Shares or assets become permanently unclaimable.

3) Async math divergence between sync and async paths#

Pattern:

  • Sync path and async path use different fee inclusion or conversion baseline.
  • Equal economic action yields different shares/assets depending on route.
  • Long-term accounting drifts and unfair allocation emerges.

4) Token behavior assumptions#

Pattern:

  • Code assumes strict ERC-20 return behavior.
  • Non-standard tokens (USDT-like return patterns, WBTC allowance behavior) break flow or lock value.

5) Partial-fill and zap residue theft#

Pattern:

  • Router or zap path permits partial execution.
  • Unspent tokens remain in intermediate contract.
  • Subsequent caller can drain residual value.

6) Allowance depletion over long horizons#

Pattern:

  • "Approve max once" assumed sufficient.
  • Token decreases max allowance on each transfer.
  • Eventually pulls fail and funds become trapped without a refresh mechanism.
mindmap root((Async vault risks)) Settlement ordering Claim before settle Epoch boundary confusion Pending and claimable desync Identity model owner vs controller mismatch operator authorization bugs Math and accounting fee inclusion mismatch rounding drift request batch pro-rata errors Token integration nonstandard ERC20 returns allowance depletion semantics Router integration partial fill leftovers unsafe approvals

Practical invariant suite for ERC-7540 systems#

A robust test plan should combine unit tests, fuzzing, and invariants around state conservation and phase correctness.

Suggested invariant classes:

  1. Request conservation: pending + claimable + claimed transitions cannot create or destroy value unexpectedly.

  2. Phase safety: claim path must revert unless request is actually claimable.

  3. Identity consistency: for every request key, same identity tuple used for creation, transition, and claim.

  4. Operator safety: operator permissions must only widen access intentionally and revocation must take effect immediately.

  5. Pricing consistency: if design requires deterministic batch pricing, all participants in same batch must receive same rate.

  6. Cancellation safety: if cancel is pending for a side, new requests for that side must be blocked per design.

flowchart LR A["Request created"] --> B["pending amount increases"] B --> C["fulfill"] C --> D["claimable amount increases"] D --> E["claim"] E --> F["claimable amount decreases"] A --> G["cancel request optional"] G --> H["pending cancel"] H --> I["claimable cancel"] I --> J["cancel claim settles back"]

Where ERC-7540 is strong#

  • Standardized async vocabulary for integrators.
  • Shared operator model for delegated request management.
  • Cleaner integration target for RWA and delayed-liquidity systems.
  • Better composability than ad hoc request APIs.

Where ERC-7540 is hard in production#

  • Integrator confusion when 4626 method names retain different runtime semantics.
  • Fulfillment logic and pricing remain highly custom and operationally heavy.
  • Cancellations and partial fulfillments rapidly increase state complexity.
  • Off-chain orchestration quality often dominates user safety in practice.

Alternative async architecture: Mellow#

Mellow flexible-vaults is not a strict ERC-7540 implementation, but it solves similar async vault problems with a different architecture.

References:

Mellow architecture highlights:

  • Explicit DepositQueue and RedeemQueue contracts for request lifecycle.
  • Oracle-report-driven settlement cadence.
  • Subvault isolation for strategy execution.
  • Call-level policy enforcement through Verifier and custom verifier modules.

This is structurally similar to async request systems but with stronger strategy-execution policy tooling inside the protocol stack itself.

flowchart TB U["User"] --> DQ["DepositQueue"] U --> RQ["RedeemQueue"] O["Oracle reports"] --> V["Vault and ShareModule"] DQ --> V RQ --> V V --> SV["Subvaults"] SV --> CM["CallModule"] CM --> VF["Verifier"] VF --> EP["External protocols"]

ERC-7540 vs Mellow: concise comparison#

DimensionTypical ERC-7540 designMellow flexible-vaults
Standard interfaceERC-7540 and often 7575/7887Custom queue interfaces, ERC-4626-like user surface
Request abstractionStandardized request and claim methodsQueue modules and report-triggered handling
Strategy execution controlsOften external manager policyBuilt-in verifier and call-module gating
Settlement triggerImplementation-definedExplicit oracle report path
Integrator familiarityBetter at interface levelBetter at policy and modular operations

What high-quality ERC-7540 designs have in common#

Across strong implementations, the following properties keep reappearing:

  • Deterministic and auditable request state transitions.
  • Explicit identity model for owner, controller, operator.
  • Conversion logic tied to a clearly defined pricing point.
  • Guardrails against same-epoch or stale-context claims.
  • Cancellation semantics that avoid ambiguous overlapping states.
  • Good integration hygiene for non-standard ERC-20 behavior.
flowchart LR S["Safe async vault"] --> P1["Clear state machine"] S --> P2["Identity consistency"] S --> P3["Single source of pricing truth"] S --> P4["Cancellation discipline"] S --> P5["Token integration hardening"] S --> P6["Operational monitoring"]

Implementation checklist for new deployments#

  1. Decide whether requests are per-user aggregate (requestId == 0) or per-batch/per-position.
  2. Pin one precise pricing point and encode it in tests and documentation.
  3. Add phase-gate checks to every claim and cancel claim path.
  4. Harden token interactions with safe transfer patterns and allowance refresh strategies.
  5. Prove identity invariants for owner/controller/operator transitions.
  6. If using cancellation, enforce request blocking during pending cancel windows.
  7. Add invariant tests for conservation and no-stuck-value guarantees.
  8. Document exactly which ERC-7540 subset is implemented if hybrid.

Final perspective#

ERC-7540 is useful because it standardizes the async request vocabulary without forcing one fulfillment engine.

That flexibility is both the strength and the danger:

  • it enables adaptation to many real strategies,
  • but it shifts safety to implementation correctness and operational discipline.

If you are integrating async vaults, audit the actual settlement engine and identity model first. The interface alone is not enough.

Code index#

Standards and references#

Implementations#

Alternatives#

Context and discussion#