Skip to content

Closed-ended Vault - #587

Merged
Tapanito merged 47 commits into
XRPLF:masterfrom
a1q123456:a1q123456/term-vault
Sep 15, 2026
Merged

Tapanito merged 47 commits into
XRPLF:masterfrom
a1q123456:a1q123456/term-vault

Conversation

@a1q123456

@a1q123456 a1q123456 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

High Level Overview of Change

Context of Change

Type of Change

  • New XLS Draft
  • XLS Update (changes to an existing XLS)
  • XLS Status Change (e.g., Draft → Final, Draft → Stagnant)
  • Process/Meta (changes to CONTRIBUTING.md, XLS-1, templates, etc.)
  • Infrastructure (CI, workflows, scripts, website)
  • Documentation (README updates, typo fixes)

Comment thread XLS-xxxx-term-vault/README.md Outdated
Comment thread XLS-xxxx-term-vault/README.md Outdated
Comment thread XLS-xxxx-term-vault/README.md Outdated
Comment thread XLS-xxxx-term-vault/README.md Outdated
Comment thread XLS-xxxx-term-vault/README.md Outdated
Comment thread XLS-xxxx-term-vault/README.md Outdated
Comment thread XLS-xxxx-term-vault/README.md Outdated
Comment thread XLS-xxxx-term-vault/README.md Outdated
@a1q123456
a1q123456 requested review from Tapanito and gregtatcam July 22, 2026 16:03
@github-actions

Copy link
Copy Markdown

⚠️ Self-Assigned XLS Number Warning

This PR adds a new XLS directory with a self-assigned number. XLS numbers should be assigned by the CI system using the XLS-draft-* naming convention.

Directory Self-Assigned Number
XLS-0103-term-vault 103

What to do

Please rename your directory to use the draft convention instead (e.g., XLS-draft-my-feature). An XLS number will be automatically assigned after a maintainer approves the PR.


This warning was automatically generated.

@a1q123456
a1q123456 marked this pull request as ready for review July 22, 2026 18:40
@a1q123456
a1q123456 force-pushed the a1q123456/term-vault branch from 9af7400 to 30123ca Compare July 29, 2026 20:05
Comment thread XLS-draft-0103-term-vault/README.md Outdated
Comment thread XLS-draft-closed-ended-vault/README.md Outdated
Comment thread XLS-draft-closed-ended-vault/README.md
Comment thread XLS-draft-0103-term-vault/README.md Outdated

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.

🟡 Changes recommended

Critical specification conflicts and undefined state, amendment, and immutability behavior remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

XLS-draft-closed-ended-vault/README.md:281

  • LoanAccept is not defined by XLS-66: the parent requires LoanSet to carry CounterpartySignature and settle atomically (XLS-0066 README.md:1024-1048, 1510), and this section supplies neither fields nor state changes. As written, implementations cannot determine what pending state is being accepted or what this phase gate applies to. Define the two-step transaction/state in this XLS or link the companion amendment and specify the delta.
## 8. Transaction: `LoanAccept` (modified)

### 8.1. Fields

No changes.

XLS-draft-closed-ended-vault/README.md:262

  • XLS-66 already assigns tecKILLED to a loan schedule that exceeds protocol time limits (§3.8.5.2, item 2). Because RedemptionDate is a UINT32, the new wide-arithmetic comparison is also true for those schedules, leaving two different results for the same input unless evaluation precedence is specified. State whether the inherited overflow failure runs first, or explicitly replace it, and limit tecNO_PERMISSION to representable schedules.
1. If the vault is closed-ended and `now <= SubscriptionDate` (`now` is the parent ledger close time), return `tecTOO_SOON`.
2. If the vault is closed-ended and `now >= RedemptionDate`, return `tecEXPIRED`.
3. If the vault is closed-ended and `StartDate + (PaymentInterval × PaymentTotal) + LOAN_REDEMPTION_BUFFER` is greater than `RedemptionDate`, where `StartDate` is `now` (XLS-66 sets a new loan's `StartDate` to the ledger close time), return `tecNO_PERMISSION`. Equivalently, the loan's final scheduled payment MUST fall at least `LOAN_REDEMPTION_BUFFER` seconds before `RedemptionDate`. The whole expression MUST be computed in arithmetic wide enough to hold it without overflow.

XLS-draft-closed-ended-vault/README.md:31

  • The statement that existing open-ended vaults are “unaffected” conflicts with §9.2.1: creating a new LoanBroker against any open-ended vault is rejected, including an existing vault with no broker. Section 13 only grandfathers already-existing brokers. Scope this compatibility claim to the phase behavior/existing brokers, or document the new broker-creation restriction as an intentional incompatibility.
This proposal extends the [Single Asset Vault](../XLS-0065-single-asset-vault/README.md) (XLS-65) with a new `ClosedEnded` vault kind that enforces this lifecycle on-chain. Existing open-ended vaults are unaffected and continue to behave exactly as before.

XLS-draft-closed-ended-vault/README.md:62

  • The table calls 946708560 the maximum investment period, but the normative rule uses the strict inequality RedemptionDate < SubscriptionDate + MAX_INVESTMENT_PERIOD and the test plan rejects a gap equal to MAX_INVESTMENT_PERIOD (14.1). The largest accepted gap is therefore 946708559 seconds; clients treating the listed value as an inclusive maximum will submit a transaction the spec says is malformed.
| `MAX_INVESTMENT_PERIOD`  | `946708560` | Maximum length, in seconds, of the Investment phase (30 Gregorian years of 365.2425 days).                          |
  • Files reviewed: 1/1 changed files
  • Comments generated: 4
  • Review effort level: Lite

Comment thread XLS-draft-closed-ended-vault/README.md Outdated
Comment thread XLS-draft-closed-ended-vault/README.md Outdated
Comment thread XLS-draft-closed-ended-vault/README.md Outdated
Comment thread XLS-draft-closed-ended-vault/README.md Outdated

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.

🟡 Changes recommended

Several unresolved specification and documentation issues affect phase boundaries, amendment gating, tests, compatibility, and examples.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (6)

XLS-draft-closed-ended-vault/README.md:61

  • MAX_INVESTMENT_PERIOD is used as an exclusive upper bound in the invariant and creation check (RedemptionDate < SubscriptionDate + MAX_INVESTMENT_PERIOD), and the test plan rejects a gap exactly equal to this value. Thus 946708560 is not the maximum permitted gap—the largest valid gap is one second smaller. Describing it as the “maximum length” is inconsistent and can lead an implementation to accept the rejected boundary; call it an exclusive upper bound or change the predicate if the 30-year boundary is intended to be valid.
| `MAX_INVESTMENT_PERIOD`  | `946708560` | Maximum length, in seconds, of the Investment phase (30 Gregorian years of 365.2425 days).                          |

XLS-draft-closed-ended-vault/README.md:479

  • This test case is not unconditional: because the loan's final scheduled payment is before RedemptionDate, a payment submitted after RedemptionDate is after Loan.NextPaymentDueDate, and XLS-66's late-payment check rejects it unless tfLoanLatePayment is set and the amount covers the late-payment charges. Add those requirements to the test, or state only that the closed-ended phase does not add a rejection.
- A payment made after `RedemptionDate`, on a loan whose schedule ended before it, still succeeds and adds the proceeds to the vault. The phase does not block repayment (see A.5).

XLS-draft-closed-ended-vault/README.md:17

  • The abstract says the vault leaves Subscription “at SubscriptionDate,” but the normative phase table and transaction gates define now == SubscriptionDate as Subscription (VaultDeposit remains allowed and LoanSet remains rejected), with Investment beginning only when now > SubscriptionDate. Since this exact boundary changes transaction outcomes, summarize the transition as occurring after SubscriptionDate to avoid an off-by-one interpretation.
This proposal introduces a new **closed-ended** vault kind that moves through three deterministic phases - **Subscription**, **Investment**, and **Redemption** - and restricts deposits and withdrawals according to the current phase. It adds three fields to the `Vault` ledger entry (`VaultKind`, `SubscriptionDate`, `RedemptionDate`) plus phase enforcement in the vault and lending transactors. Both phase boundaries are _date-driven_ and immutable: a vault leaves Subscription for Investment at `SubscriptionDate` (after which new deposits are rejected and capital is locked), and leaves Investment for Redemption at `RedemptionDate`. Loans originated against a closed-ended vault must be scheduled to end a short buffer before `RedemptionDate`, so no payment is scheduled to fall due once Redemption has opened. Open-ended vaults are behaviourally unaffected.

XLS-draft-closed-ended-vault/README.md:288

  • This condition needs the amendment gate. When LendingProtocolV1_1 is disabled, every existing vault is open-ended (VaultKind is absent), so the current not ClosedEnded test rejects every new LoanBrokerSet, contradicting the statement in 2.3 that the parent behavior remains unchanged. Apply this rejection only when LendingProtocolV1_1 is enabled.
1. If `LoanBrokerID` is not specified (i.e. a new `LoanBroker` is being created) and the vault's `VaultKind` is not `ClosedEnded`, return `tecNO_PERMISSION`.

XLS-draft-closed-ended-vault/README.md:27

  • Redemption is date-driven even when loans remain outstanding: 7.4 and A.5 explicitly allow a late loan/payment to continue past RedemptionDate. Describing this phase as one where “the loan terms have ended” contradicts that behavior and can lead integrators to assume no loan obligations remain. Refer to the scheduled lending window instead.
- **Redemption** — the wind-down. The loan terms have ended, no new lending takes place, and depositors withdraw their share of whatever capital has been returned. Loans that were repaid late, or not at all, do not hold the phase open.

XLS-draft-closed-ended-vault/README.md:199

  • These VaultCreate example dates are earlier than the current Ripple timestamp (about 811 million seconds on 2026-09-15), but 4.2.2 requires both dates to be strictly after the parent ledger close time. As written, copying this request cannot succeed; use future example values or explicitly identify a historical parent ledger.
  "SubscriptionDate": 711232800,
  "RedemptionDate": 721600800
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread XLS-draft-closed-ended-vault/README.md Outdated
Clarified the behavior of open-ended vaults and their interactions with new features and existing loan brokers. Emphasized that existing serialised vaults remain unchanged and outlined the conditions under which open-ended vaults operate.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

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.

🟡 Changes recommended

An unresolved critical activation-gate issue must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

XLS-0065-single-asset-vault/65.1/README.md:27

  • This new index row is plain text, so readers cannot navigate from XLS-65.1 to the closed-ended vault specification; the adjacent specification rows are links. Link this entry to the draft so the index exposes the normative document it describes.
| Closed-ended Vault                                                        | Adds the `ClosedEnded` vault kind with `SubscriptionDate` and `RedemptionDate`, phase-gates vault and lending transactions, and requires a closed-ended vault for new loan brokers. |

XLS-draft-closed-ended-vault/README.md:17

  • This abstract says open-ended vaults are unaffected, but §8.2.1 changes LoanBrokerSet under LendingProtocolV1_1 by rejecting creation of a new broker against an open-ended vault. Narrow this claim or mention that exception so readers do not infer that all existing open-ended use remains unchanged.
This proposal introduces a new **closed-ended** vault kind that moves through three deterministic phases - **Subscription**, **Investment**, and **Redemption** - and restricts deposits and withdrawals according to the current phase. It adds three fields to the `Vault` ledger entry (`VaultKind`, `SubscriptionDate`, `RedemptionDate`) plus phase enforcement in the vault and lending transactors. Both phase boundaries are _date-driven_ and immutable: a vault leaves Subscription for Investment at `SubscriptionDate` (after which new deposits are rejected and capital is locked), and leaves Investment for Redemption at `RedemptionDate`. Loans originated against a closed-ended vault must be scheduled to end a short buffer before `RedemptionDate`, so no payment is scheduled to fall due once Redemption has opened. Open-ended vaults are behaviourally unaffected.
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread XLS-draft-closed-ended-vault/README.md Outdated
a1q123456 and others added 2 commits September 15, 2026 11:41
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

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.

🟡 Changes recommended

Four unresolved review findings remain in the closed-ended vault specification.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

XLS-draft-closed-ended-vault/README.md:296

  • This invariant is unconditional, but open-ended LoanBrokerSet creation is explicitly retained before LendingProtocolV1_1 (see §2.3/§12 and XLS-66 §3.3.3). Enforcing 8.4 as written before activation would reject valid pre-amendment brokers, contradicting the stated backwards compatibility. Scope this invariant to the enabled-amendment behavior.
- No `LoanBroker` is created against a vault whose `VaultKind` is not `ClosedEnded`.

XLS-draft-closed-ended-vault/README.md:150

  • The VaultCreate table repeats the same boundary ambiguity: §3.2 defines the timestamp as Subscription through equality, but "start of Investment phase" suggests Investment begins at equality. An implementer following this row could incorrectly reject a deposit or accept a loan at now == SubscriptionDate; use the explicit last-second/strictly-after wording here as well.
| `SubscriptionDate` | Conditional | `number`  |   `UINT32`    |     `N/A`     | **New.** End of Subscription / start of Investment phase. REQUIRED if `VaultKind == ClosedEnded`. Immutable after creation. |

XLS-draft-closed-ended-vault/README.md:272

  • Because fixCleanup3_4_0 is explicitly not required, the legacy unimpairment behavior described here can move NextPaymentDueDate beyond RedemptionDate after origination. That contradicts the abstract and rationale's guarantee that a closed-ended loan has no scheduled payment after Redemption opens. Preserve the maturity bound in LoanManage, require the cleanup amendment, or weaken the guarantee and update the related rationale/tests to make the check origination-only.
- No closed-ended `LoanSet` succeeds unless the loan's final scheduled payment is at least `LOAN_REDEMPTION_BUFFER` seconds before `RedemptionDate`. The bound is checked at origination only. `StartDate` and `PaymentInterval` are immutable, and `LoanPay` moves `NextPaymentDueDate` forward by exactly one `PaymentInterval` per scheduled payment, late or not, so repayment keeps the schedule in place. `LoanManage` is not phase-gated. Before `fixCleanup3_4_0`, XLS-66 unimpairment re-bases the due date of a loan that is already late to the current close time plus one `PaymentInterval`, which can push that loan's remaining schedule past `RedemptionDate`; impairment only ever moves a due date earlier, and from `fixCleanup3_4_0` onwards neither changes it. This proposal does not re-check the bound in that case. A loan that pays on time keeps its origination schedule, so the bound continues to hold for it, and a loan that is already late is the case 11.2 and A.5 already accept. The vault is unaffected either way: Redemption still opens on `RedemptionDate`, and a late payment is added when it arrives (see A.5).
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread XLS-draft-closed-ended-vault/README.md Outdated

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.

🔵 Needs a closer look

Two moderate findings remain unresolved around redemption liquidity semantics and LoanPay schedule behavior.

Review details

Suppressed comments (2)

XLS-draft-closed-ended-vault/README.md:542

  • This describes redemption as cash-basis and says a depositor receives a pro-rata share of assets collected so far, but XLS-65 still computes a share redemption from AssetsTotal - LossUnrealized and rejects it when AssetsAvailable is below that computed payout (see XLS-0065-single-asset-vault/README.md:637, 668-681). If the vault has lent all of its cash, a late final payment can therefore make a full-share withdrawal fail until the payment arrives, rather than pay the depositor a smaller cash-based amount. Please reconcile this FAQ and the Redemption permission semantics with the inherited liquidity check, or specify the required change to the withdrawal algorithm.
Redemption begins unconditionally at `RedemptionDate`, and depositors may withdraw from that point on. Because the vault uses cash-basis accounting, a withdrawing depositor only ever receives a share of the assets the vault has actually collected at that moment, which excludes any payment that has not yet arrived. A payment that lands after `RedemptionDate` is simply added to the vault's assets when it is received; it is not accrued in advance.

XLS-draft-closed-ended-vault/README.md:272

  • XLS-66 also automatically unimpairs an impaired loan at the start of LoanPay (§3.11.5), using the same pre-fixCleanup3_4_0 rebasing rule as LoanManage (§3.10.5). Therefore a successful payment can move NextPaymentDueDate and the remaining schedule past RedemptionDate; the claim here that LoanPay preserves the origination schedule, and the test plan's only exception via LoanManage, are incomplete. State explicitly that the origination-only bound also permits this LoanPay path and cover it in the tests, or re-check/forbid it if that is not intended.
- No closed-ended `LoanSet` succeeds unless the loan's final scheduled payment is at least `LOAN_REDEMPTION_BUFFER` seconds before `RedemptionDate`. The bound is checked at origination only. `StartDate` and `PaymentInterval` are immutable, and `LoanPay` moves `NextPaymentDueDate` forward by exactly one `PaymentInterval` per scheduled payment, late or not, so repayment keeps the schedule in place. `LoanManage` is not phase-gated. Before `fixCleanup3_4_0`, XLS-66 unimpairment re-bases the due date of a loan that is already late to the current close time plus one `PaymentInterval`, which can push that loan's remaining schedule past `RedemptionDate`; impairment only ever moves a due date earlier, and from `fixCleanup3_4_0` onwards neither changes it. This proposal does not re-check the bound in that case. A loan that pays on time keeps its origination schedule, so the bound continues to hold for it, and a loan that is already late is the case 11.2 and A.5 already accept. The vault is unaffected either way: Redemption still opens on `RedemptionDate`, and a late payment is added when it arrives (see A.5).
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@Tapanito
Tapanito merged commit f68f93b into XRPLF:master Sep 15, 2026
6 checks passed
pdp2121 added a commit to XRPLF/xrpl.js that referenced this pull request Sep 15, 2026
## High Level Overview of Change

<!--
Please include a summary/list of the changes.
If too broad, please consider splitting into multiple PRs.
If a relevant Asana task, please link it here.
-->
Support LendingProtocolV1_1
rippled: https://github.com/XRPLF/rippled/pulls?page=1&q=is%3Apr+label%3A%22feature%3A+LP1.1%22
Specs:
XRPLF/XRPL-Standards#587
XRPLF/XRPL-Standards#582

### Context of Change

<!--
Please include the context of a change.
If a bug fix, when was the bug introduced? What was the behavior?
If a new feature, why was this architecture chosen? What were the alternatives?
If a refactor, how is this better than the previous implementation?

If there is a design document for this feature, please link it here.
-->

### Type of Change

<!--
Please check relevant options, delete irrelevant ones.
-->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Refactor (non-breaking change that only restructures code)
- [ ] Tests (You added tests for code that already exists, or your new feature included in this PR)
- [ ] Documentation Updates
- [ ] Release

### Did you update HISTORY.md?

- [x] Yes
- [ ] No, this change does not impact library users
pdp2121 added a commit to XRPLF/xrpl-py that referenced this pull request Sep 16, 2026
## High Level Overview of Change

<!--
Please include a summary/list of the changes.
If too broad, please consider splitting into multiple PRs.
If a relevant Asana task, please link it here.
-->
Support LendingProtocolV1_1
rippled: https://github.com/XRPLF/rippled/pulls?page=1&q=is%3Apr+label%3A%22feature%3A+LP1.1%22
Specs:
XRPLF/XRPL-Standards#587
XRPLF/XRPL-Standards#582


### Type of Change

<!--
Please check relevant options, delete irrelevant ones.
-->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Refactor (non-breaking change that only restructures code)
- [ ] Tests (You added tests for code that already exists, or your new feature included in this PR)
- [ ] Documentation Updates
- [ ] Release

### Did you update CHANGELOG.md?

- [x] Yes
- [ ] No, this change does not impact library users
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.

6 participants