diff --git a/XLS-0065-single-asset-vault/README.md b/XLS-0065-single-asset-vault/README.md
index 2d5230a3e..ab677af09 100644
--- a/XLS-0065-single-asset-vault/README.md
+++ b/XLS-0065-single-asset-vault/README.md
@@ -8,12 +8,12 @@
category: Amendment
requires: [XLS-33](../XLS-0033-multi-purpose-tokens/README.md)
created: 2024-04-12
- updated: 2025-11-17
+ updated: 2026-02-11
# Single Asset Vault
-## _Abstract_
+## Abstract
A Single Asset Vault is a new on-chain primitive for aggregating assets from one or more depositors, and making the assets available for other on-chain protocols. The Single Asset Vault uses [Multi-Purpose-Token](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0033-multi-purpose-tokens) to represent ownership shares of the Vault. The Vault serves diverse purposes, such as lending markets, aggregators, yield-bearing tokens, asset management, etc. The Single Asset Vault decouples the liquidity provision functionality from the specific protocol logic.
@@ -24,26 +24,22 @@ A Single Asset Vault is a new on-chain primitive for aggregating assets from one
- [**1.2. Terminology**](#12-terminology)
- [**1.3. Actors**](#13-actors)
- [**1.4 Connecting to the Vault**](#14-connecting-to-the-vault)
-- [**2. Ledger Entries**](#2-ledger-entries)
- - [**2.1. `Vault` Ledger Entry**](#21-vault-ledger-entry)
- - [**2.1.1. Object Identifier**](#211-object-identifier)
- - [**2.1.2. Fields**](#212-fields)
- - [**2.1.3. Vault _pseudo-account_**](#213-vault-_pseudo-account_)
- - [**2.1.4. Ownership**](#214-ownership)
- - [**2.1.5. Owner Reserve**](#215-owner-reserve)
- - [**2.1.6. Vault Shares**](#216-vault-shares)
- - [**2.1.7. Exchange Algorithm**](#217-exchange-algorithm)
-- [**3. Transactions**](#3-transactions)
- - [**3.1. VaultCreate, VaultSet & VaultDelete Transactions**](#31-vaultcreate-vaultset-vaultdelete-transactions)
- - [**3.1.1. VaultCreate Transaction**](#311-vaultcreate-transaction)
- - [**3.1.2. VaultSet Transaction**](#312-vaultset-transaction)
- - [**3.1.3. VaultDelete Transaction**](#313-vaultdelete-transaction)
- - [**3.2. VaultDeposit & VaultWithdraw Transactions**](#32-vaultdeposit-and-vaultwithdraw-transactions)
- - [**3.2.1. VaultDeposit Transaction**](#321-vaultdeposit-transaction)
- - [**3.2.2. VaultWithdraw Transaction**](#322-vaultwithdraw-transaction)
- - [**3.3. VaultClawback Transaction**](#33-vaultclawback-transaction)
- - [**3.4. Payment Transaction**](#34-payment-transaction)
-- [**4. API**](#4-api)
+- [**2. Ledger Entry: `Vault`**](#2-ledger-entry-vault)
+ - [**2.1. Object Identifier**](#21-object-identifier)
+ - [**2.2. Fields**](#22-fields)
+ - [**2.3. Vault _pseudo-account_**](#23-vault-_pseudo-account_)
+ - [**2.4. Ownership**](#24-ownership)
+ - [**2.5. Owner Reserve**](#25-owner-reserve)
+ - [**2.6. Vault Shares**](#26-vault-shares)
+ - [**2.7. Exchange Algorithm**](#27-exchange-algorithm)
+- [**3. Transaction: `VaultCreate`**](#3-transaction-vaultcreate)
+- [**4. Transaction: `VaultSet`**](#4-transaction-vaultset)
+- [**5. Transaction: `VaultDelete`**](#5-transaction-vaultdelete)
+- [**6. Transaction: `VaultDeposit`**](#6-transaction-vaultdeposit)
+- [**7. Transaction: `VaultWithdraw`**](#7-transaction-vaultwithdraw)
+- [**8. Transaction: `VaultClawback`**](#8-transaction-vaultclawback)
+- [**9. Transaction: `Payment`**](#9-transaction-payment)
+- [**10. API**](#10-api)
- [Appendix](#appendix)
## 1. Introduction
@@ -100,13 +96,11 @@ A protocol connecting to a Vault must track its debt. Furthermore, the updates t
[**Return to Index**](#index)
-## 2. Ledger Entries
-
-### 2.1 `Vault` Ledger Entry
+## 2. Ledger Entry: `Vault`
The **`Vault`** ledger entry describes the state of the tokenized vault.
-#### 2.1.1 Object Identifier
+### 2.1 Object Identifier
The key of the `Vault` object is the result of [`SHA512-Half`](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#hashes) of the following values concatenated in order:
@@ -114,32 +108,32 @@ The key of the `Vault` object is the result of [`SHA512-Half`](https://xrpl.org/
- The [`AccountID`](https://xrpl.org/docs/references/protocol/binary-format/#accountid-fields) of the account submitting the `VaultSet`transaction, i.e.`VaultOwner`.
- The transaction `Sequence` number. If the transaction used a [Ticket](https://xrpl.org/docs/concepts/accounts/tickets/), use the `TicketSequence` value.
-#### 2.1.2 Fields
+### 2.2 Fields
A vault has the following fields:
-| Field Name | Modifiable? | Required? | JSON Type | Internal Type | Default Value | Description |
-| ------------------- | :---------: | :----------------: | :----------------: | :-----------: | :-----------: | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `LedgerEntryType` | `N/A` | :heavy_check_mark: | `string` | `UINT16` | `0x0084` | Ledger object type. |
-| `LedgerIndex` | `N/A` | :heavy_check_mark: | `string` | `UINT16` | `N/A` | Ledger object identifier. |
-| `Flags` | `Yes` | :heavy_check_mark: | `string` | `UINT32` | 0 | Ledger object flags. |
-| `PreviousTxnID` | `N/A` | :heavy_check_mark: | `string` | `HASH256` | `N/A` | Identifies the transaction ID that most recently modified this object. |
-| `PreviousTxnLgrSeq` | `N/A` | :heavy_check_mark: | `number` | `UINT32` | `N/A` | The sequence of the ledger that contains the transaction that most recently modified this object. |
-| `Sequence` | `N/A` | :heavy_check_mark: | `number` | `UINT32` | `N/A` | The transaction sequence number that created the vault. |
-| `OwnerNode` | `N/A` | :heavy_check_mark: | `number` | `UINT64` | `N/A` | Identifies the page where this item is referenced in the owner's directory. |
-| `Owner` | `No` | :heavy_check_mark: | `string` | `AccountID` | `N/A` | The account address of the Vault Owner. |
-| `Account` | `N/A` | :heavy_check_mark: | `string` | `ACCOUNTID` | `N/A` | The address of the Vaults _pseudo-account_. |
-| `Data` | `Yes` | | `string` | `BLOB` | None | Arbitrary metadata about the Vault. Limited to 256 bytes. |
-| `Asset` | `No` | :heavy_check_mark: | `string or object` | `ISSUE` | `N/A` | The asset of the vault. The vault supports `XRP`, `IOU` and `MPT`. |
-| `AssetsTotal` | `N/A` | :heavy_check_mark: | `number` | `NUMBER` | 0 | The total value of the vault. |
-| `AssetsAvailable` | `N/A` | :heavy_check_mark: | `number` | `NUMBER` | 0 | The asset amount that is available in the vault. |
-| `LossUnrealized` | `N/A` | :heavy_check_mark: | `number` | `NUMBER` | 0 | The potential loss amount that is not yet realized expressed as the vaults asset. |
-| `AssetsMaximum` | `Yes` | | `number` | `NUMBER` | 0 | The maximum asset amount that can be held in the vault. Zero value `0` indicates there is no cap. |
-| `ShareMPTID` | `N/A` | :heavy_check_mark: | `number` | `UINT192` | 0 | The identifier of the share MPTokenIssuance object. |
-| `WithdrawalPolicy` | `No` | :heavy_check_mark: | `string` | `UINT8` | `N/A` | Indicates the withdrawal strategy used by the Vault. |
-| `Scale` | `No` | :heavy_check_mark: | `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. |
-
-##### 2.1.2.1 Flags
+| Field Name | Constant | Required | JSON Type | Internal Type | Default Value | Description |
+| ------------------- | :------: | :------: | :----------------: | :-----------: | :-----------: | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `LedgerEntryType` | `No` | Yes | `string` | `UINT16` | `0x0084` | Ledger object type. |
+| `LedgerIndex` | `No` | Yes | `string` | `UINT16` | `N/A` | Ledger object identifier. |
+| `Flags` | `Yes` | Yes | `string` | `UINT32` | 0 | Ledger object flags. |
+| `PreviousTxnID` | `No` | Yes | `string` | `HASH256` | `N/A` | Identifies the transaction ID that most recently modified this object. |
+| `PreviousTxnLgrSeq` | `No` | Yes | `number` | `UINT32` | `N/A` | The sequence of the ledger that contains the transaction that most recently modified this object. |
+| `Sequence` | `No` | Yes | `number` | `UINT32` | `N/A` | The transaction sequence number that created the vault. |
+| `OwnerNode` | `No` | Yes | `number` | `UINT64` | `N/A` | Identifies the page where this item is referenced in the owner's directory. |
+| `Owner` | `No` | Yes | `string` | `AccountID` | `N/A` | The account address of the Vault Owner. |
+| `Account` | `No` | Yes | `string` | `ACCOUNTID` | `N/A` | The address of the Vaults _pseudo-account_. |
+| `Data` | `Yes` | No | `string` | `BLOB` | None | Arbitrary metadata about the Vault. Limited to 256 bytes. |
+| `Asset` | `No` | Yes | `string or object` | `ISSUE` | `N/A` | The asset of the vault. The vault supports `XRP`, `IOU` and `MPT`. |
+| `AssetsTotal` | `No` | Yes | `number` | `NUMBER` | 0 | The total value of the vault. |
+| `AssetsAvailable` | `No` | Yes | `number` | `NUMBER` | 0 | The asset amount that is available in the vault. |
+| `LossUnrealized` | `No` | Yes | `number` | `NUMBER` | 0 | The potential loss amount that is not yet realized expressed as the vaults asset. |
+| `AssetsMaximum` | `Yes` | No | `number` | `NUMBER` | 0 | The maximum asset amount that can be held in the vault. Zero value `0` indicates there is no cap. |
+| `ShareMPTID` | `No` | Yes | `number` | `UINT192` | 0 | The identifier of the share MPTokenIssuance object. |
+| `WithdrawalPolicy` | `No` | Yes | `string` | `UINT8` | `N/A` | Indicates the withdrawal strategy used by the Vault. |
+| `Scale` | `No` | Yes | `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. |
+
+#### 2.2.1 Flags
The `Vault` object supports the following flags:
@@ -147,48 +141,48 @@ The `Vault` object supports the following flags:
| ----------------- | :----------: | :---------: | :------------------------------------------: |
| `lsfVaultPrivate` | `0x00010000` | `No` | If set, indicates that the vault is private. |
-#### 2.1.3 Vault `_pseudo-account_`
+### 2.3 Vault `_pseudo-account_`
An AccountRoot entry holds the XRP, IOU or MPT deposited into the vault. It also acts as the issuer of the vault's shares. The _pseudo-account_ follows the XLS-64 specification for pseudo accounts. The `AccountRoot` object is created when creating the `Vault` object.
-#### 2.1.4 Ownership
+### 2.4 Ownership
The `Vault` objects are stored in the ledger and tracked in an [Owner Directory](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/directorynode) owned by the account submitting the `VaultSet` transaction. Furthermore, to facilitate `Vault` object lookup from the vault shares, the object is also tracked in the `OwnerDirectory` of the _`pseudo-account`_.
-#### 2.1.5 Owner Reserve
+### 2.5 Owner Reserve
The `Vault` object costs one reserve fee per object created:
- The `Vault` object itself.
- The `MPTokenIssuance` associated with the shares of the Vault.
-#### 2.1.6 Vault Shares
+### 2.6 Vault Shares
Shares represent the portion of the Vault assets a depositor owns. Vault Owners set the currency code of the share and whether the token is transferable during the vault's creation. These two values are immutable. The share is represented by a [Multi-Purpose Token](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0033-multi-purpose-tokens). The MPT is issued by the vault's pseudo-account.
-##### 2.1.6.1 `Scale`
+#### 2.6.1 `Scale`
The **`Scale`** field enables the vault to accurately represent fractional asset values using integer-only MPT shares, which prevents the loss of value from decimal truncation. It defines a scaling factor, calculated as $10^{\text{Scale}}$, that converts a decimal asset amount into a corresponding whole number of shares. For example, with a `Scale` of `6`, a deposit of **20.3** assets is multiplied by $10^6$ and credited as **20,300,000** shares.
As a general rule, all calculations involving MPTs are executed with a precision of a single MPT, treating them as indivisible units. If a calculation results in a fractional amount, it will be rounded up, down or to the nearest whole number depending on the context. Crucially, the rounding direction is determined by the protocol and is not controlled by the transaction submitter, which may lead to unexpected results.
-##### 2.1.6.1.1 `IOU`
+##### 2.6.1.1 `IOU`
When a vault holds an **`IOU`**, the `Scale` is configurable by the Vault Owner at the time of the vault's creation. The value can range from **0** to a maximum of **18**, with a default of **6**. This flexibility allows issuers to set a level of precision appropriate for their specific token.
-##### 2.1.6.1.2 `XRP`
+##### 2.6.1.2 `XRP`
When a vault holds **`XRP`**, the `Scale` is fixed at **0**. This aligns with XRP's native structure, where one share represents one drop (the smallest unit of XRP), and one XRP equals 1,000,000 drops. Therefore, a deposit of 10 XRP to an empty Vault will result in the issuance of 10,000,000 shares ($10 \times 10^6$).
-##### 2.1.6.1.3 `MPT`
+##### 2.6.1.3 `MPT`
When a vault holds `MPT`, its `Scale` is fixed at **0**. This creates a 1-to-1 relationship between deposited MPT units and the shares issued (for example, depositing 10 MPTs to an empty Vault issues 10 shares). The value of a single MPT is determined at the issuer's discretion. If an MPT is set to represent a large value, the vault owner and the depositor must be cautious. Since only whole MPT units are used in calculations, any value that is not a multiple of a single MPT's value may be lost due to rounding during a transaction.
-##### 2.1.6.2 `MPTokenIssuance`
+#### 2.6.2 `MPTokenIssuance`
The `MPTokenIssuance` object represents the share on the ledger. It is created and deleted together with the `Vault` object.
-###### 2.1.6.2.1 `MPTokenIssuance` Values
+##### 2.6.2.1 `MPTokenIssuance` Values
Here’s the table with the headings "Field," "Description," and "Value":
@@ -209,11 +203,11 @@ The following flags are set based on whether the shares are transferable and if
| **Public Vault** | `lsfMPTCanEscrow`
`lsfMPTCanTrade`
`lsfMPTCanTransfer` | No Flags |
| **Private Vault** | `lsfMPTCanEscrow`
`lsfMPTCanTrade`
`lsfMPTCanTransfer`
`lsfMPTRequireAuth` | `lsfMPTRequireAuth` |
-##### 2.1.6.3 `MPToken`
+#### 2.6.3 `MPToken`
The `MPToken` object represents the amount of shares held by a depositor. It is created when the account deposits liquidity into the vault and is deleted when a depositor redeems (or transfers) all shares.
-###### 2.1.6.3.1 `MPToken` Values
+##### 2.6.3.1 `MPToken` Values
The `MPToken` values should be set as per the `MPT` [specification](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0033-multi-purpose-tokens#2112-fields).
@@ -222,11 +216,11 @@ The `MPToken` values should be set as per the `MPT` [specification](https://gith
| **Public Vault** | No Flags | `lsfMPTAuthorized` |
| **Private Vault** | `lsfMPTAuthorized` | `lsfMPTAuthorized` |
-#### 2.1.7 Exchange Algorithm
+### 2.7 Exchange Algorithm
Exchange Algorithm refers to the logic that is used to exchange assets into shares and shares into assets. This logic is executed when depositing or redeeming liquidity. A Vault comes with the default exchange algorithm, which is detailed below.
-##### 2.1.7.1 Unrealized Loss
+#### 2.7.1 Unrealized Loss
A well-informed depositor may learn of an incoming loss and redeem their shares early, causing the remaining depositors to bear the full loss. To discourage such behaviour, we introduce a concept of "paper loss," captured by the `Vault` object's `LossUnrealized` attribute. The "paper loss" captures a potential loss the vault may experience and thus temporarily decreases the vault value. Only a protocol connected to the `Vault` may increase or decrease the `LossUnrealized` attribute.
@@ -256,7 +250,7 @@ A depositor could deposit $100k assets at a 0.1 exchange rate and get 1.0m share
To account for this problem, the Vault must use two different exchange rate models: one for depositing assets and one for withdrawing them.
-### 2.1.7.2 Exchange Rate Algorithms
+#### 2.7.2 Exchange Rate Algorithms
This section details the algorithms used to calculate the exchange between assets and shares for deposits, redemptions, and withdrawals.
@@ -269,7 +263,7 @@ This section details the algorithms used to calculate the exchange between asset
- **$\iota$**: The vault's total **unrealized loss**.
- **$\sigma$**: The scaling factor derived from `Scale`, used to convert fractional assets into integer shares.
-### 2.1.7.2.1 Deposit
+##### 2.7.2.1 Deposit
The deposit function calculates the number of shares a user receives for their assets.
@@ -294,7 +288,7 @@ The vault's totals are updated with the final calculated amounts.
- **New Total Assets**: $\Gamma_{assets} \leftarrow \Gamma_{assets} + \Delta_{assets'}$
- **New Total Shares**: $\Gamma_{shares} \leftarrow \Gamma_{shares} + \Delta_{shares}$
-### 2.1.7.2.2 Redeem
+##### 2.7.2.2 Redeem
The redeem function calculates the asset payout for a user burning a specific number of shares.
@@ -311,7 +305,7 @@ The vault's totals are reduced after the redemption.
- **New Total Assets**: $\Gamma_{assets} \leftarrow \Gamma_{assets} - \Delta_{assets}$
- **New Total Shares**: $\Gamma_{shares} \leftarrow \Gamma_{shares} - \Delta_{shares}$
-### 2.1.7.2.3 Withdraw
+##### 2.7.2.3 Withdraw
The withdraw function handles a request for a specific amount of assets, which involves a two-step process to determine the final payout.
@@ -334,56 +328,52 @@ The vault's totals are reduced by the final calculated amounts.
- **New Total Assets**: $\Gamma_{assets} \leftarrow \Gamma_{assets} - \Delta_{assets\_out}$
- **New Total Shares**: $\Gamma_{shares} \leftarrow \Gamma_{shares} - \Delta_{shares}$
-##### 2.1.7.3 Withdrawal Policy
+#### 2.7.3 Withdrawal Policy
Withdrawal policy controls the logic used when removing liquidity from a vault. Each strategy has its own implementation, but it can be used in multiple vaults once implemented. Therefore, different vaults may have different withdrawal policies. The specification introduces the following options:
-###### 2.1.7.3.1 `first-come-first-serve`
+##### 2.7.3.1 `first-come-first-serve`
The First Come, First Serve strategy treats all requests equally, allowing a depositor to redeem any amount of assets provided they have a sufficient number of shares.
-#### 2.1.8 Frozen Assets
+### 2.8 Frozen Assets
The issuer of the Vaults asset may enact a freeze either through a [Global Freeze](https://xrpl.org/docs/concepts/tokens/fungible-tokens/freezes/#global-freeze) for IOUs or [locking MPT](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0033-multi-purpose-tokens#21122-flags). When the vaults asset is frozen, it can only be withdrawn by specifying the `Destination` account as the `Issuer` of the asset. Similarly, a frozen asset _may not_ be deposited into a vault. Furthermore, when the asset of a vault is frozen, the shares corresponding to the asset may not be transferred.
-#### 2.1.9 Transfer Fees
+### 2.9 Transfer Fees
The Vault does not apply the [Transfer Fee](https://xrpl.org/docs/concepts/tokens/transfer-fees) to `VaultDeposit` and `VaultWithdraw` transactions. Furthermore, whenever a protocol moves assets from or to a Vault, the `Transfer Fee` must not be charged.
[**Return to Index**](#index)
-## 3. Transactions
+## 3. Transaction: `VaultCreate`
All transactions introduced by this proposal incorporate the [common transaction fields](https://xrpl.org/docs/references/protocol/transactions/common-fields) that are shared by all transactions. Standard fields are only documented in this proposal if needed because this proposal introduces new possible values for such fields.
-### 3.1 `VaultCreate`, `VaultSet`, `VaultDelete` transactions
-
-The `Vault` object is managed with `VaultCreate`, `VaultSet` and `VaultDelete` transactions.
-
-#### 3.1.1 `VaultCreate` Transaction
-
The `VaultCreate` transaction creates a new `Vault` object.
-| Field Name | Required? | JSON Type | Internal Type | Default Value | Description |
-| ------------------ | :----------------: | :----------------: | :-----------: | :----------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `TransactionType` | :heavy_check_mark: | `string` | `UINT16` | `58` | The transaction type. |
-| `Flags` | :heavy_check_mark: | `number` | `UINT32` | 0 | Specifies the flags for the Vault. |
-| `Data` | | `string` | `BLOB` | | Arbitrary Vault metadata, limited to 256 bytes. |
-| `Asset` | :heavy_check_mark: | `string or object` | `ISSUE` | `N/A` | The asset (`XRP`, `IOU` or `MPT`) of the Vault. |
-| `AssetsMaximum` | | `number` | `NUMBER` | 0 | The maximum asset amount that can be held in a vault. |
-| `MPTokenMetadata` | | `string` | `BLOB` | | Arbitrary metadata about the share `MPT`, in hex format, limited to 1024 bytes. |
-| `WithdrawalPolicy` | | `number` | `UINT8` | `strFirstComeFirstServe` | Indicates the withdrawal strategy used by the Vault. |
-| `DomainID` | | `string` | `HASH256` | | The `PermissionedDomain` object ID associated with the shares of this Vault. |
-| `Scale` | | `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. |
+### 3.1 Fields
+
+| Field Name | Required | JSON Type | Internal Type | Default Value | Description |
+| ------------------ | :------: | :----------------: | :-----------: | :----------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `TransactionType` | Yes | `string` | `UINT16` | `58` | The transaction type. |
+| `Flags` | Yes | `number` | `UINT32` | 0 | Specifies the flags for the Vault. |
+| `Data` | No | `string` | `BLOB` | | Arbitrary Vault metadata, limited to 256 bytes. |
+| `Asset` | Yes | `string or object` | `ISSUE` | `N/A` | The asset (`XRP`, `IOU` or `MPT`) of the Vault. |
+| `AssetsMaximum` | No | `number` | `NUMBER` | 0 | The maximum asset amount that can be held in a vault. |
+| `MPTokenMetadata` | No | `string` | `BLOB` | | Arbitrary metadata about the share `MPT`, in hex format, limited to 1024 bytes. |
+| `WithdrawalPolicy` | No | `number` | `UINT8` | `strFirstComeFirstServe` | 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. |
-##### 3.1.1.1 Flags
+### 3.2 Flags
| Flag Name | Flag Value | Description |
| ----------------------------- | :----------: | :--------------------------------------------------------------------------------------- |
| `tfVaultPrivate` | `0x00010000` | Indicates that the vault is private. It can only be set during Vault creation. |
| `tfVaultShareNonTransferable` | `0x00020000` | Indicates the vault share is non-transferable. It can only be set during Vault creation. |
-###### 3.1.1.2 WithdrawalPolicy
+#### 3.3 WithdrawalPolicy
The type indicates the withdrawal strategy supported by the vault. The following values are supported:
@@ -391,190 +381,211 @@ The type indicates the withdrawal strategy supported by the vault. The following
| ---------------------------------- | :------: | :-------------------------------------------------------: |
| `vaultStrategyFirstComeFirstServe` | `0x0001` | Requests are processed on a first-come-first-serve basis. |
-##### 3.1.1.3 Transaction Fees
+### 3.4 Transaction Fees
The transaction creates an `AccountRoot` object for the `_pseudo-account_`. Therefore, the transaction [must destroy](../XLS-0064-pseudo-account/README.md) one incremental owner reserve amount.
-##### 3.1.1.4 Failure Conditions
-
-- The `Asset` is `XRP`:
- - The `Scale` parameter is provided.
+### 3.5 Failure Conditions
-- The `Asset` is `MPT`:
- - The `Scale` parameter is provided.
- - The `lsfMPTCanTransfer` is not set in the `MPTokenIssuance` object. (the asset is not transferable).
- - The `lsfMPTLocked` flag is set in the `MPTokenIssuance` object. (the asset is locked).
+1. The `Asset` is `XRP`:
+ 1. The `Scale` parameter is provided.
-- The `Asset` is an `IOU`:
- - The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
- - The `Scale` parameter is provided, and is less than **0** or greater than **18**.
+2. The `Asset` is `MPT`:
+ 1. The `Scale` parameter is provided.
+ 2. The `lsfMPTCanTransfer` is not set in the `MPTokenIssuance` object. (the asset is not transferable).
+ 3. The `lsfMPTLocked` flag is set in the `MPTokenIssuance` object. (the asset is locked).
-- The `tfVaultPrivate` flag is not set and the `DomainID` is provided. (The VaultOwner is attempting to create a public Vault with a PermissionedDomain)
+3. The `Asset` is an `IOU`:
+ 1. The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
+ 2. The `Scale` parameter is provided, and is less than **0** or greater than **18**.
-- The `PermissionedDomain` object does not exist with the provided `DomainID`.
+4. The `tfVaultPrivate` flag is not set and the `DomainID` is provided. (The VaultOwner is attempting to create a public Vault with a PermissionedDomain)
-- The `Data` field is larger than 256 bytes.
-- The account submiting the transaction has insufficient `AccountRoot.Balance` for the Owner Reserve.
+5. The `PermissionedDomain` object does not exist with the provided `DomainID`.
+6. The `Data` field is larger than 256 bytes.
+7. The account submiting the transaction has insufficient `AccountRoot.Balance` for the Owner Reserve.
-##### 3.1.1.5 State Changes
+### 3.6 State Changes
-- Create a new `Vault` ledger object.
-- Create a new `MPTokenIssuance` ledger object for the vault shares.
- - If the `DomainID` is provided:
- - `MPTokenIssuance(Vault.ShareMPTID).DomainID = DomainID` (Set the Permissioned Domain ID).
- - Create an `MPToken` object for the Vault Owner to hold Vault Shares.
-- Create a new `AccountRoot`[_pseudo-account_](../XLS-0064-pseudo-account/README.md) object setting the `PseudoOwner` to `VaultID`.
+1. Create a new `Vault` ledger object.
+2. Create a new `MPTokenIssuance` ledger object for the vault shares.
+ 1. If the `DomainID` is provided: `MPTokenIssuance(Vault.ShareMPTID).DomainID = DomainID` (Set the Permissioned Domain ID).
+ 2. Create an `MPToken` object for the Vault Owner to hold Vault Shares.
+3. Create a new `AccountRoot`[_pseudo-account_](../XLS-0064-pseudo-account/README.md) object setting the `PseudoOwner` to `VaultID`.
-- If `Vault.Asset` is an `IOU`:
- - Create a `RippleState` object between the _pseudo-account_ `AccountRoot` and `Issuer` `AccountRoot`.
+4. If `Vault.Asset` is an `IOU`:
+ 1. Create a `RippleState` object between the _pseudo-account_ `AccountRoot` and `Issuer` `AccountRoot`.
-- If `Vault.Asset` is an `MPT`:
- - Create `MPToken` object for the _pseudo-account_ for the `Asset.MPTokenIssuance`.
+5. If `Vault.Asset` is an `MPT`:
+ 1. Create `MPToken` object for the _pseudo-account_ for the `Asset.MPTokenIssuance`.
-##### 3.1.1.6 Invariants
+### 3.7 Invariants
**TBD**
[**Return to Index**](#index)
-#### 3.1.2 `VaultSet` Transaction
+## 4. Transaction: `VaultSet`
The `VaultSet` updates an existing `Vault` ledger object.
-| Field Name | Required? | JSON Type | Internal Type | Default Value | Description |
-| ----------------- | :----------------: | :-------: | :-----------: | :-----------: | :-------------------------------------------------------------------------------------------------------------------------------------- |
-| `TransactionType` | :heavy_check_mark: | `string` | `Uint16` | `59` | The transaction type. |
-| `VaultID` | :heavy_check_mark: | `string` | `Hash256` | `N/A` | The ID of the Vault to be modified. Must be included when updating the Vault. |
-| `Data` | | `string` | `Blob` | | Arbitrary Vault metadata, limited to 256 bytes. |
-| `AssetsMaximum` | | `number` | `Number` | | The maximum asset amount that can be held in a vault. The value cannot be lower than the current `AssetsTotal` unless the value is `0`. |
-| `DomainID` | | `string` | `Hash256` | | The `PermissionedDomain` object ID associated with the shares of this Vault. |
+### 4.1 Fields
-##### 3.1.2.1 Failure Conditions
+| Field Name | Required | JSON Type | Internal Type | Default Value | Description |
+| ----------------- | :------: | :-------: | :-----------: | :-----------: | :-------------------------------------------------------------------------------------------------------------------------------------- |
+| `TransactionType` | Yes | `string` | `Uint16` | `59` | The transaction type. |
+| `VaultID` | Yes | `string` | `Hash256` | `N/A` | The ID of the Vault to be modified. Must be included when updating the Vault. |
+| `Data` | No | `string` | `Blob` | | Arbitrary Vault metadata, limited to 256 bytes. |
+| `AssetsMaximum` | No | `number` | `Number` | | The maximum asset amount that can be held in a vault. The value cannot be lower than the current `AssetsTotal` unless the value is `0`. |
+| `DomainID` | No | `string` | `Hash256` | | The `PermissionedDomain` object ID associated with the shares of this Vault. |
-- `Vault` object with the specified `VaultID` does not exist on the ledger.
-- The submitting account is not the `Owner` of the vault.
-- The `Data` field is larger than 256 bytes.
-- If `Vault.AssetsMaximum` > `0` AND `AssetsMaximum` > 0 AND:
- - The `AssetsMaximum` < `Vault.AssetsTotal` (new `AssetsMaximum` cannot be lower than the current `AssetsTotal`).
-- The `sfVaultPrivate` flag is not set and the `DomainID` is provided (Vault Owner is attempting to set a PermissionedDomain to a public Vault).
-- The `PermissionedDomain` object does not exist with the provided `DomainID`.
-- The transaction is attempting to modify an immutable field.
-- The transaction does not specify any of the modifiable fields.
+### 4.2 Failure Conditions
-##### 3.1.2.2 State Changes
+1. `Vault` object with the specified `VaultID` does not exist on the ledger.
+2. The submitting account is not the `Owner` of the vault.
+3. The `Data` field is larger than 256 bytes.
+4. If `Vault.AssetsMaximum` > `0` AND `AssetsMaximum` > 0 AND:
+ 1. The `AssetsMaximum` < `Vault.AssetsTotal` (new `AssetsMaximum` cannot be lower than the current `AssetsTotal`).
+5. The `sfVaultPrivate` flag is not set and the `DomainID` is provided (Vault Owner is attempting to set a PermissionedDomain to a public Vault).
+6. The `PermissionedDomain` object does not exist with the provided `DomainID`.
+7. The transaction is attempting to modify an immutable field.
+8. The transaction does not specify any of the modifiable fields.
-- Update mutable fields in the `Vault` ledger object.
-- If `DomainID` is provided:
- - Set `MPTokenIssuance(Vault.ShareMPTID).DomainID = DomainID` (Set the Permissioned Domain).
+### 4.3 State Changes
-##### 3.1.2.3 Invariants
+1. Update mutable fields in the `Vault` ledger object.
+2. If `DomainID` is provided:
+ 1. Set `MPTokenIssuance(Vault.ShareMPTID).DomainID = DomainID` (Set the Permissioned Domain).
+
+### 4.4 Invariants
**TBD**
[**Return to Index**](#index)
-#### 3.1.3 `VaultDelete` Transaction
+## 5. Transaction: `VaultDelete`
The `VaultDelete` transaction deletes an existing vault object.
-| Field Name | Required? | JSON Type | Internal Type | Default Value | Description |
-| ----------------- | :----------------: | :-------: | :-----------: | :-----------: | :--------------------------------: |
-| `TransactionType` | :heavy_check_mark: | `string` | `Uint16` | `60` | Transaction type. |
-| `VaultID` | :heavy_check_mark: | `string` | `Hash256` | `N/A` | The ID of the vault to be deleted. |
+### 5.1 Fields
-##### 3.1.3.1 Failure Conditions
+| Field Name | Required? | JSON Type | Internal Type | Default Value | Description |
+| ----------------- | :-------: | :-------: | :-----------: | :-----------: | :-------------------------------------------------------: |
+| `TransactionType` | Yes | `string` | `Uint16` | `60` | Transaction type. |
+| `VaultID` | Yes | `string` | `Hash256` | `N/A` | The ID of the vault to be deleted. |
+| `Data` | No | `string` | `BLOB` | Empty | Arbitrary metadata about the Vault. Limited to 256 bytes. |
-- `Vault` object with the `VaultID` does not exist on the ledger.
-- The submitting account is not the `Owner` of the vault.
-- `AssetsTotal`, `AssetsAvailable`, or `MPTokenIssuance(Vault.ShareMPTID).OutstandingAmount` are greater than zero.
-- The `OwnerDirectory` of the Vault _pseudo-account_ contains pointers to objects other than the `Vault`, the `MPTokenIssuance` for its shares, or an `MPToken` or trust line for its asset.
+### 5.2 Failure Conditions
-##### 3.1.3.2 State Changes
+#### 5.2.1. Data Verification
-- Delete the `MPTokenIssuance` object for the vault shares.
-- Delete the `MPToken` or `RippleState` object corresponding to the vault's holding of the asset, if one exists.
-- Delete the `AccountRoot` object of the _pseudo-account_, and its `DirectoryNode` objects.
-- Release the Owner Reserve to the `Vault.Owner` account.
-- Delete the `Vault` object.
+1. `VaultID` is empty. (`temMALFORMED`)
+2. `Data` field is provided and `fixLendingProtocolV1_1` amendment is not enabled. (`temDISABLED`)
+3. `Data` field is greater than 256 bytes. (`temMALFORMED`)
-##### 3.1.3.3 Invariants
+#### 5.2.2. Protocol-Level Failures
-**TBD**
+1. `Vault` object with the `VaultID` does not exist on the ledger. (`tecNO_ENTRY`)
+2. The submitting account is not the `Owner` of the vault. (`tecNO_PERMISSION`)
+3. `AssetsTotal`, `AssetsAvailable`, or `MPTokenIssuance(Vault.ShareMPTID).OutstandingAmount` are greater than zero. (`tecHAS_OBLIGATIONS`)
+4. The `OwnerDirectory` of the Vault _pseudo-account_ contains objects other than the `Vault`, the `MPTokenIssuance` for its shares, or an `MPToken` or trust line for its asset. (`tecHAS_OBLIGATIONS`)
-[**Return to Index**](#index)
+### 5.3 State Changes
-### 3.2 `VaultDeposit` and `VaultWithdraw` transactions
+1. Delete the `MPTokenIssuance` object for the vault shares.
+2. Delete the `MPToken` or `RippleState` object corresponding to the vault's holding of the asset, if one exists.
+3. Delete the `AccountRoot` object of the _pseudo-account_, and its `DirectoryNode` objects.
+4. Release the Owner Reserve to the `Vault.Owner` account.
+5. Delete the `Vault` object.
-Depositors call the `VaultDeposit` and `VaultWithdraw` transactions to add or remove assets from the Tokenized Vault.
+### 5.4 Invariants
+
+**TBD**
[**Return to Index**](#index)
-#### 3.2.1 `VaultDeposit` transaction
+## 6. Transaction: `VaultDeposit`
The `VaultDeposit` transaction adds Liqudity in exchange for vault shares.
-| Field Name | Required? | JSON Type | Internal Type | Default Value | Description |
-| ----------------- | :----------------: | :------------------: | :-----------: | :-----------: | :----------------------------------------------------- |
-| `TransactionType` | :heavy_check_mark: | `string` | `UINT16` | `61` | Transaction type. |
-| `VaultID` | :heavy_check_mark: | `string` | `HASH256` | `N/A` | The ID of the vault to which the assets are deposited. |
-| `Amount` | :heavy_check_mark: | `string` or `object` | `STAmount` | `N/A` | Asset amount to deposit. |
+### 6.1 Fields
-##### 3.2.1.1 Failure conditions
+| Field Name | Required | JSON Type | Internal Type | Default Value | Description |
+| ----------------- | :------: | :------------------: | :-----------: | :-----------: | :----------------------------------------------------- |
+| `TransactionType` | Yes | `string` | `UINT16` | `61` | Transaction type. |
+| `VaultID` | Yes | `string` | `HASH256` | `N/A` | The ID of the vault to which the assets are deposited. |
+| `Amount` | Yes | `string` or `object` | `STAmount` | `N/A` | Asset amount to deposit. |
-- `Vault` object with the `VaultID` does not exist on the ledger.
-- The asset type of the vault does not match the asset type the depositor is depositing.
-- The depositor does not have sufficient funds to make a deposit.
-- Adding the `Amount` to the `AssetsTotal` of the vault would exceed the `AssetsMaximum`.
-- The `Vault` `lsfVaultPrivate` flag is set and the `Account` depositing the assets does not have credentials in the permissioned domain of the share.
+### 6.2 Flags
-- The `Vault.Asset` is `MPT`:
- - `MPTokenIssuance.lsfMPTCanTransfer` is not set (the asset is not transferable).
- - `MPTokenIssuance.lsfMPTLocked` flag is set (the asset is globally locked).
- - `MPToken(MPTokenIssuanceID, AccountID).lsfMPTLocked` flag is set (the asset is locked for the depositor).
- - `MPToken(MPTokenIssuanceID, AccountID).MPTAmount` < `Amount` (insufficient balance).
+| Flag Name | Flag Value | Description |
+| --------------- | :----------: | :------------------------------------------------------------------ |
+| `tfVaultDonate` | `0x00010000` | Indicates that the deposit is a donation, and no shares are issued. |
-- The `Asset` is an `IOU`:
- - The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
- - The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the depositor.
- - The `RippleState` object `Balance` < `Amount` (insufficient balance).
+### 6.3 Failure conditions
-##### 3.2.1.2 State Changes
+1. `Vault` object with the `VaultID` does not exist on the ledger.
+2. The asset type of the vault does not match the asset type the depositor is depositing.
+3. The depositor does not have sufficient funds to make a deposit.
+4. Adding the `Amount` to the `AssetsTotal` of the vault would exceed the `AssetsMaximum`.
+5. The `Vault` `lsfVaultPrivate` flag is set and the `Account` depositing the assets does not have credentials in the permissioned domain of the share.
+6. `tfVaultDonate` flag is set and:
+ 1. The Account submitting the transaction is not the owner of the vault (`tecNO_PERMISSION`).
+ 2. The Vault is empty: `Vault.AssetsTotal == 0 and Vault.SharesTotal == 0` (`tecINSUFFICIENT_FUNDS`).
+ 3. Donation would exceed the `AssetsMaximum`: `Vault.AssetsTotal + Amount > Vault.AssetsMaximum` (`tecLIMIT_EXCEEDED`).
-If no `MPToken` object exists for the depositor, create one. For object details, see [2.1.6.2 `MPToken`](#2162-mptoken).
+7. The `Vault.Asset` is `MPT`:
+ 1. `MPTokenIssuance.lsfMPTCanTransfer` is not set (the asset is not transferable).
+ 2. `MPTokenIssuance.lsfMPTLocked` flag is set (the asset is globally locked).
+ 3. `MPToken(MPTokenIssuanceID, AccountID).lsfMPTLocked` flag is set (the asset is locked for the depositor).
+ 4. `MPToken(MPTokenIssuanceID, AccountID).MPTAmount` < `Amount` (insufficient balance).
-- Increase the `MPTAmount` field of the share `MPToken` object of the `Account` by $\Delta_{share}$.
-- Increase the `OutstandingAmount` field of the share `MPTokenIssuance` object by $\Delta_{share}$.
-- Increase the `AssetsTotal` and `AssetsAvailable` of the `Vault` by `Amount`.
+8. The `Asset` is an `IOU`:
+ 1. The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
+ 2. The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the depositor.
+ 3. The `RippleState` object `Balance` < `Amount` (insufficient balance).
-- If the `Vault.Asset` is `XRP`:
- - Increase the `Balance` field of _pseudo-account_ `AccountRoot` by `Amount`.
- - Decrease the `Balance` field of the depositor `AccountRoot` by `Amount`.
+### 6.4 State Changes
-- If the `Vault.Asset` is an `IOU`:
- - Increase the `RippleState` balance between the _pseudo-account_ `AccountRoot` and the `Issuer` `AccountRoot` by `Amount`.
- - Decrease the `RippleState` balance between the depositor `AccountRoot` and the `Issuer` `AccountRoot` by `Amount`.
+1. If no `MPToken` object exists for the depositor, create one. For object details, see [2.1.6.2 `MPToken`](#2162-mptoken).
-- If the `Vault.Asset` is an `MPT`:
- - Increase the `MPToken.MPTAmount` by `Amount` of the _pseudo-account_ `MPToken` object for the `Vault.Asset`.
- - Decrease the `MPToken.MPTAmount` by `Amount` of the depositor `MPToken` object for the `Vault.Asset`.
+2. If the `tfVaultDonate` flag is not set:
+ 1. Increase the `MPTAmount` field of the share `MPToken` object of the `Account` by $\Delta_{share}$.
+ 2. Increase the `OutstandingAmount` field of the share `MPTokenIssuance` object by $\Delta_{share}$.
-##### 3.2.1.3 Invariants
+3. Increase the `AssetsTotal` and `AssetsAvailable` of the `Vault` by `Amount`.
+
+4. If the `Vault.Asset` is `XRP`:
+ 1. Increase the `Balance` field of _pseudo-account_ `AccountRoot` by `Amount`.
+ 2. Decrease the `Balance` field of the depositor `AccountRoot` by `Amount`.
+
+5. If the `Vault.Asset` is an `IOU`:
+ 1. Increase the `RippleState` balance between the _pseudo-account_ `AccountRoot` and the `Issuer` `AccountRoot` by `Amount`.
+ 2. Decrease the `RippleState` balance between the depositor `AccountRoot` and the `Issuer` `AccountRoot` by `Amount`.
+
+6. If the `Vault.Asset` is an `MPT`:
+ 1. Increase the `MPToken.MPTAmount` by `Amount` of the _pseudo-account_ `MPToken` object for the `Vault.Asset`.
+ 2. Decrease the `MPToken.MPTAmount` by `Amount` of the depositor `MPToken` object for the `Vault.Asset`.
+
+### 6.4 Invariants
**TBD**
[**Return to Index**](#index)
-#### 3.2.2 `VaultWithdraw` transaction
+## 7. Transaction: `VaultWithdraw`
The `VaultWithdraw` transaction withdraws assets in exchange for the vault's shares.
-| Field Name | Required? | JSON Type | Internal Type | Default Value | Description |
-| ----------------- | :----------------: | :-------: | :-----------: | :-----------: | :-------------------------------------------------------------------------- |
-| `TransactionType` | :heavy_check_mark: | `string` | `UINT16` | `62` | Transaction type. |
-| `VaultID` | :heavy_check_mark: | `string` | `HASH256` | `N/A` | The ID of the vault from which assets are withdrawn. |
-| `Amount` | :heavy_check_mark: | `number` | `STAmount` | 0 | The exact amount of Vault asset to withdraw. |
-| `Destination` | | `string` | `AccountID` | Empty | An account to receive the assets. It must be able to receive the asset. |
-| `DestinationTag` | | `number` | `UINT32` | Empty | Arbitrary tag identifying the reason for the withdrawal to the destination. |
+### 7.1 Fields
+
+| Field Name | Required | JSON Type | Internal Type | Default Value | Description |
+| ----------------- | :------: | :-------: | :-----------: | :-----------: | :-------------------------------------------------------------------------- |
+| `TransactionType` | Yes | `string` | `UINT16` | `62` | Transaction type. |
+| `VaultID` | Yes | `string` | `HASH256` | `N/A` | The ID of the vault from which assets are withdrawn. |
+| `Amount` | Yes | `number` | `STAmount` | 0 | The exact amount of Vault asset to withdraw. |
+| `Destination` | No | `string` | `AccountID` | Empty | An account to receive the assets. It must be able to receive the asset. |
+| `DestinationTag` | No | `number` | `UINT32` | Empty | Arbitrary tag identifying the reason for the withdrawal to the destination. |
- If `Amount` is the Vaults asset, calculate the share cost using the [**Withdraw formula**](#21723-withdraw).
- If `Amount` is the Vaults share, calculate the assets amount using the [**Redeem formula**](#21722-redeem).
@@ -587,204 +598,200 @@ In sections below assume the following variables:
- $\Delta_{asset}$ - the change in the total amount of assets after a deposit, withdrawal, or redemption.
- $\Delta_{share}$ - che change in the total amount of shares after a deposit, withdrawal, or redemption.
-##### 3.2.2.1 Failure conditions
+### 7.2 Failure conditions
-- `Vault` object with the `VaultID` does not exist on the ledger.
+1. `Vault` object with the `VaultID` does not exist on the ledger.
-- The `Vault.Asset` is `MPT`:
- - `MPTokenIssuance.lsfMPTCanTransfer` is not set (the asset is not transferable).
- - `MPTokenIssuance.lsfMPTLocked` flag is set (the asset is globally locked).
- - `MPToken(MPTokenIssuanceID, AccountID | Destination).lsfMPTLocked` flag is set (the asset is locked for the depositor or the destination).
+2. The `Vault.Asset` is `MPT`:
+ 1. `MPTokenIssuance.lsfMPTCanTransfer` is not set (the asset is not transferable).
+ 2. `MPTokenIssuance.lsfMPTLocked` flag is set (the asset is globally locked).
+ 3. `MPToken(MPTokenIssuanceID, AccountID | Destination).lsfMPTLocked` flag is set (the asset is locked for the depositor or the destination).
-- The `Asset` is an `IOU`:
- - The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
- - The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the `AccountRoot` of the `AccountID` or the `Destination`.
+3. The `Asset` is an `IOU`:
+ 1. The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
+ 2. The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the `AccountRoot` of the `AccountID` or the `Destination`.
-- The unit of `Amount` is not shares of the vault.
-- The unit of `Amount` is not asset of the vault.
+4. The unit of `Amount` is not shares of the vault.
+5. The unit of `Amount` is not asset of the vault.
-- There is insufficient liquidity in the vault to fill the request:
- - If `Amount` is the vaults share:
- - `MPTokenIssuance(Vault.ShareMPTID).OutstandingAmount` < `Amount` (attempt to withdraw more shares than there are in total).
- - The shares `MPToken.MPTAmount` of the `Account` is less than `Amount` (attempt to withdraw more shares than owned).
- - `Vault.AssetsAvailable` < $\Delta_{asset}$ (the vault has insufficient assets).
+6. There is insufficient liquidity in the vault to fill the request:
+ 1. If `Amount` is the vaults share:
+ 1. `MPTokenIssuance(Vault.ShareMPTID).OutstandingAmount` < `Amount` (attempt to withdraw more shares than there are in total).
+ 2. The shares `MPToken(MPTokenIssuanceID, AccountID | Destination).MPTAmount` of the `Account` is less than `Amount` (attempt to withdraw more shares than owned).
+ 3. `Vault.AssetsAvailable` < $\Delta_{asset}$ (the vault has insufficient assets).
- - If `Amount` is the vaults asset:
- - The shares `MPToken.MPTAmount` of the `Account` is less than $\Delta_{share}$ (attempt to withdraw more shares than owned).
- - `Vault.AssetsAvailable` < `Amount` (the vault has insufficient assets).
+7. If `Amount` is the vaults asset:
+ 1. The shares `MPToken.MPTAmount` of the `Account` is less than $\Delta_{share}$ (attempt to withdraw more shares than owned).
+ 2. `Vault.AssetsAvailable` < `Amount` (the vault has insufficient assets).
-- The `Destination` account is specified:
- - The account does not have permission to receive the asset.
- - The account does not have a `RippleState` or `MPToken` object for the asset.
+8. The `Destination` account is specified:
+ 1. The account does not have permission to receive the asset.
+ 2. The account does not have a `RippleState` or `MPToken` object for the asset.
-##### 3.2.2.2 State Changes
+### 7.3 State Changes
-- If the `Vault.Asset` is XRP:
- - Decrease the `Balance` field of _pseudo-account_ `AccountRoot` by $\Delta_{asset}$.
- - Increase the `Balance` field of the depositor `AccountRoot` by $\Delta_{asset}$.
+1. If the `Vault.Asset` is XRP:
+ 1. Decrease the `Balance` field of _pseudo-account_ `AccountRoot` by $\Delta_{asset}$.
+ 2. Increase the `Balance` field of the depositor `AccountRoot` by $\Delta_{asset}$.
-- If the `Vault.Asset` is an `IOU`:
- - If the Depositor account does not have a `RippleState` object for the Vaults Asset, create the `RippleState` object.
+2. If the `Vault.Asset` is an `IOU`:
+ 1. If the Depositor account does not have a `RippleState` object for the Vaults Asset, create the `RippleState` object.
+ 2. Decrease the `RippleState` balance between the _pseudo-account_ `AccountRoot` and the `Issuer` `AccountRoot` by $\Delta_{asset}$.
+ 3. Increase the `RippleState` balance between the depositor `AccountRoot` and the `Issuer` `AccountRoot` by $\Delta_{asset}$.
- - Decrease the `RippleState` balance between the _pseudo-account_ `AccountRoot` and the `Issuer` `AccountRoot` by $\Delta_{asset}$.
- - Increase the `RippleState` balance between the depositor `AccountRoot` and the `Issuer` `AccountRoot` by $\Delta_{asset}$.
+3. If the `Vault.Asset` is an `MPT`:
+ 1. If the Depositor account does not have a `MPToken` object for the Vaults Asset, create the `MPToken` object.
+ 2. Decrease the `MPToken.MPTAmount` by $\Delta_{asset}$ of the _pseudo-account_ `MPToken` object for the `Vault.Asset`.
+ 3. Increase the `MPToken.MPTAmount` by $\Delta_{asset}$ of the depositor `MPToken` object for the `Vault.Asset`.
-- If the `Vault.Asset` is an `MPT`:
- - If the Depositor account does not have a `MPToken` object for the Vaults Asset, create the `MPToken` object.
+4. Update the `MPToken` object for the `Vault.ShareMPTID` of the depositor `AccountRoot`:
+ 1. Decrease the `MPToken.MPTAmount` by $\Delta_{share}$.
+ 2. If `MPToken.MPTAmount == 0`, delete the object.
- - Decrease the `MPToken.MPTAmount` by $\Delta_{asset}$ of the _pseudo-account_ `MPToken` object for the `Vault.Asset`.
- - Increase the `MPToken.MPTAmount` by $\Delta_{asset}$ of the depositor `MPToken` object for the `Vault.Asset`.
+5. Update the `MPTokenIssuance` object for the `Vault.ShareMPTID`:
+ 1. Decrease the `OutstandingAmount` field of the share `MPTokenIssuance` object by $\Delta_{share}$.
-- Update the `MPToken` object for the `Vault.ShareMPTID` of the depositor `AccountRoot`:
- - Decrease the `MPToken.MPTAmount` by $\Delta_{share}$.
- - If `MPToken.MPTAmount == 0`, delete the object.
+6. Decrease the `AssetsTotal` and `AssetsAvailable` by $\Delta_{asset}$
-- Update the `MPTokenIssuance` object for the `Vault.ShareMPTID`:
- - Decrease the `OutstandingAmount` field of the share `MPTokenIssuance` object by $\Delta_{share}$.
-
-- Decrease the `AssetsTotal` and `AssetsAvailable` by $\Delta_{asset}$
-
-##### 3.2.2.3 Invariants
+### 7.4 Invariants
**TBD**
[**Return to Index**](#index)
-## 3.3 VaultClawback Transaction
-
-#### 3.3.1 `VaultClawback` Transaction
+## 8. Transaction: `VaultClawback`
The `VaultClawback` transaction performs a Clawback from the Vault, exchanging the shares of an account. Conceptually, the transaction performs `VaultWithdraw` on behalf of the `Holder`, sending the funds to the `Issuer` account of the asset. In case there are insufficient funds for the entire `Amount` the transaction will perform a partial Clawback, up to the `Vault.AssetsAvailable`. The Clawback transaction must respect any future fees or penalties.
-| Field Name | Required? | JSON Type | Internal Type | Default Value | Description |
-| ----------------- | :----------------: | :-------: | :-----------: | :-----------: | :------------------------------------------------------------------------------------------------------------- |
-| `TransactionType` | :heavy_check_mark: | `string` | `UINT16` | `63` | Transaction type. |
-| `VaultID` | :heavy_check_mark: | `string` | `HASH256` | `N/A` | The ID of the vault from which assets are withdrawn. |
-| `Holder` | :heavy_check_mark: | `string` | `AccountID` | `N/A` | The account ID from which to clawback the assets. |
-| `Amount` | | `number` | `NUMBER` | 0 | The asset amount to clawback. When Amount is `0` clawback all funds, up to the total shares the `Holder` owns. |
+### 8.1 Fields
-##### 3.3.1.1 Failure conditions
+| Field Name | Required | JSON Type | Internal Type | Default Value | Description |
+| ----------------- | :------: | :-------: | :-----------: | :-----------: | :------------------------------------------------------------------------------------------------------------- |
+| `TransactionType` | Yes | `string` | `UINT16` | `63` | Transaction type. |
+| `VaultID` | Yes | `string` | `HASH256` | `N/A` | The ID of the vault from which assets are withdrawn. |
+| `Holder` | Yes | `string` | `AccountID` | `N/A` | The account ID from which to clawback the assets. |
+| `Amount` | No | `number` | `NUMBER` | 0 | The asset amount to clawback. When Amount is `0` clawback all funds, up to the total shares the `Holder` owns. |
-- `Vault` object with the `VaultID` does not exist on the ledger.
+### 8.2 Failure conditions
-- If `Vault.Asset` is `XRP`.
+1. `Vault` object with the `VaultID` does not exist on the ledger.
-- If `Vault.Asset` is an `IOU` and:
- - The `Issuer` account is not the submitter of the transaction.
- - If the `AccountRoot(Issuer)` object does not have `lsfAllowTrustLineClawback` flag set (the asset does not support clawback).
- - If the `AccountRoot(Issuer)` has the `lsfNoFreeze` flag set (the asset cannot be frozen).
+2. If `Vault.Asset` is `XRP`.
-- If `Vault.Asset` is an `MPT` and:
- - `MPTokenIssuance.Issuer` is not the submitter of the transaction.
- - `MPTokenIssuance.lsfMPTCanClawback` flag is not set (the asset does not support clawback).
- - If the `MPTokenIssuance.lsfMPTCanLock` flag is NOT set (the asset cannot be locked).
+3. If `Vault.Asset` is an `IOU` and:
+ 1. The `Issuer` account is not the submitter of the transaction.
+ 2. If the `AccountRoot(Issuer)` object does not have `lsfAllowTrustLineClawback` flag set (the asset does not support clawback).
+ 3. If the `AccountRoot(Issuer)` has the `lsfNoFreeze` flag set (the asset cannot be frozen).
-- The `MPToken` object for the `Vault.ShareMPTID` of the `Holder` `AccountRoot` does not exist OR `MPToken.MPTAmount == 0`.
+4. If `Vault.Asset` is an `MPT` and:
+ 1. `MPTokenIssuance.Issuer` is not the submitter of the transaction.
+ 2. `MPTokenIssuance.lsfMPTCanClawback` flag is not set (the asset does not support clawback).
+ 3. If the `MPTokenIssuance.lsfMPTCanLock` flag is NOT set (the asset cannot be locked).
-##### 3.3.1.2 State Changes
+5. The `MPToken` object for the `Vault.ShareMPTID` of the `Holder` `AccountRoot` does not exist OR `MPToken.MPTAmount == 0`.
-- If the `Vault.Asset` is an `IOU`:
- - Decrease the `RippleState` balance between the _pseudo-account_ `AccountRoot` and the `Issuer` `AccountRoot` by `min(Vault.AssetsAvailable`, $\Delta_{asset}$`)`.
+### 8.3 State Changes
-- If the `Vault.Asset` is an `MPT`:
- - Decrease the `MPToken.MPTAmount` by `min(Vault.AssetsAvailable`, $\Delta_{asset}$`)` of the _pseudo-account_ `MPToken` object for the `Vault.Asset`.
+1. If the `Vault.Asset` is an `IOU`:
+ 1. Decrease the `RippleState` balance between the _pseudo-account_ `AccountRoot` and the `Issuer` `AccountRoot` by `min(Vault.AssetsAvailable`, $\Delta_{asset}$`)`.
-- Update the `MPToken` object for the `Vault.ShareMPTID` of the depositor `AccountRoot`:
- - Decrease the `MPToken.MPTAmount` by $\Delta_{share}$.
- - If `MPToken.MPTAmount == 0`, delete the object.
+2. If the `Vault.Asset` is an `MPT`:
+ 1. Decrease the `MPToken.MPTAmount` by `min(Vault.AssetsAvailable`, $\Delta_{asset}$`)` of the _pseudo-account_ `MPToken` object for the `Vault.Asset`.
-- Update the `MPTokenIssuance` object for the `Vault.ShareMPTID`:
- - Decrease the `OutstandingAmount` field of the share `MPTokenIssuance` object by $\Delta_{share}$.
+3. Update the `MPToken` object for the `Vault.ShareMPTID` of the depositor `AccountRoot`:
+ 1. Decrease the `MPToken.MPTAmount` by $\Delta_{share}$.
+ 2. If `MPToken.MPTAmount == 0`, delete the object.
-- Decrease the `AssetsTotal` and `AssetsAvailable` by `min(Vault.AssetsAvailable`, $\Delta_{asset}$`)`
+4. Update the `MPTokenIssuance` object for the `Vault.ShareMPTID`:
+ 1. Decrease the `OutstandingAmount` field of the share `MPTokenIssuance` object by $\Delta_{share}$.
-##### 3.3.1.3 Invariants
+5. Decrease the `AssetsTotal` and `AssetsAvailable` by `min(Vault.AssetsAvailable`, $\Delta_{asset}$`)`
+
+### 8.4 Invariants
**TBD**
[**Return to Index**](#index)
-## 3.4 Payment Transaction
-
-### 3.4.1 `Payment` transaction
+## 9. Transaction: `Payment`
The Single Asset Vault does not introduce new `Payment` transaction fields. However, it adds additional failure conditions and state changes when transfering Vault shares.
-#### 3.4.1.1 Failure Conditions
+### 9.1 Failure Conditions
-- If `Payment.Amount` is a `Vault` share AND:
- - The `Vault` `lsfVaultPrivate` flag is set and the `Payment.Destination` account does not have credentials in the permissioned domain of the Vaults Share.
- - The `Vault` `tfVaultShareNonTransferable` flag is set.
+1. If `Payment.Amount` is a `Vault` share AND:
+ 1. The `Vault` `lsfVaultPrivate` flag is set and the `Payment.Destination` account does not have credentials in the permissioned domain of the Vaults Share.
+ 2. The `Vault` `tfVaultShareNonTransferable` flag is set.
- - The `Vault.Asset` is `MPT`:
- - `MPTokenIssuance.lsfMPTCanTransfer` is not set (the asset is not transferable).
- - `MPTokenIssuance.lsfMPTLocked` flag is set (the asset is globally locked).
- - `MPToken(MPTokenIssuanceID, AccountID).lsfMPTLocked` flag is set (the asset is locked for the payer).
- - `MPToken(MPTokenIssuanceID, PseudoAccountID).lsfMPTLocked` flag is set (the asset is locked for the `pseudo-account`).
- - `MPToken(MPTokenIssuanceID, Destination).lsfMPTLocked` flag is set (the asset is locked for the destination account).
+ 3. The `Vault.Asset` is `MPT`:
+ 1. `MPTokenIssuance.lsfMPTCanTransfer` is not set (the asset is not transferable).
+ 2. `MPTokenIssuance.lsfMPTLocked` flag is set (the asset is globally locked).
+ 3. `MPToken(MPTokenIssuanceID, AccountID).lsfMPTLocked` flag is set (the asset is locked for the payer).
+ 4. `MPToken(MPTokenIssuanceID, PseudoAccountID).lsfMPTLocked` flag is set (the asset is locked for the `pseudo-account`).
+ 5. `MPToken(MPTokenIssuanceID, Destination).lsfMPTLocked` flag is set (the asset is locked for the destination account).
- - The `Vault.Asset` is an `IOU`:
- - The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
- - The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the payer account.
- - The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the destination account.
- - The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the `pseudo-account`.
+ 4. The `Vault.Asset` is an `IOU`:
+ 1. The `lsfGlobalFreeze` flag is set on the issuing account (the asset is frozen).
+ 2. The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the payer account.
+ 3. The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the destination account.
+ 4. The `lsfHighFreeze` or `lsfLowFreeze` flag is set on the `RippleState` object between the Asset `Issuer` and the `pseudo-account`.
-#### 3.4.1.2 State Changes
+### 9.2 State Changes
-- If `MPToken`object for shares does not exist for the destination account, create one.
+1. If `MPToken`object for shares does not exist for the destination account, create one.
[**Return to Index**](#index)
-## 4. API
+## 10. API
-### 4.1 RPC `vault_info`
+### 10.1 RPC `vault_info`
This RPC retrieves the Vault ledger entry and the IDs associated with it.
-#### 4.1.1 Request Fields
+#### 10.1.1 Request Fields
We propose adding the following fields to the `ledger_entry` method:
-| Field Name | Required? | JSON Type | Description |
-| ---------- | :----------------: | :-------: | :----------------------------------------: |
-| `vault` | :heavy_check_mark: | `string` | The object ID of the Vault to be returned. |
-
-#### 4.1.2 Response
-
-| Field Name | Required? | JSON Type | Description |
-| -------------------------------- | --------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `vault` | `yes` | `object` | Root object representing the vault. |
-| `vault.Account` | `yes` | `string` | The pseudo-account ID of the vault. |
-| `vault.Asset` | `yes` | `object` | Object representing the asset held in the vault. |
-| `vault.Asset.currency` | `yes` | `string` | Currency code of the asset stored in the vault. |
-| `vault.Asset.issuer` | `no` | `string` | Issuer address of the asset. |
-| `vault.AssetsAvailable` | `yes` | `string` | Amount of assets currently available for withdrawal. |
-| `vault.AssetsTotal` | `yes` | `string` | Total amount of assets in the vault. |
-| `vault.Flags` | `no` | `number` | Bit-field flags associated with the vault. |
-| `vault.LedgerEntryType` | `yes` | `string` | Ledger entry type, always "Vault". |
-| `vault.LossUnrealized` | `no` | `string` | Unrealized loss associated with the vault. |
-| `vault.Owner` | `yes` | `string` | ID of the Vault Owner account. |
-| `vault.OwnerNode` | `no` | `string` | Identifier for the owner node in the ledger tree. |
-| `vault.PreviousTxnID` | `yes` | `string` | Transaction ID of the last modification to this vault. |
-| `vault.PreviousTxnLgrSeq` | `yes` | `number` | Ledger sequence number of the last transaction modifying this vault. |
-| `vault.Sequence` | `yes` | `number` | Sequence number of the vault entry. |
-| `vault.ShareMPTID` | `no` | `string` | Multi-purpose token ID associated with this vault. |
-| `vault.WithdrawalPolicy` | `no` | `number` | Policy defining withdrawal conditions. |
-| `vault.index` | `yes` | `string` | Unique index of the vault ledger entry. |
-| `vault.shares` | `yes` | `object` | Object containing details about issued shares. |
-| `vault.shares.Flags` | `no` | `number` | Bit-field flags associated with the shares issuance. |
-| `vault.shares.Issuer` | `yes` | `string` | The ID of the Issuer of the Share. It will always be the pseudo-account ID. |
-| `vault.shares.LedgerEntryType` | `yes` | `string` | Ledger entry type, always "MPTokenIssuance". |
-| `vault.shares.OutstandingAmount` | `yes` | `string` | Total outstanding shares issued. |
-| `vault.shares.OwnerNode` | `no` | `string` | Identifier for the owner node of the shares. |
-| `vault.shares.PreviousTxnID` | `yes` | `string` | Transaction ID of the last modification to the shares issuance. |
-| `vault.shares.PreviousTxnLgrSeq` | `yes` | `number` | Ledger sequence number of the last transaction modifying the shares issuance. |
-| `vault.shares.Sequence` | `yes` | `number` | Sequence number of the shares issuance entry. |
-| `vault.shares.index` | `yes` | `string` | Unique index of the shares ledger entry. |
-| `vault.shares.mpt_issuance_id` | `no` | `string` | The ID of the `MPTokenIssuance` object. It will always be equal to `vault.ShareMPTID`. |
-| `vault.Scale` | `yes` | `number` | 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. |
-
-#### 4.1.2.1 Example
+| Field Name | Required | JSON Type | Description |
+| ---------- | :------: | :-------: | :----------------------------------------: |
+| `vault` | Yes | `string` | The object ID of the Vault to be returned. |
+
+#### 10.1.2 Response
+
+| Field Name | Required | JSON Type | Description |
+| -------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `vault` | `yes` | `object` | Root object representing the vault. |
+| `vault.Account` | `yes` | `string` | The pseudo-account ID of the vault. |
+| `vault.Asset` | `yes` | `object` | Object representing the asset held in the vault. |
+| `vault.Asset.currency` | `yes` | `string` | Currency code of the asset stored in the vault. |
+| `vault.Asset.issuer` | `no` | `string` | Issuer address of the asset. |
+| `vault.AssetsAvailable` | `yes` | `string` | Amount of assets currently available for withdrawal. |
+| `vault.AssetsTotal` | `yes` | `string` | Total amount of assets in the vault. |
+| `vault.Flags` | `no` | `number` | Bit-field flags associated with the vault. |
+| `vault.LedgerEntryType` | `yes` | `string` | Ledger entry type, always "Vault". |
+| `vault.LossUnrealized` | `no` | `string` | Unrealized loss associated with the vault. |
+| `vault.Owner` | `yes` | `string` | ID of the Vault Owner account. |
+| `vault.OwnerNode` | `no` | `string` | Identifier for the owner node in the ledger tree. |
+| `vault.PreviousTxnID` | `yes` | `string` | Transaction ID of the last modification to this vault. |
+| `vault.PreviousTxnLgrSeq` | `yes` | `number` | Ledger sequence number of the last transaction modifying this vault. |
+| `vault.Sequence` | `yes` | `number` | Sequence number of the vault entry. |
+| `vault.ShareMPTID` | `no` | `string` | Multi-purpose token ID associated with this vault. |
+| `vault.WithdrawalPolicy` | `no` | `number` | Policy defining withdrawal conditions. |
+| `vault.index` | `yes` | `string` | Unique index of the vault ledger entry. |
+| `vault.shares` | `yes` | `object` | Object containing details about issued shares. |
+| `vault.shares.Flags` | `no` | `number` | Bit-field flags associated with the shares issuance. |
+| `vault.shares.Issuer` | `yes` | `string` | The ID of the Issuer of the Share. It will always be the pseudo-account ID. |
+| `vault.shares.LedgerEntryType` | `yes` | `string` | Ledger entry type, always "MPTokenIssuance". |
+| `vault.shares.OutstandingAmount` | `yes` | `string` | Total outstanding shares issued. |
+| `vault.shares.OwnerNode` | `no` | `string` | Identifier for the owner node of the shares. |
+| `vault.shares.PreviousTxnID` | `yes` | `string` | Transaction ID of the last modification to the shares issuance. |
+| `vault.shares.PreviousTxnLgrSeq` | `yes` | `number` | Ledger sequence number of the last transaction modifying the shares issuance. |
+| `vault.shares.Sequence` | `yes` | `number` | Sequence number of the shares issuance entry. |
+| `vault.shares.index` | `yes` | `string` | Unique index of the shares ledger entry. |
+| `vault.shares.mpt_issuance_id` | `no` | `string` | The ID of the `MPTokenIssuance` object. It will always be equal to `vault.ShareMPTID`. |
+| `vault.Scale` | `yes` | `number` | 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. |
+
+##### 10.1.2.1 Example
Vault holding an `IOU`: