Skip to content

feat: Implement LoanBroker cash-basis accounting - #7817

Merged
bthomee merged 17 commits into
developfrom
tapanito/lending-cash-basis
Jul 30, 2026
Merged

feat: Implement LoanBroker cash-basis accounting#7817
bthomee merged 17 commits into
developfrom
tapanito/lending-cash-basis

Conversation

@Tapanito

@Tapanito Tapanito commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

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

  • LendingHelpers: introduces Accrual:: and CashBasis:: namespaces, each implementing loanOriginationDeltas, loanVaultExposure, and loanPaymentDeltas for their respective recognition model. Three public dispatcher functions of the same names pick the correct namespace based on the Vault's LEVersion — these dispatchers are the only entry points called from transactors, so LoanSet, LoanPay, and LoanManage require no amendment-awareness themselves.
  • Vault schema versioning: adds sfLEVersion to the Vault ledger entry (new VaultVersion enum: Legacy / CashBasis) via VaultHelpers::getVaultVersion. Vaults created before featureLendingProtocolV1_1 activates have no LEVersion set and resolve to Legacy (accrual). New vaults created after activation are stamped CashBasis.
  • VaultCreate sets sfLEVersion on new vaults when the amendment is enabled.
  • Pre-amendment behavior is unchanged — this only affects vaults/loans created once LendingProtocolV1_1 is active.

Testing

  • LendingHelpers_test.cpp: new unit tests covering both Accrual:: and CashBasis:: delta computations and the dispatcher's version-selection logic.
  • Loan_test.cpp: extensive new coverage exercising cash-basis accounting through loan origination, payment, impairment, and default flows.
  • Vault_test.cpp: coverage for LEVersion assignment on vault creation and getVaultVersion resolution (including legacy vaults with no field set).
  • Invariants_test.cpp: updated to account for the new accounting model.

Specification: XRPLF/XRPL-Standards#582

High Level Overview of Change

Context of Change

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

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.
@Tapanito Tapanito added the DraftRunCI Normally CI does not run on draft PRs. This opts in. label Jul 17, 2026
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Tapanito
Tapanito marked this pull request as ready for review July 22, 2026 09:58
@Tapanito Tapanito changed the title [W.I.P] feat: Implement LendingProtocolV1_1 cash-basis accounting feat: Implement LendingProtocolV1_1 cash-basis accounting Jul 22, 2026
@Tapanito
Tapanito requested a review from a1q123456 July 22, 2026 09:59
@Tapanito
Tapanito requested a review from gregtatcam July 22, 2026 09:59

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues.

Review by Claude Sonnet 4.6 · Prompt: V15

@Tapanito Tapanito changed the title feat: Implement LendingProtocolV1_1 cash-basis accounting feat: Implement LoanBroker cash-basis accounting Jul 22, 2026
@bthomee
bthomee requested a review from Copilot July 22, 2026 10:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 + sfLEVersion and stamps new Vaults with CashBasis under featureLendingProtocolV1_1.
  • Refactors lending accounting touch-points into Accrual:: vs CashBasis:: implementations with dispatcher helpers used by LoanSet, LoanPay, and LoanManage.
  • 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.

Comment thread src/libxrpl/ledger/helpers/VaultHelpers.cpp
Comment thread include/xrpl/ledger/helpers/VaultHelpers.h Outdated
Comment thread src/test/app/Vault_test.cpp Outdated
Comment thread src/libxrpl/tx/transactors/lending/LoanPay.cpp Outdated
Comment thread src/libxrpl/tx/transactors/lending/LoanPay.cpp Outdated
@Tapanito Tapanito removed the DraftRunCI Normally CI does not run on draft PRs. This opts in. label Jul 22, 2026
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

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

Review by Claude Sonnet 4.6 · Prompt: V15

Tapanito added 2 commits July 23, 2026 10:21
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.

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invariant violation in cash-basis accounting: AssetsMaximum check missing during payments. See inline comment.

Comment thread src/libxrpl/ledger/helpers/LendingHelpers.cpp
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.
Comment thread src/libxrpl/ledger/helpers/LendingHelpers.cpp
Comment thread src/libxrpl/ledger/helpers/LendingHelpers.cpp Outdated
Comment thread src/libxrpl/ledger/helpers/LendingHelpers.cpp Outdated

@tyalymov tyalymov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread src/libxrpl/ledger/helpers/LendingHelpers.cpp Outdated
Tapanito added 2 commits July 27, 2026 11:49
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.

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amendment check missing in cashBasisEnabled dispatcher gate - violates documented contract and test assumptions.

Comment thread src/libxrpl/ledger/helpers/LendingHelpers.cpp

@a1q123456 a1q123456 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a small suggestion, looks good otherwise.

Comment thread src/libxrpl/ledger/helpers/VaultHelpers.cpp Outdated

@gregtatcam gregtatcam left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Tapanito added 2 commits July 27, 2026 14:39
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.
@Tapanito
Tapanito requested a review from a1q123456 July 27, 2026 12:50
Tapanito added 2 commits July 27, 2026 14:50
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.

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precision-loss guard now reachable under cash-basis accounting — requires rounding validation.

Comment thread src/libxrpl/tx/transactors/lending/LoanPay.cpp

@a1q123456 a1q123456 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Tapanito Tapanito added this to the Lending Protocol 1.1 milestone Jul 28, 2026
@Tapanito Tapanito added the Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required. label Jul 28, 2026
@Tapanito Tapanito mentioned this pull request Jul 29, 2026
4 tasks
@bthomee
bthomee added this pull request to the merge queue Jul 30, 2026
Merged via the queue into develop with commit 8a5eded Jul 30, 2026
55 of 58 checks passed
@bthomee
bthomee deleted the tapanito/lending-cash-basis branch July 30, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants