Skip to content

fix: Add 60s buffer before closed-ended vault redemption - #8151

Merged
bthomee merged 5 commits into
developfrom
tapanito/closed-ended-buffer
Sep 1, 2026
Merged

fix: Add 60s buffer before closed-ended vault redemption#8151
bthomee merged 5 commits into
developfrom
tapanito/closed-ended-buffer

Conversation

@Tapanito

@Tapanito Tapanito commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Closed-ended LoanSet could originate a loan whose last scheduled payment lands on RedemptionDate. Once the vault enters Redemption, deposits are already closed and the remaining Investment window is gone, so there is no time left to collect that payment before the vault starts paying out LPs.

Reject LoanSet when the final payment is fewer than 60 seconds before RedemptionDate (finalPayment + kLoanRedemptionBuffer > RedemptionDate). Raise kMinInvestmentPeriod from 60s to 180s so a minimum-interval loan can still fit: StartDate is strictly after SubscriptionDate, kMinPaymentInterval is 60s, and the new 60s buffer must all sit inside the Investment window. ValidLoan mirrors the same bound.

Test plan

  • LoanSet (closed-ended phase and maturity cases, including min-gap origination)
  • InvariantsVault (buffer-zone loan creation)
  • VaultClosedEnded (create-time 180s floor)

Loan origination could schedule a final payment at RedemptionDate, leaving no time to settle before the vault enters Redemption. Reject LoanSet unless the final payment is at least 60s before RedemptionDate, and raise the minimum Investment window to 180s so a min-interval loan can still fit.
clang-tidy misc-include-cleaner requires a direct include for LoanSet::kMinPaymentInterval.

@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.

This MR introduces a kLoanRedemptionBuffer (60s) that closed-ended loans must clear before RedemptionDate, and raises kMinInvestmentPeriod from 60s to 180s so a minimum-interval loan can still fit. I traced the arithmetic in LoanSet::preclaim, ValidLoan::finalize, and the new static_assert, and it is internally consistent: preclaim rejects when finalPayment + kLoanRedemptionBuffer > RedemptionDate, ValidLoan mirrors the same bound, and the static_assert (kMinInvestmentPeriod >= kMinPaymentInterval + kLoanRedemptionBuffer + 1) correctly guarantees a minimum-interval loan starting strictly after SubscriptionDate can still clear the buffer within a minimum-length Investment window (121s required vs. 180s chosen, leaving headroom). The updated tests (LoanSet_test boundary case 5, new case 6, and InvariantsVault_test's buffer-zone synthetic loan) check out arithmetically against the new inequality. No correctness, security, or resource issues found in the changed lines; documentation/comments were updated consistently across all touched files.

@Tapanito
Tapanito requested a lite review from Copilot September 1, 2026 10:11
@Tapanito Tapanito changed the title Require a 60s buffer before closed-ended vault redemption fix: Add 60s buffer before closed-ended vault redemption Sep 1, 2026
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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

This PR tightens closed-ended vault loan origination rules by requiring a 60-second safety buffer between a loan’s final scheduled payment and the vault’s RedemptionDate, and increases the minimum Investment window to ensure minimum-interval loans can still be originated early in Investment.

Changes:

  • Add kLoanRedemptionBuffer and enforce finalPayment + kLoanRedemptionBuffer <= RedemptionDate in LoanSet preclaim and ValidLoan invariant checks.
  • Raise kMinInvestmentPeriod from 60s to 180s and add a compile-time guard ensuring the minimum window can accommodate min payment interval + buffer.
  • Update and extend unit/invariant tests to cover the new boundary behavior and min-gap origination cases.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/libxrpl/tx/transactors/lending/LoanSet.cpp Enforces the new redemption buffer rule in LoanSet::preclaim and adds a static assertion tying Investment-window minimums to loan schedule minimums.
src/libxrpl/tx/invariants/LoanInvariant.cpp Mirrors the buffer rule in ValidLoan to catch bypassed preclaim cases at invariant time.
include/xrpl/protocol/Protocol.h Introduces kLoanRedemptionBuffer and increases kMinInvestmentPeriod to 180s (protocol constant change).
include/xrpl/tx/invariants/VaultInvariant.h Updates invariant documentation to describe the buffered maturity bound.
src/test/app/lending/LoanSet_test.cpp Adds boundary tests for the buffer rule and a min-gap Investment-window origination/regression case.
src/test/app/lending/LoanTestBase.h Updates test-base documentation to reflect the buffered final-payment constraint.
src/test/app/invariants/InvariantsVault_test.cpp Updates invariant tests to construct an “inside-buffer” loan and expect the updated invariant failure text.
src/test/app/vault/VaultClosedEnded_test.cpp Updates comments and cross-references to reflect kMinInvestmentPeriod and the new buffer reasoning.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/test/app/invariants/InvariantsVault_test.cpp Outdated
Comment thread include/xrpl/protocol/Protocol.h
@Tapanito Tapanito added this to the 3.4.0 milestone Sep 1, 2026
@Tapanito Tapanito added the feature: LP1.1 Lending Protocol 1.1. Remove once merged. label Sep 1, 2026

@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.

Generally looks good. Just a small issue that copilot commented.

Set StartDate to RedemptionDate - kLoanRedemptionBuffer so interval 1 lands the final payment one second inside the buffer, matching the test comment.

@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.

This MR introduces kLoanRedemptionBuffer (60s) and raises kMinInvestmentPeriod from 60s to 180s so a minimum-interval loan can still be originated inside the Investment window with the new buffer. I traced the arithmetic through LoanSet::preclaim, ValidLoan::finalize, and the new static_assert, and the boundary conditions are consistent: finalPayment + kLoanRedemptionBuffer > redemption is used uniformly in both the transactor and the invariant, and the constants satisfy kMinPaymentInterval + kLoanRedemptionBuffer + 1 <= kMinInvestmentPeriod as required. The accompanying tests (LoanSet_test boundary cases, InvariantsVault_test buffer-zone synthesis, VaultClosedEnded_test comment updates) match the new formula correctly (e.g., the accept/reject boundary tests land exactly on redemptionDate - buffer and redemptionDate - buffer + 1). No correctness, security, or consistency issues were found in the changed lines.

@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 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 Sep 1, 2026

@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.

This change consistently reworks the closed-ended loan/redemption boundary check from a strict '>=' comparison to a buffered '+ kLoanRedemptionBuffer >' comparison across LoanSet::preclaim and ValidLoan::finalize, and raises kMinInvestmentPeriod from 60s to 180s with a static_assert tying it to kMinPaymentInterval + kLoanRedemptionBuffer + 1. I traced the arithmetic in both production sites and in the updated tests (InvariantsVault_test.cpp, LoanSet_test.cpp boundary cases, and the new min-gap origination test in case 6) and the math checks out: the accept/reject boundaries line up exactly with the documented buffer semantics, and the two enforcement points (preclaim gate and invariant) mirror each other. No correctness, security, or consistency issues found in the changed lines.

@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.

This is a well-scoped, internally consistent fix. The new kLoanRedemptionBuffer constant is applied identically in LoanSet::preclaim and ValidLoan::finalize (both use finalPayment + buffer > redemptionDate), the raised kMinInvestmentPeriod is backed by a compile-time static_assert that correctly accounts for StartDate being strictly after SubscriptionDate, and the boundary tests in LoanSet_test.cpp/InvariantsVault_test.cpp correctly exercise the new off-by-buffer edge (accept at exactly the buffer, reject one second inside it). I traced the arithmetic through all four production sites and didn't find a logic error or an inconsistency between the preclaim check and the invariant check.

@bthomee
bthomee added this pull request to the merge queue Sep 1, 2026
Merged via the queue into develop with commit 7863ac8 Sep 1, 2026
55 checks passed
@bthomee
bthomee deleted the tapanito/closed-ended-buffer branch September 1, 2026 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: LP1.1 Lending Protocol 1.1. Remove once merged. 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.

5 participants