0.8.34, OpenZeppelin v5 upgradeable. Every deploy is a TransparentUpgradeableProxy. Module state uses ERC-7201 namespaced storage (neobank.storage.*).
Three layers
Protocol root creates tenants; each tenant creates funds; each fund is a hub with spokes and execution wallets. Spokes and execution wallets around the hub: Protocol root. FundManagerDeployer: canonical implementations, one FundManager per tenant, protocol fee recipient resolved live by funds. Tenant. FundManager owns eight Factory instances and wires hub + six spokes in onecreateFund. Also deploys strategies and chooses implementations for future components.
Fund instance. Fund plus FundShare, DepositQueue, RedeemQueue, Oracle, FeeManager, RiskManager, and any Strategy wallets.
Pattern 1: Star (hub-and-spoke)
Spokes talk only to the Fund. Spokes hold no role state — admin checks call back into the Fund ACL. Grant and revoke roles in one place. See Access control and roles. Fund behavior comes from modules:Pattern 2: Batch lifecycle
No continuous AMM pricing. Oracle cuts time into batches. After cutoff, a reporter submits per-asset NAV; after review delay, acceptance settles deposits and redeems and accrues fees inside oneFund.acceptReport.
Conventions:
- Prices — 1e18 asset-per-share:
shares = amount × 1e18 / price - Native ETH — sentinel
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE - Fees — newly minted shares, never assets
- Cancel until settlement; claims never expire
- Redeem payouts snapshotted at settlement
Settlement (acceptReport)
Fund.acceptReport(nextCutoffTime) in one call:
1
Assemble asset set
Union of deposit assets, redeem assets, and fee base asset.
2
Accept Oracle report
Oracle.acceptReport: batch closed, no pending suspicious price, every asset inside accept window — then consume reports, update lastAcceptedPrice, advance batch, set next cutoff.3
Accrue fees
For fee base asset:
FeeManager.accrueFees; mint management/performance/protocol fee shares.4
Settle deposits and redemptions
Per asset: mint user and entry-fee shares for the deposit batch; burn redeemed shares and snapshot redeem payout net of exit fee.
acceptSuspiciousReport is the same path with suspicious-price check bypassed and a stronger role — after manual review.
Redeem funding
Settlement records what each redeem batch is owed. Payout is separate and role-gated. Assets return by pull from a Strategy (cannot refuse) or by transfer from an external wallet / bridge (operational trust). ThenFUND_REDEEM_ROLE calls fundRedeem to deliver the snapshotted amount to RedeemQueue.
Strategies
A Strategy is a fund-controlled wallet limited to allowlisted calls keyed by(caller, target, selector), optionally with pinned calldata words. Invariants: Fund can always pull (pullAsset is onlyFund); strategy cannot call Fund (target == fund rejected).
Cross-chain execution uses StandaloneStrategy: same allowlist engine, no Fund control. Bridge operationally; reflect results in NAV via Oracle.
Upgrades
Roles govern behavior; ProxyAdmin ownership governs code. EachTransparentUpgradeableProxy has a ProxyAdmin owned by the proxyAdmin chosen at tenant/fund creation. Changing implementations on FundManagerDeployer or a Factory affects future deploys only. Live proxies upgrade through their own ProxyAdmins. Protocol operator cannot push code into a live fund.
Operational notes
- Settlement is all-or-nothing per report.
acceptReportreverts if any pending report in the asset set is suspicious. Reject/resubmit, or useacceptSuspiciousReportafter review. - Fee base asset must be reported every settlement.
- External-wallet and cross-chain legs are trust-based. Fund cannot pull those assets back.
- After
createFund,DEFAULT_ADMIN_ROLEmust grant operational roles before the fund can run.
Source
Per-contract detail: Contract reference. Source:zentryHQ/redpotion-contracts.