Skip to content

v6#24

Open
mejango wants to merge 83 commits intomainfrom
version/6
Open

v6#24
mejango wants to merge 83 commits intomainfrom
version/6

Conversation

@mejango
Copy link

@mejango mejango commented Feb 21, 2026

Description

What does this PR: do, how, why?

Limitations & risks

Are there any trade-off or new vulnarbility surface based on theses changes?

Check-list

  • Tests are covering the new feature
  • Code is natspec'd
  • Code is linted and formatted
  • I have run the test locally (and they pass)
  • I have rebased to the latest main commit (and tests still pass)

Interactions

These changes will impact the following contracts:

  • Directly:

  • Indirectly:

mejango and others added 30 commits February 21, 2026 14:39
… (C-2)

The buyback hook specification was hardcoded to index [1], but when
usesTiered721Hook=false the array is only size 1 (valid index: [0]).
This caused an out-of-bounds revert on every payment to non-721 revnets
when the buyback hook activated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
newBorrowAmount and newCollateralCount are uint256 but stored as uint112.
Without overflow checks, values exceeding uint112.max are silently
truncated, enabling fund theft by borrowing the full amount but only
recording the truncated (tiny) loan.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevent no-op repayments where both repayBorrowAmount and
collateralCountToReturn are zero, which would burn and re-mint
loan NFTs without any actual repayment, allowing unlimited
loan NFT inflation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dedWith

Add DIRECTORY.isTerminalOf check to prevent unauthorized callers
from invoking afterCashOutRecordedWith with crafted parameters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevent cross-source collateral reallocation that could extract
value when different terminal/token pairs have different surplus
ratios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The _borrowableAmountFrom function reads live surplus, raising concerns
about flash loan manipulation via addToBalanceOf. Analysis shows this
attack is economically irrational: addToBalanceOf permanently donates
funds, and the extra borrowable amount is always a fraction of the
donation (bounded by collateralCount/totalSupply < 1). The bonding
curve's concavity with non-zero cashOutTaxRate makes it even worse
for the attacker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add NatSpec documenting that project conversion does not account
for pre-existing token supply and that existing holders should
be informed of the economic implications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive NatSpec documenting that expired loan
liquidation permanently destroys collateral, and that borrowers
have the full loan duration to repay. This is intentional design
to keep the protocol simple.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove per-revnet loans config param in favor of a single immutable
LOANS address set at REVDeployer construction time. All revnets now
share the same loans contract.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The M-27 fix rejects repayLoan calls where both repay amount and
collateral return are zero. Add vm.assume to skip those fuzz inputs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of granting USE_ALLOWANCE per-revnet on every deploy, grant it
once in the constructor with projectId=0 (wildcard). Saves gas on each
deploy. Adds integration test verifying the permission.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
REVLoans constructor now takes IJBController directly instead of
IREVDeployer. Removes the REVNETS check so any project can use
loans at their own risk. Deploy script no longer needs the
address(0) placeholder + re-computation dance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Loan fund access limits were redundantly specified in REVConfig.loanSources
when all the same information was already available in terminalConfigurations.
Now _makeLoanFundAccessLimits iterates over each terminal's accounting
contexts directly, creating one surplus allowance per terminal+token pair.

- Remove REVLoanSource[] loanSources from REVConfig struct
- Rewrite _makeLoanFundAccessLimits to derive from terminalConfigurations
- Delete _matchingCurrencyOf (no longer needed)
- Remove REVDeployer_LoanSourceDoesntMatchTerminalConfigurations error
- Update Deploy.s.sol and all 12 test files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the REV fee payment fails (try-catch), zero out revFeeAmount so
the borrower receives the full amount instead of it being stuck in the
contract. For ERC-20 tokens, decrease the dangling allowance. Fixes the
dead-code ternary (L-23) as well.

This keeps the try-catch by design — a revnet should never be blocked
by fee infrastructure issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Permissionless function to burn project tokens stuck in REVDeployer
from reserved token distribution when splits don't sum to 100%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
A non-terminal caller would just be donating their own funds as fees.
There's nothing to exploit — no caller validation required.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace per-revnet buybackHookOf mapping with immutable BUYBACK_HOOK_REGISTRY.
Auto-configure buyback pools during deployment using sensible defaults
(1% fee tier, 30min TWAP window) read from terminal accounting contexts.
Fix C-2 array OOB in beforePayRecordedWith hook specification indexing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split operator can now manage swap terminal selection in the
registry, alongside existing buyback pool and TWAP configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…payable removal

- Normalize token amounts by decimal difference in _totalBorrowedFrom
  before aggregating across different token decimals
- Change liquidation threshold from > to >= for LOAN_LIQUIDATION_DURATION
- Remove payable from reallocateCollateralFromLoan and reject msg.value
  since the function only moves existing collateral

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes collateral→collateralCount and return value name mismatches
in borrowableAmountFrom, borrowFrom, repayLoan, and
reallocateCollateralFromLoan.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates 3 calls to currentReclaimableSurplusOf in REVLoansSourced.t.sol
to use the renamed parameter cashOutCount instead of tokenCount.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update all import paths, dependency references, and prose from Juicebox V5
to Juicebox V6. Repo folder name headers preserved as-is.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Matching hash covers economic parameters but not terminal/accounting
configs. Revnets on non-ETH-native chains must use WETH/USDC ERC20
accounting instead of NATIVE_TOKEN to avoid asset mismatch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the risk of using NATIVE_TOKEN as a terminal accounting
context on non-ETH-native chains, and that the matching hash does
not cover terminal configurations. References INTEROP-6 and SECURITY.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Foundry's correct key is evm_version. vm_version is silently
ignored, meaning EVM version was not being enforced.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use IJBRulesetDataHook instead of IJBBuybackHookRegistry so the
REVDeployer has no notion of a registry — just a generic data hook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This change belongs in a separate PR with dedicated tests.
The fee recovery logic (catch block with safeDecreaseAllowance
and revFeeAmount zeroing) will be submitted independently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When feeTerminal.pay() reverts, the borrower now receives the REV fee
amount back instead of losing it. For ERC-20 tokens, the dangling
allowance to the fee terminal is cleaned up via safeDecreaseAllowance.

Changes:
- Remove redundant revFeeAmount == 0 ternary (already inside if > 0)
- Add catch block: decrease ERC-20 allowance and zero out revFeeAmount
- Fix comments: "beneficiary" → "fee terminal", "msg.sender" → "beneficiary"

9 new tests covering native ETH, ERC-20, fuzz, and no-stuck-funds scenarios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mejango and others added 18 commits March 4, 2026 16:28
fix: align IREVLoans argument names with implementation
Add verified README.md and SKILLS.md
…ing-risk

Document cross-chain NATIVE_TOKEN accounting mismatch risk (INTEROP-6)
Fix vm_version typo to evm_version in foundry.toml
L-15: Document stage transition impact on outstanding loans
L-16: Document intentional 100% LTV with cashOutTaxRate buffer
L-24: Clear loan storage after NFT burn for gas refund

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs caused by _repayLoan deleting loan storage:
1. repayLoan reads loan.source.token after delete → address(0) →
   SafeERC20 fails. Fix: cache sourceToken before _repayLoan.
2. _repayLoan returns storage pointer after delete → all zeros.
   Fix: snapshot loan to memory before delete in full-repayment path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eanup)

Verifies stage transitions reduce borrowable amount (higher cashOutTaxRate),
loan storage is cleared after full repayment and after liquidation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds 3 tests for PR #11 gaps:
- Partial repay clears old loan storage (else branch in _repayLoan)
- Excess ETH refund uses cached sourceToken (use-after-delete regression)
- Reallocation clears old loan storage after replacement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Snapshot deltas from current loan state, write loan.amount and
loan.collateral BEFORE any external calls, then interact using
pre-computed deltas. Any reentrant call sees updated state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ReentrantBorrower contract reads loan state during ETH callback,
  verifies CEI pattern exposes finalized (not stale) state
- Atomic consistency test: loan.amount == totalBorrowedFrom after
  each borrow and both == 0 after full repay
- Rapid borrow/repay sequence: 3 cycles with clean accounting
- Source verification documented: all 4 _adjust helpers use memory
  copies and delta parameters, never read loan.amount/loan.collateral

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three new tests exercising _totalBorrowedFrom decimal normalization:
1. Zero-borrow continue path (TOKEN source has 0 borrowed → skipped)
2. TOKEN (6-dec) source borrow normalized correctly in 18-dec ETH context
3. Both ETH and TOKEN sources with cross-decimal aggregation

Also documents price feed precision limitation: inverse price at 6 decimals
truncates to 0 (mulDiv(1e6,1e6,1e21)=0), requiring TOKEN borrows before ETH.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Spec

- _totalBorrowedFrom: explain decimal normalization requirement and
  inverse price feed precision limitation at low decimal counts
- reallocateCollateralFromLoan: explain why function is not payable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: REVLoans mixed-decimal normalization, liquidation boundary, and …
Fix fee payment error recovery in REVLoans
@mejango mejango changed the title first v6 Mar 4, 2026
mejango and others added 11 commits March 4, 2026 17:21
Resolve conflict in src/REVLoans.sol by keeping both the source mismatch
validation (PR #13) and the not-payable NatSpec comment (from version/6).
Fix pre-existing test issues: REVDeployer constructor args and
currentReclaimableSurplusOf parameter name (cashOutCount → tokenCount).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Merge version/6 into hardcode-loans-address (auto-merged, no source conflicts)
- Update 14 test files for PR #25 changes:
  - REVLoans constructor: revnets → controller
  - REVDeployer constructor: add address(LOANS_CONTRACT) param
  - REVConfig: remove loanSources and loans fields
  - Deploy order: REVLoans before REVDeployer
- Fix pre-existing test issues:
  - REVLoansSourced: cashOutCount → tokenCount param name
  - REVLoansFeeRecovery: add missing buybackHookConfiguration param

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…st failures

- Resolve merge conflicts with version/6 (keep SourceMismatch error, remove RevnetsMismatch)
- Add explicit IJBProjects parameter to REVLoans constructor instead of double indirection
- Fix 3 fuzz test boundary failures (liquidation now triggers at >= LOAN_LIQUIDATION_DURATION)
- Fix TestPR10 assertion for loan data deletion after liquidation (delete _loanOf[loanId])
- Update deploy script and all 23 test files with new constructor signature
- All 144 tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ress)

- REVDeployer constructor now takes both buybackHook and loans params
- hasMintPermissionFor uses both global LOANS and BUYBACK_HOOK immutables
- Remove per-revnet buyback setup (now global) and per-revnet loans setup (now global)
- Delete REVBuybackHookConfig and REVBuybackPoolConfig structs
- Remove buybackHookConfiguration from all deployFor calls across 14 test files
- Add MockBuybackDataHook for tests (BUYBACK_HOOK can no longer be address(0))
- Update TestPR22 assertion: buyback hook spec is always present (1, not 0)
- All 144 tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
REVDeployer fits with 43 bytes margin. Better gas per call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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