feat: Implement LoanBroker cash-basis accounting - #7817
Conversation
Gate Vault AssetsTotal and LoanBroker DebtTotal to principal-only tracking under featureLendingProtocolV1_1, replacing whole-life (accrual) interest recognition. Adds Accrual/CashBasis namespaces and three public dispatchers in LendingHelpers, consumed by LoanSet, LoanPay, and LoanManage without any amendment-awareness in the transactors themselves. Pre-amendment behavior is unchanged.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Implements a per-Vault, amendment-gated cash-basis accounting model for the lending protocol. When featureLendingProtocolV1_1 is enabled and a Vault is stamped with sfLEVersion == VaultVersion::CashBasis, Vault.AssetsTotal / LoanBroker.DebtTotal track principal-only and recognize interest as income only when paid; legacy Vaults continue using the existing accrual/whole-life model.
Changes:
- Introduces
VaultVersion+sfLEVersionand stamps new Vaults withCashBasisunderfeatureLendingProtocolV1_1. - Refactors lending accounting touch-points into
Accrual::vsCashBasis::implementations with dispatcher helpers used byLoanSet,LoanPay, andLoanManage. - Adds/updates extensive unit tests validating cash-basis behavior, dispatcher selection, and legacy-vault behavior after amendment activation.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp | Extends protocol-autogen round-trip tests to cover sfLEVersion accessors/setters. |
| src/test/app/Vault_test.cpp | Adds VaultCreate/VaultSet tests for sfLEVersion presence/absence and malformation handling. |
| src/test/app/Loan_test.cpp | Adds dedicated cash-basis end-to-end tests and adjusts existing expectations to account for LEVersion-based behavior. |
| src/test/app/LendingHelpers_test.cpp | Adds unit tests for Accrual::/CashBasis:: delta helpers and dispatcher selection logic. |
| src/libxrpl/tx/transactors/vault/VaultCreate.cpp | Stamps new Vaults with sfLEVersion = CashBasis when featureLendingProtocolV1_1 is enabled. |
| src/libxrpl/tx/transactors/lending/LoanSet.cpp | Uses loanOriginationDeltas(...) dispatcher to apply model-specific AssetsTotal / DebtTotal updates. |
| src/libxrpl/tx/transactors/lending/LoanPay.cpp | Uses loanPaymentDeltas(...) dispatcher for model-specific AssetsTotal / DebtTotal updates and updates logging. |
| src/libxrpl/tx/transactors/lending/LoanManage.cpp | Uses loanVaultExposure(...) dispatcher for impair/unimpair/default exposure calculations. |
| src/libxrpl/ledger/helpers/VaultHelpers.cpp | Adds getVaultVersion(...) implementation resolving sfLEVersion (or Legacy when absent). |
| src/libxrpl/ledger/helpers/LendingHelpers.cpp | Adds Accrual:: / CashBasis:: helper implementations plus public dispatchers gated by amendment + Vault LEVersion. |
| include/xrpl/protocol/Protocol.h | Adds public enum class VaultVersion describing Vault schema/accounting versions. |
| include/xrpl/protocol/detail/sfields.macro | Defines new sfLEVersion SField. |
| include/xrpl/protocol/detail/ledger_entries.macro | Adds sfLEVersion to the Vault ledger entry format as a defaulted field. |
| include/xrpl/protocol_autogen/ledger_entries/Vault.h | Adds protocol-autogen wrapper accessors and builder setter for sfLEVersion. |
| include/xrpl/ledger/helpers/VaultHelpers.h | Declares getVaultVersion(...) and documents LEVersion resolution behavior. |
| include/xrpl/ledger/helpers/LendingHelpers.h | Declares new accounting delta structs, Accrual::/CashBasis:: helpers, and dispatcher APIs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Reference VaultVersion::Legacy instead of raw 0 in getVaultVersion's docstring, correct a stale test label, and rename LoanPay's precision log fields from ValueChange to AssetsTotalDelta to match what they actually print
Cash-basis origination never recognizes interest into Vault.AssetsTotal, so checking interestDue against remaining AssetsMaximum headroom rejects loans for no reason. Split the check into Accrual/CashBasis namespaces, mirroring the existing loanOriginationDeltas/loanPaymentDeltas dispatch.
Remove unused vaultMaximum local left over from the AssetsMaximum guard refactor, and stop the DebtMaximum guard from unconditionally adding interestDue to the projected DebtTotal. Reuse the existing loanOriginationDeltas dispatcher, which already excludes interestDue under cash-basis accounting.
cashBasisEnabled now only checks the Vault's LEVersion, so the Rules parameter threaded through loanOriginationDeltas, loanOriginationExceedsVaultMaximum, loanVaultExposure, and loanPaymentDeltas is dead weight. Remove it from the declarations, definitions, and call sites.
a1q123456
left a comment
There was a problem hiding this comment.
left a small suggestion, looks good otherwise.
Now that cashBasisEnabled only checks the Vault's LEVersion, dispatch no longer depends on the amendment flag. The "amendment disabled picks Accrual" sub-cases fed a CashBasis-versioned vault while disabling the amendment, a combination that can no longer occur (LEVersion is fixed at Vault creation) and that the current dispatch correctly ignores, so they failed. The remaining legacy-vault/cash-basis-vault cases already cover the dispatch logic.
Address PR review comment: VaultVersion's enumerators already have explicit values, so static_cast<VaultVersion>(leVersion) avoids repeating them as switch-case magic numbers. Out-of-range values still hit the UNREACHABLE guard.
Implement LendingProtocolV1_1 cash-basis accounting
Summary
Adds a cash-basis accounting model for Vaults/Loans, gated behind featureLendingProtocolV1_1, as an alternative to the existing whole-life (accrual) model. Under cash-basis, Vault.AssetsTotal and LoanBroker.DebtTotal track principal only — interest is recognized as income only when it's actually paid, rather than being recognized in full at loan origination.
Changes
Testing
Specification: XRPLF/XRPL-Standards#582
High Level Overview of Change
Context of Change
API Impact
libxrplchange (any change that may affectlibxrplor dependents oflibxrpl)