Skip to content

Commit c8bedfa

Browse files
OpenZeppelin Docs Boternestognw
authored andcommitted
📚 Update API docs for OpenZeppelin/openzeppelin-community-contracts master
- 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
1 parent 3f4d3bd commit c8bedfa

1 file changed

Lines changed: 25 additions & 51 deletions

File tree

content/community-contracts/erc7540.mdx

Lines changed: 25 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,11 @@ An ERC-7540 vault must have at least one async side (deposit or redeem). If both
2929

3030
Every async request transitions through three states:
3131

32-
```
33-
requestDeposit() Fulfillment deposit() / mint()
34-
requestRedeem() withdraw() / redeem()
35-
┌─────────┐ ┌─────────┐ ┌───────────┐ ┌─────────┐
36-
│ (none) │ ──────────────► │ Pending │ ──────────► │ Claimable │ ───────────────► │ Claimed │
37-
└─────────┘ └─────────┘ └───────────┘ └─────────┘
38-
Assets/shares Ready to claim Shares/assets
39-
locked in vault delivered to
40-
receiver
32+
```mermaid
33+
flowchart LR
34+
None(["(none)"]) -->|"requestDeposit() / requestRedeem()"| Pending["Pending<br/><i>Assets/shares locked in vault</i>"]
35+
Pending -->|Fulfillment| Claimable["Claimable<br/><i>Ready to claim</i>"]
36+
Claimable -->|"deposit() / mint() / withdraw() / redeem()"| Claimed["Claimed<br/><i>Shares/assets delivered to receiver</i>"]
4137
```
4238

4339
* ***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
5248

5349
The implementation is split into a base contract and strategy extensions:
5450

55-
```
56-
┌──────────────────────────────────────────────────────────┐
57-
│ ERC7540 (base) │
58-
│ │
59-
│ Routing logic (sync vs async) │
60-
│ Operator management │
61-
│ ERC-4626 interface │
62-
│ totalAssets / totalSupply adjustments │
63-
│ 14 virtual hooks for strategies to implement │
64-
└────────────────────────┬─────────────────────────────────┘
65-
66-
┌────────────────────────┼─────────────────────────────────┐
67-
│ │ │
68-
┌─────────▼──────────┐ ┌──────────▼──────────┐ ┌──────────────────▼───┐
69-
│ Admin strategy │ │ Delay strategy │ │ Sync strategy │
70-
│ │ │ │ │ │
71-
│ Privileged caller │ │ Time-based, no │ │ Standard ERC-4626 │
72-
│ fulfills with │ │ privileged caller │ │ (no async lifecycle)│
73-
│ explicit rate │ │ needed │ │ │
74-
└────────────────────┘ └─────────────────────┘ └──────────────────────┘
51+
```mermaid
52+
flowchart TD
53+
Base["<b>ERC7540 (base)</b><br/>Routing logic (sync vs async)<br/>Operator management<br/>ERC-4626 interface<br/>totalAssets / totalSupply adjustments<br/>14 virtual hooks for strategies to implement"]
54+
Base --> Admin["<b>Admin strategy</b><br/>Privileged caller fulfills<br/>with explicit rate"]
55+
Base --> Delay["<b>Delay strategy</b><br/>Time-based, no<br/>privileged caller needed"]
56+
Base --> Sync["<b>Sync strategy</b><br/>Standard ERC-4626<br/>(no async lifecycle)"]
7557
```
7658

7759
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
196178

197179
The delay strategy uses `Checkpoints.Trace208` to track cumulative deposit/redeem amounts keyed by their maturity timepoint. Multiple requests accumulate and mature independently:
198180

199-
```
200-
Time ──────────────────────────────────────────────────────►
201-
202-
t=100 t=120 t=160 t=180
203-
request 500 request 300 500 claimable 800 claimable
204-
maturity=160 maturity=180 300 still pending (all matured)
205-
206-
Checkpoints:
207-
key=160 → value=500 (cumulative)
208-
key=180 → value=800 (cumulative)
181+
```mermaid
182+
flowchart LR
183+
T100["<b>t=100</b><br/>request 500<br/>maturity=160"] --> T120["<b>t=120</b><br/>request 300<br/>maturity=180"] --> T160["<b>t=160</b><br/>500 claimable<br/>300 still pending"] --> T180["<b>t=180</b><br/>800 claimable<br/>(all matured)"]
184+
CP["<b>Checkpoints (cumulative)</b><br/>key=160 → value=500<br/>key=180 → value=800"]
185+
T120 -.-> CP
209186
```
210187

211188
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
352329

353330
The base `ERC7540` contract overrides both to keep the share price accurate during the async lifecycle:
354331

355-
```
356-
totalAssets() = asset.balanceOf(vault) - _totalPendingDepositAssets
357-
│ │
358-
│ └─ Assets received but not yet converted to shares.
359-
│ Must not inflate the perceived yield.
360-
└─ All assets in the vault, including pending ones.
361-
362-
totalSupply() = ERC20.totalSupply() + _totalPendingRedeemShares
363-
│ │
364-
│ └─ Shares already burned/escrowed but logically still
365-
│ outstanding (request not yet settled).
366-
└─ The on-chain ERC-20 supply.
332+
```mermaid
333+
flowchart TD
334+
TA["<b>totalAssets()</b> = asset.balanceOf(vault) − _totalPendingDepositAssets"]
335+
TA --> TA1["<b>asset.balanceOf(vault)</b><br/>All assets in the vault,<br/>including pending ones"]
336+
TA --> TA2["<b>_totalPendingDepositAssets</b><br/>Assets received but not yet<br/>converted to shares.<br/>Must not inflate the perceived yield."]
337+
338+
TS["<b>totalSupply()</b> = ERC20.totalSupply() + _totalPendingRedeemShares"]
339+
TS --> TS1["<b>ERC20.totalSupply()</b><br/>The on-chain ERC-20 supply"]
340+
TS --> TS2["<b>_totalPendingRedeemShares</b><br/>Shares already burned/escrowed<br/>but logically still outstanding<br/>(request not yet settled)"]
367341
```
368342

369343
This ensures `convertToShares` / `convertToAssets` reflect the real exchange rate at all times, even while requests are in flight.

0 commit comments

Comments
 (0)