From c8bedfa684161afa3844e61aade692c924a35b4e Mon Sep 17 00:00:00 2001 From: OpenZeppelin Docs Bot Date: Fri, 15 May 2026 22:01:06 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9A=20Update=20API=20docs=20for=20Open?= =?UTF-8?q?Zeppelin/openzeppelin-community-contracts=20master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Repository: OpenZeppelin/openzeppelin-community-contracts - Ref: master - Trigger: commit - Output: content/community-contracts/api - Timestamp: 2026-05-15 22:01:06 UTC Auto-generated via workflow_dispatch --- content/community-contracts/erc7540.mdx | 76 ++++++++----------------- 1 file changed, 25 insertions(+), 51 deletions(-) diff --git a/content/community-contracts/erc7540.mdx b/content/community-contracts/erc7540.mdx index 94967bc4..206d1cd5 100644 --- a/content/community-contracts/erc7540.mdx +++ b/content/community-contracts/erc7540.mdx @@ -29,15 +29,11 @@ An ERC-7540 vault must have at least one async side (deposit or redeem). If both Every async request transitions through three states: -``` - requestDeposit() Fulfillment deposit() / mint() - requestRedeem() withdraw() / redeem() - ┌─────────┐ ┌─────────┐ ┌───────────┐ ┌─────────┐ - │ (none) │ ──────────────► │ Pending │ ──────────► │ Claimable │ ───────────────► │ Claimed │ - └─────────┘ └─────────┘ └───────────┘ └─────────┘ - Assets/shares Ready to claim Shares/assets - locked in vault delivered to - receiver +```mermaid +flowchart LR + None(["(none)"]) -->|"requestDeposit() / requestRedeem()"| Pending["Pending
Assets/shares locked in vault"] + Pending -->|Fulfillment| Claimable["Claimable
Ready to claim"] + Claimable -->|"deposit() / mint() / withdraw() / redeem()"| Claimed["Claimed
Shares/assets delivered to receiver"] ``` * ***Pending***: Assets (for deposits) or shares (for redeems) are locked in the vault. The request is not yet ready to be claimed. @@ -52,26 +48,12 @@ Requests must NOT skip the Claimable state, even if fulfillment happens in the s The implementation is split into a base contract and strategy extensions: -``` - ┌──────────────────────────────────────────────────────────┐ - │ ERC7540 (base) │ - │ │ - │ Routing logic (sync vs async) │ - │ Operator management │ - │ ERC-4626 interface │ - │ totalAssets / totalSupply adjustments │ - │ 14 virtual hooks for strategies to implement │ - └────────────────────────┬─────────────────────────────────┘ - │ - ┌────────────────────────┼─────────────────────────────────┐ - │ │ │ - ┌─────────▼──────────┐ ┌──────────▼──────────┐ ┌──────────────────▼───┐ - │ Admin strategy │ │ Delay strategy │ │ Sync strategy │ - │ │ │ │ │ │ - │ Privileged caller │ │ Time-based, no │ │ Standard ERC-4626 │ - │ fulfills with │ │ privileged caller │ │ (no async lifecycle)│ - │ explicit rate │ │ needed │ │ │ - └────────────────────┘ └─────────────────────┘ └──────────────────────┘ +```mermaid +flowchart TD + Base["ERC7540 (base)
Routing logic (sync vs async)
Operator management
ERC-4626 interface
totalAssets / totalSupply adjustments
14 virtual hooks for strategies to implement"] + Base --> Admin["Admin strategy
Privileged caller fulfills
with explicit rate"] + Base --> Delay["Delay strategy
Time-based, no
privileged caller needed"] + Base --> Sync["Sync strategy
Standard ERC-4626
(no async lifecycle)"] ``` Each strategy comes in a deposit and redeem variant. You combine exactly one deposit strategy with one redeem strategy: @@ -196,16 +178,11 @@ The `requestId` returned by the delay strategy is the absolute timestamp at whic The delay strategy uses `Checkpoints.Trace208` to track cumulative deposit/redeem amounts keyed by their maturity timepoint. Multiple requests accumulate and mature independently: -``` - Time ──────────────────────────────────────────────────────► - - t=100 t=120 t=160 t=180 - request 500 request 300 500 claimable 800 claimable - maturity=160 maturity=180 300 still pending (all matured) - - Checkpoints: - key=160 → value=500 (cumulative) - key=180 → value=800 (cumulative) +```mermaid +flowchart LR + T100["t=100
request 500
maturity=160"] --> T120["t=120
request 300
maturity=180"] --> T160["t=160
500 claimable
300 still pending"] --> T180["t=180
800 claimable
(all matured)"] + CP["Checkpoints (cumulative)
key=160 → value=500
key=180 → value=800"] + T120 -.-> CP ``` The total claimable amount at any time `T` is: @@ -352,18 +329,15 @@ During the async lifecycle, shares and assets must be held somewhere between req The base `ERC7540` contract overrides both to keep the share price accurate during the async lifecycle: -``` -totalAssets() = asset.balanceOf(vault) - _totalPendingDepositAssets - │ │ - │ └─ Assets received but not yet converted to shares. - │ Must not inflate the perceived yield. - └─ All assets in the vault, including pending ones. - -totalSupply() = ERC20.totalSupply() + _totalPendingRedeemShares - │ │ - │ └─ Shares already burned/escrowed but logically still - │ outstanding (request not yet settled). - └─ The on-chain ERC-20 supply. +```mermaid +flowchart TD + TA["totalAssets() = asset.balanceOf(vault) − _totalPendingDepositAssets"] + TA --> TA1["asset.balanceOf(vault)
All assets in the vault,
including pending ones"] + TA --> TA2["_totalPendingDepositAssets
Assets received but not yet
converted to shares.
Must not inflate the perceived yield."] + + TS["totalSupply() = ERC20.totalSupply() + _totalPendingRedeemShares"] + TS --> TS1["ERC20.totalSupply()
The on-chain ERC-20 supply"] + TS --> TS2["_totalPendingRedeemShares
Shares already burned/escrowed
but logically still outstanding
(request not yet settled)"] ``` This ensures `convertToShares` / `convertToAssets` reflect the real exchange rate at all times, even while requests are in flight.