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.