Skip to content

[VPD-633] Venus Fixed Rate Vault × Ceffu #52

Open
Debugger022 wants to merge 32 commits intodevelopfrom
feat/vpd-663
Open

[VPD-633] Venus Fixed Rate Vault × Ceffu #52
Debugger022 wants to merge 32 commits intodevelopfrom
feat/vpd-663

Conversation

@Debugger022
Copy link
Contributor

@Debugger022 Debugger022 commented Feb 27, 2026

Summary

Introduces the Venus × Ceffu Fixed Rate Vault system — a set of three smart contracts that allow Venus to deploy tokenized fixed-rate lending vaults whose underlying funds are routed to Ceffu for off-chain yield generation.

Contracts

  • FixedRateVault — An ERC-4626 tokenized vault deployed as an EIP-1167 minimal proxy clone. Implements a four-state lifecycle (Fundraising → PendingFill → Locked → Matured) with an additional Cancelled terminal state. Users deposit during the fundraising window and redeem shares for principal + interest after maturity. Key features include per-user deposit limits, auto-close on max cap, pause-exempt withdrawals, admin-triggered emergency unlock (after grace period), and protocol reserve collection.

  • FundRouter — A singleton upgradeable proxy that manages the six-step fund flow between vaults and Ceffu with strict sequential ordering and idempotency guards: receiveFundsFromVault → setCeffuAddressForVault → transferToCeffu → confirmOrderFillForVault → recordRepayment → distributeRepaymentToVault. Includes asset whitelisting, sweep functionality, and selective pause controls.

  • VaultFactory — Deploys vault clones via Clones.cloneDeterministic() using keccak256(ceffuRequestId) as salt for deterministic addressing. Tracks all deployed vaults by address, index, and Ceffu request ID.

Interfaces

  • IFixedRateVault, IFundRouter, IVaultFactory — Full NatSpec-documented interfaces for all three contracts.

Architecture Highlights

  • All contracts use AccessControlledV8 (Venus ACM) for permissioned operations
  • Vault clone pattern: constructor calls _disableInitializers(), only deployable via factory
  • withdraw()/redeem() are NOT paused — users can always exit in terminal states
  • cancelVault() is NOT paused — admin can always cancel as a safety mechanism
  • ERC-4626 compliant share accounting with 1:1 share-to-asset ratio during fundraising

Test plan

  • All 310 unit tests pass (npx hardhat test tests/hardhat/venus-ceffu/**/*.ts)

FixedRateVault

  • Initialization, parameter validation, boundary values
  • Deposits: happy path, reverts, auto-close on maxCap, maxDeposit/maxMint views
  • Lifecycle: closeFundraising, cancelVault, confirmOrderFill, emergencyUnlock
  • Repayment: receiveRepayment with interest edge cases, withdrawReserves
  • Withdrawals: redeem/withdraw in Matured & Cancelled, loss scenario, ERC-4626 allowance
  • Views: totalAssets per state, maxRedeem/maxWithdraw, preview functions, storage getters

FundRouter

  • Initialization and proxy setup
  • 6-step fund flow with sequential ordering, idempotency guards, and E2E cycles
  • Admin: asset approval, sweepToken, pause/unpause, pausability effects

VaultFactory

  • Clone deployment, deterministic addressing, ID mappings, impl/router updates, ACL

Cross-cutting coverage

  • Access control (ACM deny/allow) for all permissioned functions
  • Pausability: deposit/mint blocked, redeem/withdraw always allowed, cancelVault always allowed
  • All custom errors tested with exact args
  • All events verified with exact args

- VaultFactory: deploys and registers vaults via `deployVault()`, with
  deterministic addressing derived from ceffuRequestId salt
- VaultFactoryStorage: storage layout with vault registry mappings
  (by address, index, and ceffuRequestId) and 44-slot upgrade gap
- IVaultFactory: interface defining events, errors, and external API
- FixedRateVault: ERC-4626 tokenized vault with four-state lifecycle
  (Fundraising → Locked → Matured / Cancelled), per-user deposit limits,
  auto-close on maxCap, protocol reserve extraction, and emergency unlock
- FixedRateVaultStorage: storage layout with packed slot optimization,
  config fields, runtime state, user deposit tracking, and 33-slot gap
- IFixedRateVault: interface defining VaultState enum, VaultInitParams
  struct, lifecycle events, custom errors, and external API
…effu

- FundRouter: five-step fund flow (receiveFundsFromVault → setCeffuAddress
  → transferToCeffu → recordRepayment → distributeRepaymentToVault) with
  boolean idempotency guards, asset whitelist, and token sweep recovery
- FundRouterStorage: per-vault VaultAllocation tracking, approved assets
  whitelist, and 47-slot upgrade gap
- IFundRouter: interface defining VaultAllocation struct, fund flow events,
  idempotency errors, and external API
- IFixedRateVault: insert PendingFill enum between Fundraising and Locked,
  add confirmOrderFill() function and OrderFillConfirmed event
- FixedRateVault: fundraising close now transitions to PendingFill (not
  Locked), lockStartAt and lockPeriodEndTime set only on order fill
  confirmation, cancelVault() extended to accept PendingFill state
- FixedRateVaultStorage: add lockStartAt field, adjust gap (33 → 32)
- IFundRouter: add orderFillConfirmed bool to VaultAllocation struct,
  add confirmOrderFillForVault() function and event, recordRepayment
  prerequisite now requires orderFillConfirmed (not fundsSentToCeffu)
- FundRouter: implement confirmOrderFillForVault() step between
  transferToCeffu and recordRepayment in the fund flow
- FundRouterStorage: update NatSpec to reflect new fund flow
@Debugger022 Debugger022 changed the title Feat/vpd 663 [VPD-633] Venus Fixed Rate Vault × Ceffu Feb 27, 2026
- Add deployFullSystemFixture wiring ACM, MockToken, VaultFactory, FundRouter, and FixedRateVault
- Add defaultVaultParams helper for reusable vault init parameters
- Test VaultFactory initialization and re-initialization guard
- Test deployVault: clone creation, sequential IDs, event emission, ceffuRequestId validation, ACM access control
- Test setVaultImplementation and setFundRouter admin setters with zero-address and unchanged-value reverts
- Test isVault lookup and predictVaultAddress deterministic address computation
- Import MockToken in ImportsV8.sol for artifact generation
- Add ./tests to tsconfig.json include paths
- Test receiveRepayment via FundRouter: state transition, totalRepayment, protocolReserve calculation, event emission
- Test interest edge cases: zero interest, partial loss, 1-wei rounding
- Test withdrawReserves: token transfer, reservesWithdrawn flag, totalAssets invariant
- Test revert conditions: OnlyFundRouter, InvalidState, ReservesAlreadyWithdrawn, NoReservesToWithdraw, ACM
- Test redeem() in Matured state: full/partial redemption, share burning, proportional multi-user distribution
- Test redeem() in Cancelled state: 1:1 refund for single and multiple users
- Test withdraw(): exact asset withdrawal, maxWithdraw full drain, cancelled 1:1
- Test revert conditions: blocked in Fundraising, PendingFill, and Locked states
- Test redemption amount unchanged after withdrawReserves
- Test ERC-4626 allowance: third-party redemption with approval, insufficient allowance revert
- Test withdrawal succeeds when vault is paused
- Test initialization: ACM, vaultFactory, re-initialization guard
- Test setAssetApproval: approve/revoke assets, zero-address revert, ACM access control
- Test setCeffuAddressForVault: set/update ceffu wallet, non-vault revert, ACM check
- Test transferToCeffu: token transfer to ceffu wallet, state transition to Locked, revert conditions
- Test confirmOrderFillForVault: PendingFill → Locked transition, non-PendingFill revert
- Test recordRepayment and distributeRepaymentToVault: repayment recording, distribution to vault, double-distribution revert
- Test pause/unpause: blocked operations while paused, ACM access control
- remove unused variables and imports
- replace non-null assertions
- run prettier
- Replace non-strict inequalities (>=, <=) with negated strict form in FixedRateVault
- Disable max-states-count rule for FixedRateVaultStorage (21 vars required)
- Suppress gas-small-strings warnings for ACM signature strings in FundRouter
- Index lockStartAt and lockPeriodEndTime params in OrderFillConfirmed event
@Debugger022 Debugger022 self-assigned this Mar 2, 2026
- Prevent direct admin cancelVault() in PendingFill state to avoid
  zero-asset redemption bug where vault transitions to Cancelled
  while all funds remain stuck in FundRouter
- Add FundRouter.returnFundsAndCancelVault() that atomically returns
  principal to the vault and then triggers cancellation, ensuring
  users always redeem shares for actual assets
- Restrict PendingFill cancelVault() to FundRouter-only via
  OnlyFundRouter check; Fundraising cancel remains admin-callable
- Block cancellation after transferToCeffu() with FundsAlreadySentToCeffu
  guard since funds are no longer on-chain recoverable
- Clean up allocation state on cancel to prevent stale operations
  (e.g., transferToCeffu after cancel)
- Update NatSpec and state machine diagram to document both cancel paths
- Add comprehensive tests for returnFundsAndCancelVault happy path,
  revert conditions, and post-cancel redemption flow
- Add check that maxUserDeposit >= minUserDeposit when both are
  non-zero, preventing misconfigured vaults where no valid deposit
  amount exists within the allowed range
- Add check that minUserDeposit <= maxCap, preventing vaults where
  the minimum deposit exceeds total capacity making fundraising
  impossible
- Skip maxUserDeposit vs minUserDeposit check when either is zero
  to preserve the "no per-user cap" (maxUserDeposit=0) semantic
- Add revert and boundary-value tests for both new validations
…und lockup

- Allow anyone to call closeFundraising() after fundraisingEndTime,
  removing admin dependency that could leave user funds locked
  indefinitely if admin fails to act
- Before deadline: retain admin-only access control and pause guard
  for early close scenarios
- Cancel path (below minCap) after deadline bypasses pause check
  since it involves no external calls, ensuring users can always
  recover funds
- Success path (above minCap) still requires unpaused state due to
  FundRouter interaction during fund transfer
- Replace whenNotPaused modifier with explicit _requireNotPaused()
  calls to enable granular pause behavior per code path
- Update NatSpec to document timing-based access control semantics
- Add tests for permissionless close, paused cancel path, paused
  success path revert, and post-cancel 1:1 redemption flow
- Cap fixedAPY at MAX_BPS (10000 / 100%) to prevent absurd interest
  rates from misconfiguration or fat-finger errors
- Reject fundraisingEndTime that is at or before current block.timestamp,
  preventing deployment of vaults with already-expired fundraising windows
- Cap gracePeriod at 365 days to prevent unreasonably long grace periods
  that could delay user fund access indefinitely
- Add revert tests for each new boundary and acceptance tests for
  edge values (fixedAPY=10000, gracePeriod=365 days)
…ising

- Prevent closeFundraising() from being called before fundraisingStartTime,
  which could prematurely cancel a vault before users have any chance
  to deposit
- Add test for revert when called before fundraisingStartTime
- Fix existing tests to advance time to fundraisingStartTime before
  calling closeFundraising, aligning with the new guard
- Document why cancelVault() omits nonReentrant (no external calls)
  and whenNotPaused (emergency cancel must work during pause)
- Document why confirmOrderFill() bypasses pause (blocking would
  trap funds in FundRouter with no path to maturity)
- Document why receiveRepayment() bypasses pause (blocking would
  prevent users from reaching redeemable Matured state)
- Note integer division truncation in calculateExpectedInterest()
  for dust amounts (acceptable for stablecoin use case)
…fund contamination

- Introduce totalCommittedPerAsset mapping in FundRouter to track tokens
  reserved across all active vault allocations (principal + undistributed
  repayments)
- Increment committed on receiveFundsFromVault and recordRepayment;
  decrement on transferToCeffu, distributeRepaymentToVault, and
  returnFundsAndCancelVault
- Validate in recordRepayment that the recorded amount does not exceed
  free (uncommitted) balance, preventing admin error from over-allocating
  tokens belonging to other vaults
- Add InsufficientFreeBalance error for the new guard
- Update storage gap from 47 to 46 to account for the new mapping slot
- Add multi-vault integration tests covering full lifecycle tracking,
  cross-vault over-commit prevention, and cancel decrement correctness
…add minor guards

- Guard sweepToken() against sweeping committed vault funds by checking
  totalCommittedPerAsset, reusing InsufficientFreeBalance error
- Prevent no-op setCeffuAddressForVault() calls that set the same
  address, adding CeffuAddressUnchanged error for clarity
- Add nonReentrant modifier to confirmOrderFillForVault() since it
  makes an external call to vault.confirmOrderFill()
- Add tests for sweepToken free vs committed balance enforcement
  and setCeffuAddressUnchanged revert
- Block setCeffuAddressForVault after funds already sent to Ceffu
- Prevent underflow in sweepToken when balance < committed
- Fully clean up allocation on cancel (zero supplyAsset/ceffuAddress)
- Validate maxUserDeposit <= maxCap during initialization
- Fix misleading error state in cancelVault revert
… tests

Track real token amounts received via balance-before/after pattern in
FixedRateVault._deposit() and FundRouter.receiveFundsFromVault() instead
of trusting the input amount. Add tests for sweepToken committed balance
guard, post-transfer setCeffuAddress revert, and maxUserDeposit > maxCap
validation.
@Debugger022 Debugger022 marked this pull request as ready for review March 6, 2026 13:30
@github-actions
Copy link

github-actions bot commented Mar 6, 2026

Code Coverage

Package Line Rate Branch Rate Health
contracts 100% 100%
contracts.DeviationSentinel 88% 86%
contracts.DeviationSentinel.Oracles 93% 90%
contracts.Interfaces 100% 100%
contracts.LeverageManager 94% 80%
contracts.Libraries 29% 33%
contracts.PositionSwapper 0% 0%
contracts.SwapHelper 100% 100%
contracts.SwapRouter 79% 55%
contracts.venus-ceffu 99% 90%
contracts.venus-ceffu.interfaces 100% 100%
Summary 80% (875 / 1094) 67% (440 / 658)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant