Skip to content

spec(XLS-65): Update VaultCreate failure conditions and state changes - #549

Open
Tapanito wants to merge 36 commits into
masterfrom
tapanito/spec-vault-create
Open

spec(XLS-65): Update VaultCreate failure conditions and state changes#549
Tapanito wants to merge 36 commits into
masterfrom
tapanito/spec-vault-create

Conversation

@Tapanito

Copy link
Copy Markdown
Collaborator

Syncs the VaultCreate spec section with the current implementation in src/libxrpl/tx/transactors/vault/VaultCreate.cpp.

Data Verification (3.2.5.1) — was _TBD_, now populated:

  • Data > 256 bytes
  • Invalid WithdrawalPolicy value
  • DomainID = 0
  • DomainID without tfVaultPrivate
  • Negative AssetsMaximum
  • Empty or oversized MPTokenMetadata
  • Scale with XRP or MPT asset
  • Scale > 18 for IOU asset

Protocol-Level Failures (3.2.5.2) — reorganised and extended:

  • Removed preflight checks (moved to Data Verification above)
  • Added terNO_ACCOUNT, terNO_RIPPLE for IOU issuer checks (from canAddHolding)
  • Added tecWRONG_ASSET for pseudo-account issuers
  • Added tecLOCKED for MPT lock (global or per-account, via isFrozen)
  • Added terADDRESS_COLLISION for pseudo-account address collision
  • Added error codes to all items

State Changes (3.2.6) — extended:

  • MPTokenIssuance flags based on tfVaultShareNonTransferable and tfVaultPrivate
  • Authorized MPToken for pseudo-account when vault is private
  • XRP case: no holding object created
  • OwnerCount incremented by 2

Tapanito and others added 30 commits February 11, 2026 15:15
…tion

- Add Example JSON sections for Vault ledger entry and all transactions
  (VaultCreate, VaultSet, VaultDelete, VaultDeposit, VaultWithdraw,
  VaultClawback, Payment) with real transaction data
- Add invariants for the Vault ledger entry (universal checks) and all
  transaction types derived from the ValidVault invariant checker
- Restructure section 10 from "API" to "RPC: vault_info" matching the
  amendment template format with Request Fields, Response Fields,
  Failure Conditions, Example Request, and Example Response subsections
- Update response fields table with missing fields (Data, Asset.mpt_issuance_id,
  shares.DomainID, shares.MPTokenMetadata) and correct Always Present values
- Update response examples to use proper JSON format with response envelope
- Add section 9.1 Fields for Payment transaction
- Remove Index section and all Return to Index links

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Reorganize top-level sections: Abstract (1), Introduction (2),
  Specification (3), Rationale (4), Security Considerations (5),
  Appendix
- Move all ledger entry, transaction, and RPC sections under
  "3. Specification" as subsections (3.1-3.9)
- Remove "1.1 Overview" heading, merge content into Introduction body
- Renumber Introduction subsections: Terminology (2.1), Actors (2.2),
  Connecting to the Vault (2.3)
- Demote all specification headings by one level with new numbering
- Add Rationale section explaining decoupled vault design
- Rename FAQ section to "Appendix A: FAQ" with A.x numbering
- Fix heading levels for Key Variables and Vault State Update

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove functional additions (invariants, example JSONs, error codes)
added in this branch and retain only structural changes that bring
the spec into conformance with AMENDMENT_TEMPLATE.md and XLS_TEMPLATE.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Convert failure conditions and state changes from numbered lists back
to master's original nested bullet-point format. Keep the Data
Verification / Protocol-Level Failures subsection headers as template
compliance, but use master's original content and structure inside them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Convert bullet points in Failure Conditions and State Changes sections
to numbered lists with nested sub-numbering, per template requirements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Mayukha Vadari <mvadari@gmail.com>
Base automatically changed from tapanito/vault-enhanced to master May 28, 2026 15:12
An error occurred while trying to automatically change base from tapanito/vault-enhanced to master May 28, 2026 15:12

@tyalymov tyalymov left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR syncs the spec with the current VaultCreate code, so I want to flag one thing. The order of the failure conditions in 3.2.5.2 differs from the evaluation order in VaultCreate.cpp, and that order is observable: when several conditions hold at once, the transactor returns the first one it hits.

Order in the code:
preclaim (checks run in this order):

  1. canAddHolding — can the vault's pseudo-account hold this asset at all:
    IOU: terNO_ACCOUNT (issuer account missing),
    then terNO_RIPPLE (issuer has no DefaultRipple)
    MPT: tecOBJECT_NOT_FOUND (no MPTokenIssuance),
    then tecNO_AUTH (token not transferable)
  2. tecWRONG_ASSET (issuer is a pseudo-account)
  3. freeze/lock: tecFROZEN (IOU) / tecLOCKED (MPT)
  4. domain missing: tecOBJECT_NOT_FOUND
  5. terADDRESS_COLLISION

doApply (runs after all of preclaim):
6. tecINSUFFICIENT_RESERVE
Two places where the spec list disagrees:

tecWRONG_ASSET vs freeze/lock. The spec puts the freeze/lock checks (1.3 and 2.3) before tecWRONG_ASSET (item 3), but the code checks the pseudo-account issuer first. So for an asset whose issuer is a pseudo-account and is also frozen, the spec implies tecFROZEN/tecLOCKED while the code returns tecWRONG_ASSET.

tecINSUFFICIENT_RESERVE vs terADDRESS_COLLISION. The spec lists reserve (item 5) before address collision (item 6). In the code the collision check is in preclaim and the reserve check is in doApply, which runs later, so the order is reversed.

Is the numbered list meant to be the evaluation order? If so, can we move items 3 and 5/6 to match preclaim and doApply? If it is just a list of conditions with no order implied, a one-line note saying so would help, so nobody reads a precedence into it.

3.2.5.2 now follows the actual preclaim/doApply evaluation order:
tecWRONG_ASSET is checked before freeze/lock, and terADDRESS_COLLISION
(preclaim) before tecINSUFFICIENT_RESERVE (doApply, runs later).

Addresses review feedback on PR #549.
@Tapanito

Copy link
Copy Markdown
Collaborator Author

Good catch, and yes — the list is meant to reflect evaluation order. Verified against VaultCreate.cpp: tecWRONG_ASSET (pseudo-account issuer) is checked in preclaim before the freeze/lock check, and terADDRESS_COLLISION (preclaim) happens before tecINSUFFICIENT_RESERVE (doApply, which runs strictly after preclaim). Reordered 3.2.5.2 to: canAddHolding checks (1–2) → tecWRONG_ASSET → freeze/lock → domain missing → terADDRESS_COLLISION → tecINSUFFICIENT_RESERVE.

| `WithdrawalPolicy` | No | `number` | `UINT8` | `"FirstComeFirstServe"` | Indicates the withdrawal strategy used by the Vault. |
| `DomainID` | No | `string` | `HASH256` | | The `PermissionedDomain` object ID associated with the shares of this Vault. |
| `Scale` | No | `number` | `UINT8` | 6 | The `Scale` specifies the power of 10 ($10^{\text{scale}}$) to multiply an asset's value by when converting it into an integer-based number of shares. |
| `Scale` | No | `number` | `UINT8` | 6 | The `Scale` specifies the power of 10 ($10^{\text{scale}}$) to multiply an asset's value by when converting it into an integer-based number of shares. Ignored (fixed at `0`) when `Asset` is `XRP` or `MPT`; not stored on the `Vault` object in those cases. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The Scale row says the field is ignored for XRP and MPT, but 3.2.5.1 item 7 in this same PR rejects it with temMALFORMED. VaultCreate.cpp:92-96 does the latter: if Scale is present and the asset is XRP or MPT, preflight fails. "Rejected" would be the accurate word here.

The "not stored on the Vault object" half is right: VaultCreate.cpp:243 only writes sfScale when the value is non-zero.

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.

2 participants