Security: Eliminate Double-Counting Vulnerability Class via Receipt-Based Accounting #73
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Security: Eliminate Double-Counting Vulnerability Class via Receipt-Based Accounting
Closes: #N/A (Proactive security hardening based on audit findings)
Description
This PR introduces a fundamental architectural refactoring of the FlowCreditMarket lending protocol to use receipt-based accounting instead of mutable ledger-based accounting. This change leverages Cadence's resource-oriented programming model to prevent double-counting bugs at the type system level, ensuring this class of vulnerability cannot recur.
The Problem: Why the Audit Bug Could Happen Again
During the external audit, a critical double-counting bug was identified in the deposit flow. The issue was fixed in commit
3c3272a("Math adjustments from external review"), but the underlying architectural pattern that allowed this bug to exist remains a systemic risk.The Original Bug (commit 3c3272a):
Why this class of bug can recur with the current architecture:
The root cause is the architectural pattern of maintaining a separate mutable ledger (
InternalBalancestruct withrecordDeposit/recordWithdrawalmethods) that must be manually kept in sync with actual token movements. This pattern:The Solution: Receipt-Based Accounting
Instead of maintaining a mutable ledger, this PR introduces receipt resources as the single source of truth:
Core Principle
A
DepositReceiptorBorrowReceiptresource is created atomically when tokens are deposited or borrowed. The receipt:New Resource Types
Why This Makes Double-Counting Impossible
With receipts, the deposit flow becomes:
It is impossible to record the wrong amount because:
Position Structure Change
Changes Overview
Removed (Legacy Patterns)
InternalBalancestruct with mutabledirectionandscaledBalancefieldsInternalBalance.recordDeposit()methodInternalBalance.recordWithdrawal()methodInternalPosition.balancesfieldAdded (Receipt-Based Patterns)
DepositReceiptresourceBorrowReceiptresourceInternalPosition.depositReceiptsresource dictionaryInternalPosition.borrowReceiptsresource dictionarygetDepositShares(),getBorrowShares(),getDepositTypes(),getBorrowTypes()Updated Functions
depositAndPushDepositReceiptatomically with token depositwithdrawAndPullDepositReceiptatomically with token withdrawalliquidatehealthFactorPositionView.creditBalances/debitBalancesmaxWithdrawPositionViewstructurebuildPositionViewquoteLiquidationPositionViewstructureView Layer Refactoring
The
PositionViewstruct was refactored to align with the receipt model:Testing
All 89 tests pass:
Benefits
Backward Compatibility
This is a breaking change to the internal data structures. External APIs (
PositionDetails,PositionBalance) remain unchanged.Critical Files to Review
cadence/contracts/FlowCreditMarket.cdc- Core contract changescadence/tests/phase0_pure_math_test.cdc- Updated tests for newPositionViewstructureReferences
3c3272a("Math adjustments from external review")This refactoring ensures that the class of double-counting bugs identified in the audit cannot occur again, as the accounting model now leverages Cadence's resource safety guarantees rather than relying on manual ledger synchronization.
For contributor use:
masterbranchFiles changedin the Github PR explorer