Reentrancy
- Name: Reentrancy
- Category: exploit pattern / control-flow vulnerability / invariant-break-at-external-call boundary
- Summary: Reentrancy is the canonical smart-contract control-flow failure, but the useful review shape is a little sharper than
external call happened before state update. Healthy systems keep three questions separate:did control leave through a callback-capable edge at all,did the protocol already publish one settled accounting / authority story before that edge, anddid any resumed path still deserve to spend against that story before the frame closed. Reentrancy exploits collapse those promotions into one vaguethe transfer / hook / callback is still part of the same actionstory, then let a nested call observe or spend state that honest business logic had not finished proving yet. The DAO is the historical archetype: asplitDAO()exit path created the attacker-controlled child DAO and only later tried to burn the attacker’s parent-DAO balance, while a reward payout callback reopenedsplitDAO()before those writes landed. SpankChain preserves an earlyrefund path plus hostile tokenvariant just as cleanly:LCOpenTimeout()paid ETH, then called an attacker-supplied token contract, and only deleted the channel afterward, so one malicioustransfer()kept replaying the same unopened-channel refund until pooled hub ETH was gone. Uniswap V1 preserves the AMM pricing-side cousin cleanly: an ERC-777 sender hook fired duringtransferFrom(), and nestedtokenToEthSwapInput()calls kept selling imBTC after ETH reserves had already dropped but before token reserves had risen, turning one-sided reserve truth into profitablereentrant microtrading. Lendf.Me shows the early lending-market collateral version just as cleanly: an ERC-777 sender hook fired duringtransferFrom(), a re-entrantwithdraw()made a real collateral reduction, and the outersupply()call then restored stale balance data as if the withdrawal had never happened. Akropolis preserves a different 2020 branch of the same family: a malicious token re-entereddeposit()mid-transfer so one real DAI balance increase was counted twice and minted double poolToken claims. Origin Dollar preserves the rebasing-balance cousin of that same accounting seam: a fake token re-enteredmint()while outer-frame DAI was already sitting in the vault, so one real stablecoin inflow first justified explicit OUSD minting and then justified_rebase()/changeSupply()as if fresh backing had arrived again. Grim Finance preserves the multi-frame vault-share cousin: a user-chosen token transfer and a mutable deposit checkpoint let one real LP deposit at the bottom of the stack mint the same vault-share claim several times on unwind. Revest adds the receipt-identity cousin: ERC-1155 mint delivery happened beforefnftsCreatedadvanced, so anonERC1155Received()callback racedgetNextId(), overwrote the config of a still-in-flight FNFT series, and turned millions of worthless receipts into valid vault-withdrawal claims. Omni adds the NFT-lending cousin: ERC-721safeTransferFrom()inside withdraw and liquidation paths handed control toonERC721Received(), and the attacker used those callbacks to make Omni first honor and then forget NFT-backed debt. Exactly Protocol adds a useful helper-layer cousin: a fake market first became the permit-verification anchor and then the next privileged callback target, soDebtManagerrebound its acting-account context to a victim and a maliciousdeposit()re-enteredcrossDeleverage()against real markets. Astroport / Terra adds the cross-chain timeout cousin:ibc-hookslet a malicious CosmWasm contract re-enterMsgTimeoutbefore the packet commitment for the same ICS-20 transfer was deleted, so one timed-out refund path kept minting counterfeit bridged assets that were later cashed out through Astroport-linked migration liquidity and cross-chain routes. Clober adds the attacker-installed strategy-hook cousin: the protocol let a user-created pool bind arbitrarystrategycode into the LP burn path, then calledburnHook()before reserve accounting was safely sealed, so one malicious plugin contract could make nested burns cash out against the same WETH twice. f(x) Protocol adds the nested-flash-loan context cousin: one router reused the same ambientHAS_FLASH_LOANbit across Morpho and Balancer callbacks, so attacker-controlled code inside the first close flow could open a second flash loan, self-call the privileged close hook against a victim-approved position, and sweep the victim’s released collateral out through the attacker’s outer payout path. GMX V1 adds the keeper-mediated modular-systems cousin: an ETH refund callback inOrderBook.executeDecreaseOrder()re-enteredVault.increasePosition()while a temporary leverage window was still open, bypassedShortsTracker, and turned split short-accounting state into fake GLP AUM. Sometimes the surprise is in the application code; sometimes, as the 2023 Curve/Vyper incident showed, the surprise is that the language-level guard developers relied on was not actually enforcing the lock-sharing semantics they thought they had. Sentiment shows the read-only cousin clearly: the callback window lives in Balancer, but the money is lost in the integrating lending protocol that reads inconsistent LP state as if it were collateral truth. For that narrower integration-side subfamily, see read-only-reentrancy. - What it is:
- A function performs an external interaction before its state is fully updated.
- The external interaction can be an ETH transfer, a token transfer with hooks, a callback-based standard, a bridge / IBC middleware callback, or any contract call that gives an attacker-controlled contract execution.
- The attacker re-enters the same function or a sibling function while balances, debt, collateral, shares, rewards, or authorization flags still reflect the pre-interaction state.
- The result is usually over-withdrawal, over-borrowing, double-claiming, stale-price use, or some other violation of a protocol invariant.
- Typical preconditions:
- The protocol makes an external call before reducing balances, recording debt, updating shares, or otherwise sealing the relevant accounting.
- A callback path exists. Common examples are
call{value: ...}(""), ERC-777 hooks, ERC-721 / ERC-1155 safe-transfer receivers, flash-loan callbacks, bridge / IBC timeout callbacks, and arbitrary plugin / adapter calls. - The attacker can arrange for the callee to be a contract they control or can otherwise force execution through a hook-capable asset.
- The protocol has a second callable path that still trusts stale state during the same transaction.
- Common vulnerable surfaces:
- withdraw / redeem / unwrap functions
- borrow paths that transfer assets before recording debt
- liquidation and seize flows that interact with external token logic mid-update
- reward claims, fee withdrawals, and refund paths that send ETH or tokens to attacker-controlled receivers
- vault share mint / burn paths with callback-capable assets
- delegated leverage / deleverage helpers or account-management wrappers that both authenticate and call a user-supplied integration endpoint
- cross-contract accounting where Contract A assumes Contract B is already consistent after an external call
- view functions that can expose inconsistent intermediate state to integrators, creating read-only reentrancy risk
- Exploit sequence:
- The attacker enters a function that passes its initial checks.
- The protocol performs an external interaction before finalizing internal state.
- The attacker-controlled callee or token hook calls back into the victim.
- The re-entrant call sees stale balances, stale debt, stale collateral, or another broken invariant.
- The attacker repeats the profitable path until the protocol finally updates state, then exits with excess assets or with a position whose losses are socialized to the protocol.
- Variants:
- Single-function reentrancy — the classic pattern where the same function is re-entered before storage is updated.
- Cross-function reentrancy — function A leaves the protocol inconsistent and function B can be abused during that window.
- Cross-contract reentrancy — one contract updates late while another contract in the system still trusts the old state.
- Hook-induced reentrancy — token or NFT standards with receiver/sender hooks turn an apparently ordinary transfer into an attacker-controlled callback.
- Read-only reentrancy — no direct state write happens during the callback, but an external protocol reads inconsistent mid-transaction state and makes a bad pricing, collateral, or routing decision.
- Detection heuristics:
- Treat every external call as a control-flow handoff, not just explicit low-level
call. - Look for storage writes that happen after token transfers, ETH sends, plugin calls, router calls, or hook-triggering standards.
- Ask whether the protocol’s invariants hold at every potential callback point, not only at function exit.
- Check sibling functions, not just the one that initiated the external interaction.
- Model hook-capable assets explicitly. ERC-777-style integrations deserve extra suspicion because a transfer can run arbitrary receiver logic.
- Treat every external call as a control-flow handoff, not just explicit low-level
- Defenses:
- Apply Checks-Effects-Interactions rigorously: finalize internal accounting before external interactions.
- Use reentrancy locks on every sensitive entrypoint, not just the most obvious one.
- Prefer pull-payment or pull-withdrawal designs when practical.
- Minimize callback-capable integrations unless the protocol has explicitly modeled them.
- Keep cross-market and cross-module accounting coherent; a lock on one function is not enough if sibling functions can still observe broken invariants.
- Treat
viewoutputs during state transitions as part of the attack surface when other protocols integrate against them.
Three callback-boundary promotions reviewers should not collapse
-
A callback-capable edge became live at all
- ETH transfer, token hook, safe receiver callback, flash-loan callback, plugin hook, router hop, bridge timeout handler, or helper call all answer the weak first question:
can external code run before this action is over? - Teams still lose money by stopping here and treating
this is still the same user actionas if the hard work were already done.
- ETH transfer, token hook, safe receiver callback, flash-loan callback, plugin hook, router hop, bridge timeout handler, or helper call all answer the weak first question:
-
The protocol had already published one settled accounting or authority story before that edge
- This stronger question is whether balances, debt, shares, collateral membership, receipt identity, temporary permissions, and cross-module marks already described a state the system was willing to defend as coherent if the transaction paused right there.
- Reentrancy exploits usually live in the gap between
external control leftandthe protocol had actually finished proving one settled state story.
-
Any resumed path still deserved to spend against that story before the frame closed
- Even if some partial state looks locally plausible, it still deserves its own boundary before a nested borrow, withdraw, liquidation, reward claim, helper resume, or downstream read is allowed to treat that state as money-good authority.
- The recurring mistake is not only unfinished accounting, but the later silent promotion from
state is mid-transitiontoa re-entered path may still cash out, borrow, seize, or publish against it right now.
The family becomes easier to classify once these promotions stay separate: classic same-function drains mostly fail between steps two and three, helper / router / refund variants often hide the dangerous jump in the surviving authority budget at step three, and read-only reentrancy preserves the cousin where the resumed spender is a downstream integrator rather than the original protocol itself.
What sound design looks like
- The protocol has one clear invariant boundary: before any externally controlled code can run, balances, debt, collateral membership, receipt identity, temporary permissions, and payout eligibility already describe a state that would be safe even if the transaction stopped there.
Function exitis too late; the real security boundary isevery callback edge. - Temporary execution rights are narrow and one-shot. If a flow must continue after an external handoff, the resumable object should be explicit about actor, target, stage, and allowed side effects, then consumed exactly once rather than inferred from ambient module state. Callback frame truth as a single security object is the positive-design companion when the sharper question is what exact in-flight object should have been committed, temporary capability closure before external delivery is the companion when the sharper question is why that broader privilege was still alive once refunds, hooks, or user-directed value transfers began, and exit-right consumed-state binding is the companion when the sharper question is why one refund, timeout, unlock, or payout right was still replayable after value had already started moving.
- Value movement and authority movement are separated wherever practical. A healthy system records that a user is owed a withdrawal, refund, or payout before any externally controlled transfer occurs, then lets the actual asset movement happen through a pull step or through a path that cannot reopen old accounting truth.
- Hook-capable assets are either deliberately supported or explicitly excluded.
ERC-20-like enoughis not a design. If token standards, NFT safe delivery, bridge middleware, or plugin callbacks can execute arbitrary code, the protocol should either model those semantics end to end or reject them at admission. - Cross-module systems share the same callback discipline. Routers, order books, vault wrappers, migration helpers, and keeper paths should not get a looser invariant boundary than the pool or market they sit in front of; otherwise the helper layer becomes the real reentrancy surface.
- Sensitive read surfaces are treated as publication control, not as harmless observability. If downstream protocols can monetize a transient reserve, share-price, debt, or collateral view, then the upstream design should expose a machine-checkable safe-read path or make unsafe reads fail closed during active callback windows. read-only-reentrancy is the narrower home for that integration-side cousin.
- The clean design target is not merely
nonReentrant passes. It isno callback, hook, or nested helper path can ever observe or spend a state from which honest business logic would authorize more value than one fully settled action deserves.
Failure-prevention checklist
- Write down the invariant that must hold at every callback edge, not just at function exit: balances, debt, shares, collateral flags, pending refunds, temporary permissions, and market totals should already be in a safe state before control leaves the contract.
- Inventory every callback-capable surface in the path: native-asset sends, ERC-777 sender hooks, ERC-721 / ERC-1155 safe receiver hooks, flash-loan callbacks, router adapters, plugin systems, bridge / IBC middleware, and any user-supplied integration endpoint.
- Finalize accounting before transfer whenever practical. If assets must move first, isolate the operation behind a design that cannot be profitably re-entered or that completes settlement in a separate pull step. Refunds, safe transfers, and residual payouts are still callback edges, so broad capability windows should already be dead before those deliveries run; temporary capability closure before external delivery is the cleaner design note for that ordering question.
- Apply lock coverage at the system boundary, not one function at a time. The dangerous question is whether any sibling entrypoint can still monetize intermediate state, not whether the original function is individually guarded.
- Treat helper contracts and orchestration layers as part of the trust boundary. If a manager, router, deleverage helper, or keeper path authenticates a user and then performs external calls, the ambient acting-account context must be sealed before those calls.
- Make unsupported or hook-capable asset admission an explicit policy decision. If the protocol is not designed for callback semantics, reject such assets instead of hoping ERC-20-like behavior holds.
- For NFT and receipt systems, reserve IDs, series metadata, and ownership/accounting transitions before safe delivery callbacks fire; receipt identity should never still be mutable during
onERC721Received()oronERC1155Received(). - Expose or consume safe-read primitives deliberately. If downstream protocols can observe transient state during a callback window, either make sensitive views revert under active reentrancy or document a machine-checkable safe-read path.
- Test with malicious receiver contracts and hook-capable mock assets, including cross-function and cross-contract call graphs rather than only same-function recursion.
- Review compiler- or framework-level nonreentrancy assumptions as part of the threat model. Curve / Vyper is the reminder that a guard primitive can fail beneath otherwise careful application logic.
Audit prompts
- At every external call site, what exact pieces of accounting are still stale if the callee re-enters immediately?
- Which other functions, modules, or markets trust that partially updated state during the same transaction?
- Can a token transfer, NFT safe transfer, refund, flash-loan callback, router hop, or helper contract call unexpectedly hand execution to attacker-controlled code here?
- Are reentrancy locks applied consistently across all solvency-changing paths, or can an attacker enter through a less obvious sibling such as collateral toggles, reward claims, migration helpers, or keeper execution paths?
- Does any helper layer carry ambient actor context, temporary privilege bits, cached balances, or deferred checkpoints across an external call?
- If the protocol integrates arbitrary tokens, wrappers, or markets, what assumptions are being made about callback absence, return semantics, or balance monotonicity?
- Do sensitive
viewfunctions publish intermediate reserve, supply, debt, or exchange-rate state that downstream integrators could monetize during a callback window? - Are receipt IDs, order IDs, vault series IDs, or market identities allocated early enough that a callback cannot race object creation or overwrite configuration for an in-flight position?
- What property-based or adversarial tests prove that repeated nested entry cannot mint, withdraw, borrow, liquidate, or seize more than one settled action should allow?
- If the lock primitive comes from a compiler, library, or inherited base contract, what evidence shows it actually shares the lock scope and semantics the protocol assumes?
Lessons that should already be standard
Just a transferis not a safe description. Any external interaction is a control-flow surrender until proven otherwise.- Reentrancy is usually a system-invariant failure, not a single-function bug. Teams still lose money when they guard the obvious path but leave a sibling function, helper contract, or downstream view free to trust broken intermediate state.
- Asset standards are security surfaces. ERC-777 hooks, safe NFT delivery, flash-loan callbacks, and middleware callbacks are not edge cases to remember later; they are part of the default threat model.
- Helper layers deserve the same suspicion as core pools. Exactly, Clober, and GMX V1 are reminders that routers, strategy plugins, order books, and account managers can carry temporary authority across callbacks even when the underlying market logic looks familiar.
- A reentrancy guard is a control, not a proof. It can be scoped too narrowly, skipped on one path, or fail at the language / compiler layer.
- Read-only consumers can still turn upstream callback windows into real loss. If a protocol publishes transient state, another protocol will eventually price, borrow, liquidate, or route against it.
- Receipt creation and asset transfer sequencing belong in the same security conversation. Revest and Omni are reminders that issuance systems and NFT lending paths can be just as reentrancy-sensitive as fungible-token withdrawals.
- The clean design goal is stronger than
no same-function recursion: no callback should ever observe a state from which honest business logic would authorize more value than one fully settled action deserves. - Where this pattern stops:
- Stay here when the decisive failure is that the protocol yielded execution before balances, debt, shares, collateral flags, temporary privileges, or other accounting truth became final.
- Use a quick classifier when a fake endpoint also appears in the exploit story:
- If the exploit still needed a callback window, stale accounting, or a temporary privilege to monetize, this note is usually the primary home.
- If the attacker could already have printed value, forged authority, or redirected settlement just by being accepted as the canonical market / bank / jar / verifier root, move first to account-confusion-root-of-trust-substitution.
- A simple test is
remove the counterfeit endpoint but keep the callback bugversusremove the callback but keep the counterfeit endpoint. The first case points back here; the second points to account confusion.
- Use a second quick classifier for the balance-delta-provenance seam:
- If the sharpest question is
why was an attacker-controlled token or endpoint allowed into this path at all?, start with interface-conformance-semantic-compatibility-failure. - If the sharpest question is
which balance increase got attributed to the wrong source, frame, public action, or internal sub-ledger?, move to balance-delta-provenance-failure. The fastest routing anchors there are Mirror for ambient parked balances, Penpie for wrong-action / market-hook attribution, Predy for cross-pair provenance under an aggregate-conservation check, Akropolis for the cleanone outer unwind remintversion, Grim for therepeated checkpoint-reset remintsvault cousin, and Origin Dollar forexplicit mint first, later rebasing reread of the same backing. - Only Akropolis, Grim, and Origin stay primary exemplars in this note because they still need the callback window as the payout mechanism. Mirror is the non-callback ambient-balance cousin, Penpie is the reward-accounting / semantic-source-confusion cousin where the hook matters mainly because it rewrites what
amountAfter - amountBeforeis allowed to mean, and Predy is the cross-pair provenance cousin where aggregate balance conservation hid which pair ledger still owned the assets. - If the same real inflow was not just re-counted for another share mint but also re-read by rebasing or presentation-layer supply logic, keep this note for the callback window but route analytical weight toward balance-delta-provenance-failure with Origin Dollar as the clean
_rebase()/changeSupply()cousin.
- If the sharpest question is
- Exactly Protocol deliberately sits on the root-authentication seam: the fake market mattered twice, first as the counterfeit authority anchor and then as the re-entrancy trampoline. Use this note when you want the helper-layer callback and stale-context lesson; use the account-confusion note when you want the canonical-endpoint-authentication lesson.
- Akropolis, Grim, and Origin deliberately sit on the admission-versus-payout seam: the hostile token or unsupported asset matters because it opens the callback window, but the payout primitive is still
one real inflow got monetized by more than one frame. Use this note when you want the callback-order failure; use interface-conformance for the admission mistake and balance-delta provenance for the accounting-attribution mistake. - Clober, GMX V1, Arcadia, C.R.E.A.M., and Rari Fuse are cleaner
canonical endpoint, unsafe callback windowcases. Exactly is the mixed root-authentication case; Akropolis / Grim / Origin are the mixed admission-and-provenance cases. - Move to arbitrary-call-token-approval-drain when the approved helper or router did not need stale intermediate state and the real mistake was simply exposing an attacker-directed call surface over existing wallet approvals.
- Move to safe-transfer-receiver-callback-reentrancy when the decisive callback surface is specifically ERC-721 or ERC-1155
safedelivery into an attacker-controlled recipient and the reusable lesson is about standardized receiver hooks as a hidden control plane inside withdrawal, liquidation, or issuance code. - Move to read-only-reentrancy when the integrating protocol only reads transient upstream state and monetizes the mistake through pricing, collateral, or liquidation logic without the callback directly mutating the vulnerable protocol’s own storage.
- Best concrete case studies:
- The DAO — historical archetype for recursive withdrawal before state burn.
- Uniswap V1 imBTC — clean AMM pricing-side reentrancy via ERC-777 hooks.
- Lendf.Me imBTC — early lending-market collateral overwrite through hook-capable transfer semantics.
- SpankChain — refund-path plus hostile-token archetype where one still-open channel paid repeatedly until hub ETH was gone.
- Akropolis — the clean
one real inflow counted twice on unwindpool-token overmint cousin. - Grim Finance — vault-share checkpoint-reset cousin where one real LP deposit minted the same vault claim multiple times.
- C.R.E.A.M. AMP — listed-asset integration failure turning borrow transfer into callback exploit.
- Fei Fuse — stale solvency observed through sibling-market exit during callback window.
- Vyper — compiler-level nonreentrancy failure reopening production pools.
- NFTX — ERC-721 safe-transfer receiver semantics as hidden reentrancy plane.
- Revest Finance — ERC-1155 receipt-identity race where callback-time series creation stayed mutable.
- Arcadia Finance — liquidation-path reentrancy that shows how account-level health and payout sequencing can drift apart mid-callback.
- Terra — bridge / middleware timeout handling still obeys the same old rule: do not hand callback control back before one-shot refund state is consumed.
- Clober — attacker-installed strategy code sat directly on the LP burn boundary, making
plugin hookandwithdrawal callbackthe same security surface. - Penpie — reward-harvest publication-accounting reentrancy where callback-time deposits were misbooked as harvested market rewards.
- GMX V1 OrderBook — keeper refund callback plus split short-accounting state.
Internal linkages
- Positive-design companions: callback-frame-truth-security-object, temporary-capability-closure-before-external-delivery, and exit-right-consumed-state-binding.
- Narrower subfamilies: safe-transfer-receiver-callback-reentrancy and read-only-reentrancy.
- Adjacent families when callback is not the main preserved lesson: account-confusion-root-of-trust-substitution, arbitrary-call-token-approval-drain, and balance-delta-provenance-failure.
- Best bridge / middleware exemplar: Terra — useful when the callback edge is an IBC timeout or bridge middleware path rather than an EVM token standard.
- Best attacker-installed strategy-hook exemplar: Clober — useful when the question is how
permissionless strategy pluginquietly becamewithdrawal trust boundary. - Best compact case-study set: The DAO, Uniswap V1 imBTC, Lendf.Me imBTC, SpankChain, Akropolis, Grim Finance, C.R.E.A.M. AMP, Fei Fuse, Revest Finance, Arcadia Finance, Terra, Clober, Penpie, and Vyper.
Control surface
-
The leverage sits anywhere a protocol yields execution before finalizing accounting: token hooks, receiver callbacks, flash-loan callbacks, keeper refund paths, bridge / IBC timeout adapters, plugin systems, and plain old value transfers.
-
Reentrancy is less about one bad opcode than about a recurring lie protocols tell themselves: that an external interaction is “just a transfer” instead of a full control-flow surrender.
-
The sharper review split is three-layered:
control left,one settled accounting / authority story already existed, andsome resumed path was still allowed to spend against that story before the frame closed. Most expensive callback bugs happen because teams quietly promote all three at once. -
Sources:
- https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy
- https://www.openzeppelin.com/news/reentrancy-after-istanbul
- https://swcregistry.io/docs/SWC-107/
- https://peckshield.medium.com/uniswap-lendf-me-hacks-root-cause-and-loss-analysis-50f3263dcc09
- https://www.openzeppelin.com/news/exploiting-uniswap-from-reentrancy-to-actual-profit
- https://quantstamp.com/blog/how-the-dforce-hacker-used-reentrancy-to-steal-25-million
- https://peckshield.medium.com/akropolis-incident-root-cause-analysis-c11ee59e05d4
- https://coingeek.com/2-million-stolen-in-akropolis-defi-exploit/
- https://medium.com/cream-finance/c-r-e-a-m-finance-post-mortem-amp-exploit-6ceb20a630c5
- https://medium.com/@exactly_protocol/exactly-protocol-incident-post-mortem-b4293d97e3ed
- https://github.com/cosmos/ibc-go/security/advisories/GHSA-j496-crgh-34mx
- https://medium.com/@Rarma_/terra-ibc-hooks-exploit-21cca87d70a4
- https://github.com/exactly/investigation
- https://medium.com/immunefi/hack-analysis-omni-protocol-july-2022-2d35091a0109
- https://github.com/immunefi-team/hack-analysis-pocs/tree/main/src/omni-july-2022
- https://medium.com/@JackLongarzo/fuse-exploit-post-mortem-76ce18d8974
- https://blog.penpiexyz.io/penpie-post-mortem-report-1ac9863b663a
- https://threesigma.xyz/blog/exploit/penpie-reentrancy-exploit-analysis
- https://eips.ethereum.org/EIPS/eip-777
- https://eips.ethereum.org/EIPS/eip-721
- https://github.com/vyperlang/vyper/security/advisories/GHSA-5824-cm3x-3c38
- https://hackmd.io/@vyperlang/HJUgNMhs2
- https://hackmd.io/@sentimentxyz/SJCySo1z2
- https://docs-v2.balancer.fi/concepts/advanced/valuing-bpt/valuing-bpt.html
-
Last reviewed: 2026-07-28 UTC