@@ -29,15 +29,11 @@ An ERC-7540 vault must have at least one async side (deposit or redeem). If both
2929
3030Every 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
5349The 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
7759Each 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
197179The 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
211188The 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
353330The 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
369343This ensures ` convertToShares ` / ` convertToAssets ` reflect the real exchange rate at all times, even while requests are in flight.
0 commit comments