From 0bf33e1d171eaca9011fcdb77672e0db42906e42 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 9 Sep 2025 11:45:55 -0400 Subject: [PATCH 01/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- .../README.md | 631 +++++++++++------- 1 file changed, 376 insertions(+), 255 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index b4305d02b..968e1680d 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -1,17 +1,19 @@
-  xls: XLS-68d
+  xls: 68
   title: Sponsored Fees and Reserves
   description: Allow an account to fund fees and reserves on behalf of another account
   author: Mayukha Vadari (@mvadari)
   proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196
   created: 2024-05-02
-  status: Stagnant
+  updated: 2025-09-08
+  status: Draft
   category: Amendment
+  requires: 74
 
# Sponsored Fees and Reserves -## Abstract +## 1. Abstract As the blockchain industry grows, many projects want to be able to build on the blockchain, but abstract away the complexities of using the blockchain - users don't need to submit transactions or deal with transaction fees themselves, they can just pay the platform to handle all that complexity (though, of course, the users still control their own keys). @@ -21,161 +23,205 @@ In order to handle these sorts of use-cases, this proposal adds a process for us Similar features on other chains are often called "sponsored transactions", "meta-transactions", or "relays". -## 1. Overview +## 2. Overview Accounts can include signatures from sponsors in their transactions that will allow the sponsors to pay the transaction fee for the transaction, and/or the reserve for any accounts/objects created in the transaction. -We propose modifying one ledger object and creating one new transaction type: +Sponsors can also pre-fund fees or reserves, if they do not want to deal with the burden of co-signing every sponsored transaction. -- `AccountRoot` ledger object -- `SponsorTransfer` transaction type +We propose: +* Creating the `Sponsorship` ledger entry +* Modifying the `AccountRoot` ledger entry +* Creating the `SponsorshipSet` transaction type +* Creating the `SponsorshipTransfer` transaction type +* Modifying the `AccountDelete` transaction type (behavior only, not fields) +* Adding two additional granular permissions (`SponsorFee`, `SponsorReserve`) The common fields for all ledger objects and all transactions will also be modified. In addition, there will be a modification to the `account_objects` RPC method, and a new RPC method called `account_sponsoring`. -This feature will require an amendment, tentatively titled `featureSponsor`. +This feature will require an amendment, tentatively titled `Sponsor`. -### 1.1. Terminology +### 2.1. Terminology -- **Sponsor**: The account that is covering the reserve or paying the transaction fee on behalf of another account. -- **Sponsee**: The account that the sponsor is paying a transaction fee or reserve on behalf of. -- **Owner**: The account that owns a given object (or the account itself). This is often the same as the sponsee. -- **Sponsored account**: An account that a sponsor is covering the reserve for (currently priced at 10 XRP). -- **Sponsored object**: A non-account ledger object that a sponsor is covering the reserve for (currently priced at 2 XRP). -- **Sponsor relationship**: The relationship between a sponsor and sponsee. -- **Sponsorship type**: The "type" of sponsorship - sponsoring transaction fees vs. sponsoring reserves. +* **Sponsor**: The account that is covering the reserve or paying the transaction fee on behalf of another account. +* **Sponsee**: The account that the sponsor is paying a transaction fee or reserve on behalf of. +* **Owner**: The account that owns a given object (or the account itself). This is often the same as the sponsee. +* **Sponsored account**: An account that a sponsor is covering the reserve for (currently priced at 10 XRP). +* **Sponsored object**: A non-account ledger object that a sponsor is covering the reserve for (currently priced at 2 XRP). +* **Sponsor relationship**: The relationship between a sponsor and sponsee. +* **Sponsorship type**: The "type" of sponsorship - sponsoring transaction fees vs. sponsoring reserves. -### 1.2. The Sponsorship Flow +### 2.2. The Sponsorship Flow (Not Pre-Funded) In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or reserve for the sponsee Alice's transaction. -- Alice constructs her transaction and autofills it (so that all fields, including the fee and sequence number, are included in the transaction). She adds Spencer's account and sponsorship type to the transaction as well. -- Spencer signs the transaction and provides his signature to Alice. -- Alice adds Spencer's public key and signature to her transaction. -- Alice signs and submits her transaction as normal. +* Alice constructs her transaction and autofills it (so that all fields, including the fee and sequence number, are included in the transaction). She adds Spencer's account and sponsorship type to the transaction as well. +* Spencer signs the transaction and provides his signature to Alice. +* Alice adds Spencer's public key and signature to her transaction. +* Alice signs and submits her transaction as normal. -### 1.3. Recouping a Sponsored Object Reserve +### 2.3. The Sponsorship Flow (Pre-Funded) + +In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or reserve for the sponsee Alice's transaction, but would prefer to pre-fund the XRP necessary, so that he does not have to co-sign every single one of Alice's transactions. + +* Spencer submits a transaction to initialize the sponsorship relationship and pre-fund Alice's sponsorship (note: these funds are not sent directly to Alice. She may only use the allocated funds for fees and reserves, and these are separate buckets). + * Alice does not need to do anything to accept this. +* Alice constructs her transaction and autofills it (so that all fields, including the fee and sequence number, are included in the transaction). She adds Spencer's account and sponsorship type to the transaction as well. +* Alice signs and submits her transaction as normal. + +*Note that Spencer does not need to be a part of Alice's signing and submission flow in this example.* + +### 2.4. Recouping a Sponsored Object Reserve In this scenario, the sponsor, Spencer, would like to re-obtain the reserve that is currently trapped due to his sponsorship of Alice's object. -Spencer can submit a `SponsorTransfer` transaction, which allows him to pass the onus of the reserve back to Alice, or pass it onto another sponsor. +Spencer can submit a `SponsorshipTransfer` transaction, which allows him to pass the onus of the reserve back to Alice, or pass it onto another sponsor. -### 1.4. Recouping a Sponsored Account Reserve +### 2.5. Recouping a Sponsored Account Reserve In this scenario, the sponsor, Spencer, would like to retrieve his reserve from sponsoring Alice's account. There are two ways in which he could do this: +* If Alice is done using her account, she can submit an `AccountDelete` transaction, which will send all remaining funds in the account back to Spencer. +* If Alice would like to keep using her account, or would like to switch to a different provider, she (or Spencer) can submit a `SponsorshipTransfer` transaction to either remove sponsorship or transfer it to the new provider. -- If Alice is done using her account, she can submit an `AccountDelete` transaction, which will send all remaining funds in the account back to Spencer. -- If Alice would like to keep using her account, or would like to switch to a different provider, she (or Spencer) can submit a `SponsorTransfer` transaction to either remove sponsorship or transfer it to the new provider. - -## 2. On-Ledger Objects: Common Fields +## 3. Ledger Entries: Common Fields -### 2.1. Fields +### 3.1. Fields As a reference, here are the fields that all ledger objects currently have: -| Field Name | Required? | JSON Type | Internal Type | -| ----------------- | --------- | --------- | ------------- | -| `LedgerIndex` | ✔️ | `string` | `Hash256` | -| `LedgerEntryType` | ✔️ | `string` | `UInt16` | -| `Flags` | ✔️ | `number` | `UInt16` | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-----------|---------------|-------------| +|`LedgerEntryType`| ✔️| ✔️|N/A|`string`|`UInt16`| +|`Flags`| ✔️| ✔️|N/A|`number`|`UInt16`| We propose this additional field: -| Field Name | Required? | JSON Type | Internal Type | -|------------|-----------|-----------|---------------| -|`SponsorAccount`| |`string`|`AccountID`| +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-----------|---------------|-------------| +|`SponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the reserve for this ledger object. | -#### 2.1.1. `SponsorAccount` +## 4. Ledger Entry: `Sponsorship` -The `SponsorAccount` is the sponsor that is paying the reserve for this ledger object. +`Sponsorship` is an object that reflects a sponsoring relationship between two accounts, `SponsorAccount` and `Sponsee`. This allows sponsors to "pre-fund" sponsees, if they so desire. -## 3. On-Ledger Object: `AccountRoot` +*Note: this object does not need to be created in order to sponsor accounts. It is an offered convenience, so that sponsors do not have to co-sign every sponsored transaction if they don't want to, especially for transaction fees. It also allows them to set a maximum balance even if they still want to co-sign transactions.* -### 3.1. Fields +### 4.1. Object ID -
- +The object ID for a `Sponsorship` object will be the space key (defined during implementation), plus the `Sponsor` and `Sponsee` accounts, hashed together. -As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/accountroot/#accountroot-fields) are the fields that the `AccountRoot` ledger object currently has. +### 4.2. Fields - +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-----------|---------------|-------------| +|`SponsorAccount`|✔️ |✔️ | N/A |`string`|`AccountID`|The sponsor associated with this relationship. This account also pays for the reserve of this object.| +|`Sponsee`|✔️ |✔️ | N/A |`string`|`AccountID`|The sponsee associated with this relationship.| +|`SponsorNode`|✔️ |✔️| N/A |`string`|`UInt64`|A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages.| +|`SponseeNode`|✔️ |✔️| N/A |`string`|`UInt64`|A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages.| +|`FeeAmount`| | | `0` |`string`|`Amount`| The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.| +|`ReserveCount`| | | `0` |`string`|`UInt32`| The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves.| -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------------------- | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Account` | ✔️ | `string` | `AccountID` | The identifying (classic) address of this account. | -| `AccountTxnID` | | `string` | `Hash256` | The identifying hash of the transaction most recently sent by this account. | -| `AMMID` | | `string` | `Hash256` | The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account. | -| `Balance` | | `string` | `Amount` | The account's current XRP balance. | -| `BurnedNFTokens` | | `number` | `UInt32` | How many total of this account's issued NFTs have been burned. | -| `Domain` | | `string` | `Blob` | A domain associated with this account. | -| `EmailHash` | | `string` | `Hash128` | The md5 hash of an email address. | -| `FirstNFTokenSequence` | | `number` | `UInt32` | The account's Sequence Number at the time it minted its first non-fungible-token. | -| `LedgerEntryType` | ✔️ | `string` | `UInt16` | The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object. | -| `MessageKey` | | `string` | `Blob` | A public key that may be used to send encrypted messages to this account. | -| `MintedNFTokens` | | `number` | `UInt32` | How many total non-fungible tokens have been minted by/on behalf of this account. | -| `NFTokenMinter` | | `string` | `AccountID` | Another account that can mint NFTs on behalf of this account. | -| `OwnerCount` | ✔️ | `number` | `UInt32` | The number of objects this account owns in the ledger, which contributes to its owner reserve. | -| `PreviousTxnID` | ✔️ | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this object. | -| `PreviousTxnLgrSeq` | ✔️ | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | -| `RegularKey` | | `string` | `AccountID` | The address of a key pair that can be used to sign transactions for this account instead of the master key. | -| `Sequence` | ✔️ | `number` | `UInt32` | The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account. | -| `TicketCount` | | `number` | `UInt32` | How many Tickets this account owns in the ledger. | -| `TickSize` | | `number` | `UInt8` | [How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize) | -| `TransferRate` | | `number` | `UInt32` | A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other. | -| `WalletLocator` | | `string` | `Hash256` | An arbitrary 256-bit value that users can set. | -| `WalletSize` | | `number` | `UInt32` | Unused. | +### 4.3. Flags -
+There are two flags on this object: -We propose these additional fields: -| Field Name | Required? | JSON Type | Internal Type | -|------------|-----------|-----------|---------------| -|`SponsorAccount`| |`string`|`AccountID`| -|`SponsoredOwnerCount`| |`number`|`UInt32`| -|`SponsoringOwnerCount`| |`number`|`UInt32`| -|`SponsoringAccountCount`| |`number`|`UInt32`| +* `lsfRequireSignatureForFee`: Every use of this sponsor for sponsoring fees requires a signature from the sponsor. +* `lsfRequireSignatureForReserve`: Every use of this sponsor for sponsoring reserves requires a signature from the sponsor. -#### 3.1.1. `SponsorAccount` +### 4.4. Ownership -The `SponsorAccount` field is already added in the ledger common fields (see section [2.1.1](#211-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. +The object is owned by `Sponsor`, who also pays the reserve. -This field is included if the account was created with a sponsor paying its account reserve. If this sponsored account is deleted, the destination of the `AccountDelete` transaction must equal `SponsorAccount`, so that the sponsor can recoup their fees. +### 4.5. Reserve + +This object charges 1 reserve. + +### 4.6. Deletion + +This object will be deleted any time the `FeeAmount` and `ReserveCount` are both `0`. This can be done directly via `SponsorshipSet`, or can occur in the regular flow of transactions, if the sponsorship runs out. + +### 4.7. Invariant Checks + +* At least one of `FeeAmount` and `ReserveCount` is nonzero +* `SponsorAccount` != `Sponsee` +* `FeeAmount` is nonnegative and denominated in XRP -_Note: The `Destination` field of `AccountDelete` will still work as-is if the account is not sponsored, where it can be set to any account._ +### 4.8. RPC Name -#### 3.1.2. `SponsoredOwnerCount` +The `snake_case` form of the ledger object name is `sponsorship`. -This is the number of objects the account owns that are being sponsored by a sponsor. +## 5. Ledger Entry: `AccountRoot` -#### 3.1.3. `SponsoringOwnerCount` +### 5.1. Fields + +
+ + +As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/accountroot/#accountroot-fields) are the fields that the `AccountRoot` ledger object currently has. + + +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-----------|---------------|-------------| +|`Account`|✔️ |✔️ |N/A|`string`|`AccountID`|The identifying (classic) address of this account.| +|`AccountTxnID`| | |N/A|`string`|`Hash256`|The identifying hash of the transaction most recently sent by this account.| +|`AMMID`|✔️ | |N/A|`string`|`Hash256`|The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account.| +|`Balance`| | |N/A|`string`|`Amount`|The account's current XRP balance.| +|`BurnedNFTokens`| | |`0`|`number`|`UInt32`| How many total of this account's issued NFTs have been burned.| +|`Domain`| | |N/A|`string`|`Blob`|A domain associated with this account.| +|`EmailHash`| | |N/A|`string`|`Hash128`|The md5 hash of an email address.| +|`FirstNFTokenSequence`|✔️ | |N/A|`number`|`UInt32`|The account's Sequence Number at the time it minted its first non-fungible-token.| +|`LedgerEntryType`|✔️ |✔️ |N/A|`string`|`UInt16`|The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object.| +|`MessageKey`| | |N/A|`string`|`Blob`|A public key that may be used to send encrypted messages to this account.| +|`MintedNFTokens`| | |`0`|`number`|`UInt32`|How many total non-fungible tokens have been minted by/on behalf of this account.| +|`NFTokenMinter`| | |N/A|`string`|`AccountID`|Another account that can mint NFTs on behalf of this account.| +|`OwnerCount`| |✔️ |N/A|`number`|`UInt32`|The number of objects this account owns in the ledger, which contributes to its owner reserve.| +|`PreviousTxnID`| |✔️ |N/A|`string`|`Hash256`|The identifying hash of the transaction that most recently modified this object.| +|`PreviousTxnLgrSeq`| |✔️ |N/A|`number`|`UInt32`|The ledger index that contains the transaction that most recently modified this object.| +|`RegularKey`| | |N/A|`string`|`AccountID`|The address of a key pair that can be used to sign transactions for this account instead of the master key.| +|`Sequence`| |✔️ |N/A|`number`|`UInt32`|The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account.| +|`TicketCount`| | |N/A|`number`|`UInt32`|How many Tickets this account owns in the ledger.| +|`TickSize`| | |N/A|`number`|`UInt8`|[How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize)| +|`TransferRate`| | |N/A|`number`|`UInt32`|A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other.| +|`WalletLocator`| | |N/A|`string`|`Hash256`|An arbitrary 256-bit value that users can set.| +|`WalletSize`| | |N/A|`number`|`UInt32`|Unused.| +
-This is the number of objects the account is sponsoring the reserve for. +We propose these additional fields: +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-----------|---------------|-------------| +|`SponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the account reserve for this account. | +|`SponsoredOwnerCount`| | |`0`|`number`|`UInt32`|The number of objects the account owns that are being sponsored by a sponsor. +|`SponsoringOwnerCount`| | |`0`|`number`|`UInt32`|The number of objects the account is sponsoring the reserve for.| +|`SponsoringAccountCount`| | |`0`|`number`|`UInt32`|The number of accounts that the account is sponsoring the reserve for.| + +#### 5.1.1. `SponsorAccount` -#### 3.1.4. `SponsoringAccountCount` +The `SponsorAccount` field is already added in the ledger common fields (see section [3.1.1](#311-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. -This is the number of accounts that the account is sponsoring the reserve for. +This field is included if the account was created with a sponsor paying its account reserve. If this sponsored account is deleted, the destination of the `AccountDelete` transaction must equal `SponsorAccount`, so that the sponsor can recoup their fees. -### 3.2. Account Reserve Calculation +*Note: The `Destination` field of `AccountDelete` will still work as-is if the account is not sponsored, where it can be set to any account.* + +### 5.2. Account Reserve Calculation The existing reserve calculation is: -$$ acctReserve + objReserve \* acct.OwnerCount $$ +$$ acctReserve + objReserve * acct.OwnerCount $$ The total account reserve should now be calculated as: -$$ -\displaylines{ -(acct.SponsorAccount \text{ ? } 0 : acctReserve) + \\ +$$ \displaylines{ +(acct.SponsorAccount \text{ ? } 0 : acctReserve) + \\ objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + \\ acctReserve * acct.SponsoringAccountCount -} -$$ +} $$ -## 4. Transactions: Common Fields +## 6. Transactions: Common Fields -### 4.1. Fields +### 6.1. Fields As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/common-fields/) are the fields that all transactions currently have. @@ -183,92 +229,107 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/co We propose these modifications: -| Field Name | Required? | JSON Type | Internal Type | -| ---------- | --------- | --------- | ------------- | -| `Sponsor` | | `object` | `STObject` | +| Field Name | Required? | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|---------------| +|`Sponsor`| |`object`|`STObject`| This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. -#### 4.1.1. `Sponsor` +#### 6.1.1. `Sponsor` The `Sponsor` inner object contains all of the information for the sponsorship happening in the transaction. The fields contained in this object are: -| Field Name | Required? | JSON Type | Internal Type | -| --------------- | --------- | --------- | ------------- | -| `Account` | ✔️ | `string` | `AccountID` | -| `Flags` | ✔️ | `number` | `UInt16` | -| `SigningPubKey` | | `string` | `STBlob` | -| `Signature` | | `string` | `STBlob` | -| `Signers` | | `array` | `STArray` | +| Field Name | Required? | JSON Type | Internal Type | +|------------|-----------|-----------|---------------| +|`SponsorAccount`|✔️|`string`|`AccountID`| The sponsoring account.| +|`Flags`| ✔️|`number`|`UInt16`| Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve).| +|`SigningPubKey`| |`string`|`STBlob`|The `SigningPubKey` for `SponsorAccount`, if single-signing.| +|`Signature`| |`string`|`STBlob`| A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`.| +|`Signers`| |`array`|`STArray`|An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`.| -##### 4.1.1.1. `Account` +##### 6.1.1.1. `Account` The `Sponsor.Account` field represents the sponsor. This field **will** be a signing field (it will be included in transaction signatures). -##### 4.1.1.2. `Flags` +##### 6.1.1.2. `Flags` The `Flags` field allows the user to specify which sponsorship type(s) they wish to participate in. At least one flag **must** be specified if the `Sponsor` field is included in a transaction. There are two flag values that are supported: - -- `0x00000001`: `tfSponsorFee`, sponsoring (paying for) the fee of the transaction. -- `0x00000002`: `tfSponsorReserve`, sponsoring the reserve for any objects created in the transaction. +* `0x00000001`: `tfSponsorFee`, sponsoring (paying for) the fee of the transaction. +* `0x00000002`: `tfSponsorReserve`, sponsoring the reserve for any objects created in the transaction. This field **will** be a signing field (it will be included in transaction signatures). -##### 4.1.1.3. `SigningPubKey` and `Signature` - -These fields are included if the sponsor is signing with a single signature (as opposed to multi-sign). This field contains a signature of the transaction from the sponsor, to indicate their approval of this transaction. All signing fields must be included in the signature, including `Sponsor.Account` and `Sponsor.Flags`. +##### 6.1.1.3. `SigningPubKey`, `Signature` and `Signers` Either `Signature` or `Signers` must be included in the final transaction. There will be no additional transaction fee required for the use of the `Signature` field. -`Signature` **will not** be a signing field (it will not be included in transaction signatures, though it will still be included in the stored transaction). +If the `Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\\#signatures+1)*base\textunderscore fee$. -##### 4.1.1.4. `Signers` +The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$. -This field contains an array of signatures of the transaction from the sponsor,'s signers to indicate their approval of this transaction. All signing fields must be included, including `Sponsor.Account` and `Sponsor.Flags`. +`Signature` and `Signers` **will not** be signing fields (they will not be included in transaction signatures, though they will still be included in the stored transaction). -Either `Signature` or `Signers` must be included in the final transaction. +Either `SigningPubKey`+`Signature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. -If the `Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\\#signatures+1)*base\textunderscore fee$. +### 6.2. Transaction Fee -The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$. +### 6.3. Failure Conditions + +#### 6.3.1. General Failures +* `Sponsor.Signature` is invalid. +* `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). +* The `SponsorAccount` doesn't exist on the ledger. +* An invalid sponsorship flag is used. +* `Sponsor.SigningPubKey`, `Sponsor.Signature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). -This field **will not** be a signing field (it will not be included in transaction signatures, though it will still be included in the stored transaction). +#### 6.3.2. Fee Sponsorship Failures +* The sponsor does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) -### 4.2. Failure Conditions +If a `Sponsorship` object exists: +* The `lsfRequireSignatureForFee` flag is enabled and there is no sponsor signature included. +* There is not enough XRP in the `FeeAmount` to pay for the transaction. -#### 4.2.1. General Failures +If a `Sponsorship` object does not exist: +* There is no sponsor signature included. -- `Sponsor.Signature` is invalid -- `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid) -- The sponsor account doesn't exist on the ledger -- An invalid sponsorship flag is used +#### 6.3.3. Reserve Sponsorship Failures +* The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) +* The transaction does not support reserve sponsorship (see section 6.3.4) -#### 4.2.2. Fee Sponsorship Failures +If a `Sponsorship` object exists: +* The `lsfRequireSignatureForReserve` flag is enabled and there is no sponsor signature included. +* There is not enough remaining count in the `ReserveCount` to pay for the transaction. -- The sponsor does not have enough XRP to cover the transaction fee - +If a `Sponsorship` object does not exist: +* There is no sponsor signature included. + +#### 6.3.4. Transactions that cannot be sponsored + +All transactions (other than pseudo-transactions) may use the `tfSponsorFee` flag, since they all have a fee. -#### 4.2.3. Reserve Sponsorship Failures +However, some transactions will not support the `tfSponsorReserve` flag. -- The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) -- The transaction does not support reserve sponsorship (see section 4.4) +* [`Batch` transactions](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch) + * It doesn't make any sense for `Batch` to support that flag. The inner transactions should use `tfSponsorReserve` instead. +* All pseudo-transactions (currently `EnableAmendment`, `SetFee`, and `UNLModify`) + * The reserves for those objects are covered by the network, not by any one account. -### 4.3. State Changes +Also, many transactions, such as `AccountSet`, will have no change in output when using the `tfSponsorReserve` flag, if they do not create any new objects or accounts. -#### 4.3.1. Fee Sponsorship State Changes +### 6.4. State Changes -The fee will be deducted from the sponsor instead of the sponsee. That's it. +#### 6.4.1. Fee Sponsorship State Changes +If a `Sponsorship` object exists, the `tx.Fee` value is decremented from the `Sponsorship.FeeAmount`. -#### 4.3.2. Reserve Sponsorship State Changes +If a `Sponsorship` object does not exist, the `tx.Fee` value is decremented from the sponsor's `AccountRoot.Balance`. +#### 6.4.2. Reserve Sponsorship State Changes Any account/object that is created as a part of the transaction will have a `Sponsor` field. The sponsor's `SponsoringOwnerCount` field will be incremented by the number of objects that are sponsored as a part of the transaction, and the `SponsoringAccountCount` field will be incremented by the number of new accounts that are sponsored as a part of the transaction. @@ -277,122 +338,183 @@ The sponsee's `SponsoredOwnerCount` field will be incremented by the number of o The `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` fields will be decremented when those objects/accounts are deleted. -### 4.4. Transactions that cannot be sponsored +## 7. Transaction: `SponsorshipSet` -All transactions (other than pseudo-transactions) may use the `tfSponsorFee` flag, since they all have a fee. +### 7.1. Fields -However, some transactions will not support the `tfSponsorReserve` flag. +| Field Name | Required? | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-------------| +|`SponsorAccount`|✔️ |`string`|`AccountID`|The sponsor associated with this relationship. This account also pays for the reserve of this object.| +|`Sponsee`|✔️ |`string`|`AccountID`|The sponsee associated with this relationship.| +|`FeeAmount`| |`string`|`Amount`| The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.| +|`ReserveCount`| |`number`|`UInt32`| The (remaining) amount of reserves that the sponsor has provided for the sponsee to use.| -- `Batch` (from [XLS-56d](https://github.com/XRPLF/XRPL-Standards/discussions/162)) - - It doesn't make any sense for `Batch` to support that flag. The sub-transactions should use `tfSponsorReserve` instead. -- All pseudo-transactions (currently `EnableAmendment`, `SetFee`, and `UNLModify`) - - The reserves for those objects are covered by the network, not by any one account. +### 7.2. Flags +same as Sponsorship object -Also, many transactions, such as `AccountSet`, will have no change in output when using the `tfSponsorReserve` flag, if they do not create any new objects or accounts. +### 7.2. Failure Conditions +* `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` + +### 7.3. State Changes -## 5. Transaction: `SponsorTransfer` +* If the object already exists, the amounts are increased by the amounts indicated. +* If the object doesn't exist, it will be created. +* If the `tfDeleteObject` flag is used, it will delete the object (the amount/count fields must not be specified in this case). All funds remaining in the object will be sent back to the `SponsorAccount`. + * Both sponsor and sponsee can delete the object. + * Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process. + +## 8. Transaction: `SponsorshipTransfer` This transaction transfers a sponsor relationship for a particular ledger object's object reserve. The sponsor relationship can either be passed on to a new sponsor, or dissolved entirely (with the sponsee taking on the reserve). Either the sponsor or sponsee may submit this transaction at any point in time. -### 5.1. Fields +### 8.1. Fields -| Field Name | Required? | JSON Type | Internal Type | -| ----------------- | --------- | --------- | ------------- | -| `TransactionType` | ✔️ | `string` | `UInt16` | -| `Account` | ✔️ | `string` | `AccountID` | -| `LedgerIndex` | | `string` | `UInt256` | -| `Sponsor` | | `object` | `STObject` | +| Field Name | Required? | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-------------| +|`TransactionType`| ✔️|`string`|`UInt16`| +|`Account`|✔️|`string`|`AccountID`| +|`ObjectID`| |`string`|`UInt256`| +|`Sponsor`| |`object`|`STObject`| -#### 5.1.1. `LedgerIndex` +#### 8.1.1. `ObjectID` This field should be included if this transaction is dealing with sponsored object, rather than on a sponsored account. This field indicates which object the relationship is changing for. If it is not included, then it refers to the account sending the transaction. -#### 5.1.2. `Sponsor` +#### 8.1.2. `Sponsor` -The `Sponsor` field is already added in the ledger common fields (see section [4.1.1](#411-sponsor)), but it has some additional rules associated with it on the `SponsorTransfer` transaction. +The `Sponsor` field is already added in the ledger common fields (see section [5.1.1](#511-sponsor)), but it has some additional rules associated with it on the `SponsorshipTransfer` transaction. In this case, if `Sponsor` is included with the `tfSponsorReserve` flag, then the reserve sponsorship for the provided object will be transferred to the `Sponsor.Account` instead of passing back to the ledger object's owner. If there is no `Sponsor` field, or if the `tfSponsorReserve` flag is not included, then the burden of the reserve will be passed back to the ledger object's owner (the former sponsee). -### 5.2. Ending the Sponsorship for a Sponsored Ledger Object +### 8.2. Ending the Sponsorship for a Sponsored Ledger Object -A sponsored ledger object will have the `Sponsor` field attached to it. Ending the sponsor relationship for a sponsored ledger object requires the `LedgerIndex` parameter, to specify which ledger object. +A sponsored ledger object will have the `Sponsor` field attached to it. Ending the sponsor relationship for a sponsored ledger object requires the `ObjectID` parameter, to specify which ledger object. -Two accounts are allowed to submit a `SponsorTransfer` relationship to end the sponsor relationship for a sponsored ledger object: either the sponsor for that object or the owner of that object (the sponsee). +Two accounts are allowed to submit a `SponsorshipTransfer` relationship to end the sponsor relationship for a sponsored ledger object: either the sponsor for that object or the owner of that object (the sponsee). -### 5.3. Migrating a Sponsorship to a New Account +### 8.3. Migrating a Sponsorship to a New Account A sponsorship can be migrated to a new account by including the `Sponsor` field with the `tfSponsorReserve` flag. This can be done for either a sponsored account or a sponsored ledger object. -Two accounts are allowed to submit a `SponsorTransfer` relationship to migrate the sponsor relationship: the sponsor or the sponsee. +Two accounts are allowed to submit a `SponsorshipTransfer` relationship to migrate the sponsor relationship: the sponsor or the sponsee. The sponsor will likely only rarely want to do this (such as if they are transferring accounts), but the sponsee may want to migrate if they change providers. -### 5.4. Failure Conditions +### 8.4. Failure Conditions -- If transferring the sponsorship, the new sponsor does not have enough reserve for this object/account. -- If dissolving the sponsorship, the owner does not have enough reserve for this object/account. -- The new sponsor does not exist. +* If transferring the sponsorship, the new sponsor does not have enough reserve for this object/account. +* If dissolving the sponsorship, the owner does not have enough reserve for this object/account. +* The new sponsor does not exist. +* The `tx.Account` neither the sponsor nor the owner of `ObjectID`. -### 5.5. State Changes +### 8.5. State Changes -- The `Sponsor` field on the object is changed or deleted. -- The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. -- The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. -- If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. +* The `Sponsor` field on the object is changed or deleted. +* The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. +* The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. +* If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. -## 6. RPC: `account_objects` +## 9. Transaction: `AccountDelete` -### 6.1. Fields +This transaction deletes an account. + +As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete) are the fields that `AccountDelete` currently has. This amendment proposes no changes to the fields, only to the behavior. + +### 9.1. Failure Conditions + +Existing failure conditions still apply. + +If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` field: +* The `Destination` is not equal to `AccountRoot.SponsorAccount`. + +If the `AccountRoot` associated with the `tx.Account` has a `SponsoringOwnerCount` or `SponsoringAccountCount` field, the transaction will fail with `tecHAS_OBLIGATIONS`. + +### 9.2. State Changes + +Existing state changes still apply, including rules around deletion blockers. + +If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` field, the `SponsorAccount`'s `AccountRoot.SponsoringAccountCount` is decremented by 1. + +If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. + +## 10. Permission: `SponsorFee` + +This delegatable granular permission allows an account to sponsor fees on behalf of another account. + +## 11. Permission: `SponsorReserve` + +This delegatable granular permission allows an account to sponsor reserves on behalf of another account. + +## 12. RPC: `account_objects` + +### 12.1. Request Fields The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts: -| Field Name | Required? | JSON Type | -| ------------------------ | --------- | -------------------- | -| `account` | ✔️ | `string` | -| `deletion_blockers_only` | | `boolean` | -| `ledger_hash` | | `string` | -| `ledger_index` | | `number` or `string` | -| `limit` | | `number` | -| `marker` | | `any` | -| `type` | | `string` | +| Field Name | Required? | JSON Type | Description | +|------------|-----------|-----------|-------------| +|`account`|✔️|`string`|Get ledger entries associated with this account.| +|`deletion_blockers_only`| |`boolean`|If `true`, only return ledger entries that would block this account from being deleted. The default is `false`. | +|`ledger_hash`| |`string`|The unique hash of the ledger version to use.| +|`ledger_index`| |`number` or `string`|The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically.| +|`limit`| |`number`|The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`.| +|`marker`| |`any`|Value from a previous paginated response. Resume retrieving data where that response left off.| +|`type`| |`string`|Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types.| We propose this additional field: -| Field Name | Required? | JSON Type | -| ----------- | --------- | --------- | -| `sponsored` | | `boolean` | +| Field Name | Required? | JSON Type | Description | +|------------|-----------|-----------|-------------| +|`sponsored`| |`boolean`|If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | -### 6.2. `sponsored` +### 12.2. Response Fields -If this field is excluded, all objects, sponsored or not, will be included. If `sponsored == True`, only sponsored objects will be included. If `sponsored == False`, only non-sponsored objects will be included. +The response fields remain the same. -## 7. RPC: `account_sponsoring` +## 13. RPC: `account_sponsoring` The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `SponsorAccount` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). -| Field Name | Required? | JSON Type | Description | -| ------------------------ | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------- | -| `account` | ✔️ | `string` | The sponsor in question. | -| `deletion_blockers_only` | | `boolean` | If `true`, the response only includes objects that would block this account from being deleted. The default is `false`. | -| `ledger_hash` | | `string` | A hash representing the ledger version to use. | -| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | -| `limit` | | `number` | The maximum number of objects to include in the results. | -| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | -| `type` | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`. | +### 13.1. Request Fields + +| Field Name | Required? | JSON Type | Description | +|------------|-----------|-----------|-------------| +|`account`|✔️|`string`|The sponsor in question.| +|`deletion_blockers_only`| |`boolean`|If `true`, the response only includes objects that would block this account from being deleted. The default is `false`.| +|`ledger_hash`| |`string`|A hash representing the ledger version to use.| +|`ledger_index`| |`number` or `string`|The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically.| +|`limit`| |`number`|The maximum number of objects to include in the results.| +|`marker`| |`any`|Value from a previous paginated response. Resume retrieving data where that response left off.| +|`type`| |`string`|Filter results by a ledger entry type. Some examples are `offer` and `escrow`.| + +### 13.2. Response Fields + +The response fields are nearly identical to `account_objects`. + +| Field Name | Always Present? | JSON Type | Description | +|------------|-----------|-----------|-------------| +|`account`|✔️|`string`|The account this request corresponds to.| +|`sponsored_objects`|✔️|`array`|Array of ledger entries in this account's owner directory. This includes entries that are owned by this account and entries that are linked to this account but owned by someone else, such as escrows where this account is the destination. Each member is a ledger entry in its raw ledger format. This may contain fewer entries than the maximum specified in the `limit` field.| +|`ledger_hash`| |`string`|The identifying hash of the ledger that was used to generate this response.| +|`ledger_index`| |`number`|The ledger index of the ledger that was used to generate this response.| +|`ledger_current_index`| |`number`|The ledger index of the open ledger that was used to generate this response.| +|`limit`| |`number`|The limit that was used in this request, if any.| +|`marker`| |`any`|Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one.| +|`validated`| |`boolean`|If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change.| -## 8. Security +## 14. Security -### 8.1. Security Axioms +### 14.1. Security Axioms Both the sponsee _and_ the sponsor must agree to enter into a sponsor relationship. The sponsee must be okay with the sponsor handling the reserve, and the sponsor must be willing to take on that reserve. A signature from both parties ensures that this is the case. -A sponsor will never be stuck sponsoring an sponsee's account or object it doesn't want to support anymore, because it can submit a `SponsorTransfer` transaction at any point. +A sponsor will never be stuck sponsoring an sponsee's account or object it doesn't want to support anymore, because it can submit a `SponsorshipTransfer` transaction at any point. -The sponsor's signature must _always_ include the `Account` and `Sequence` fields, to prevent signature replay attacks (where the sponsor's signature can be reused to sponsor an object or account that they did not want to sponsor). +The sponsor's signature must *always* include the `Account` and `Sequence` fields, to prevent signature replay attacks (where the sponsor's signature can be reused to sponsor an object or account that they did not want to sponsor). When sponsoring transaction fees, the sponsor must approve of the `Fee` value of the transaction, since that is the amount that they will be paying. @@ -400,39 +522,37 @@ When sponsoring reserves, the sponsor's signature must include any aspects of th A sponsee cannot take advantage of the generosity of their sponsor, since the sponsor must sign every transaction it wants to sponsor the ledger objects for. A sponsee also must not be able to change the sponsorship type that the sponsor is willing to engage in, as this could lock up to 500 of the sponsor's XRP (in the case of 250 tickets being created in one `TicketCreate` transaction). -An axiom that is out of scope: the sponsee _will not_ have any control over a sponsorship transfer. This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency. +An axiom that is out of scope: the sponsee *will not* have any control over a sponsorship transfer. This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency. -### 8.2. Signatures +### 14.2. Signatures Since a fee sponsorship must approve of the `Fee` field, and a reserve sponsorship must approve of a broad set of transaction fields, the sponsor must always sign the whole transaction. This also avoids needing to have different sponsorship processes for different sponsorship types. This includes the non-signature parts of the `Sponsor` object (`Sponsor.Account` and `Sponsor.Flags`). The same is true for the sponsee's transaction signature; the sponsee must approve of the sponsor and sponsorship type. A sponsor's `Signature` cannot be replayed or attached to a different transaction, since the whole transaction (including the `Account` and `Sequence` values) must be signed. -## 9. Invariants +## 15. Invariants An [invariant](https://xrpl.org/docs/concepts/consensus-protocol/invariant-checking/) is a statement, usually an equation, that must always be true for every valid ledger state on the XRPL. Invariant checks serve as a last line of defense against bugs; the `tecINVARIANT_FAILED` error is thrown if an invariant is violated (which ideally should never happen). -### 9.1. Tracking Owner Counts - +### 15.1. Tracking Owner Counts A transaction that creates a ledger object either increments an account's `OwnerCount` by 1 or increments two separate accounts' `SponsoringOwnerCount` and `SponsoredOwnerCount` by 1. The opposite happens when a ledger object is deleted. The equivalent also should happen with `SponsoringAccountCount`. -### 9.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount` - -$$ \sum*{accounts} Account.SponsoredOwnerCount = \sum*{accounts} Account.SponsoringOwnerCount $$ +### 15.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount` +$$ \sum_{accounts} Account.SponsoredOwnerCount = \sum_{accounts} Account.SponsoringOwnerCount $$ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to the sum of all accounts' `SponsoringOwnerCount`s. This ensures that every sponsored object is logged as being sponsored and also has a sponsor. -## 10. Example Flows +## 16. Example Flows Each example will show what the transaction will look like before **and** after both the sponsor and sponsee sign the transaction. The unsigned transaction must be autofilled before it is passed to the sponsor to sign. Tooling can be updated to handle combining the sponsor and sponsee signatures, similar to helper functions that already exist for multisigning. -### 10.1. Fee Sponsorship +### 16.1. Fee Sponsorship -#### 10.1.1. The Unsigned Transaction +#### 16.1.1. The Unsigned Transaction
@@ -450,10 +570,9 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t } } ``` -
-#### 10.1.2. The Signed Transaction +#### 16.1.2. The Signed Transaction
@@ -475,14 +594,13 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t SigningPubKey: "03A8D0093B0CD730F25E978BF414CA93084B3A2CBB290D5E0E312021ED2D2C1C8B", // rAccount's public key TxnSignature: "3045022100F2AAF90D8F9BB6C94C0C95BA31E320FC601C7BAFFF536CC07076A2833CB4C7FF02203F3C76EB34ABAD61A71CEBD42307169CDA65D9B3CA0EEE871210BEAB824E524B" ``` -
-### 10.2. Account Sponsorship +### 16.2. Account Sponsorship The only way an account can be created is via a `Payment` transaction. So the sponsor relationship must be initiated on the `Payment` transaction. -#### 10.2.1. The Unsigned Transaction +#### 16.2.1. The Unsigned Transaction
@@ -500,10 +618,9 @@ The only way an account can be created is via a `Payment` transaction. So the sp } } ``` -
-#### 10.2.2. The Signed Transaction +#### 16.2.2. The Signed Transaction
@@ -525,12 +642,11 @@ The only way an account can be created is via a `Payment` transaction. So the sp TxnSignature: "30440220245217F931FDA0C5E68B935ABB4920211D5B6182878583124DE4663B19F00BEC022070BE036264760551CF40E9DAFC8B84036FA70E7EE7257BB7E39AEB7354B2EB86" } ``` -
-### 10.3. Object Sponsorship +### 16.3. Object Sponsorship -#### 10.3.1. The Unsigned Transaction +#### 16.3.1. The Unsigned Transaction
@@ -547,10 +663,9 @@ The only way an account can be created is via a `Payment` transaction. So the sp } } ``` -
-#### 10.3.2. The Signed Transaction +#### 16.3.2. The Signed Transaction
@@ -571,20 +686,32 @@ The only way an account can be created is via a `Payment` transaction. So the sp TxnSignature: "3044022047CB72DA297B067C0E69045B7828AD660F8198A6FA03982E31CB6D27F0946DDE022055844EB63E3BFF7D9ABFB26645AA4D2502E143F4ABEE2DE57EB87A1E5426E010" } ``` -
- + * Maybe a second `Sponsor` field or something? +* How do we handle account creation? The actual account owner's signing keys aren't involved in that at all... Maybe just a new flag on the payment saying you'll pay the reserve for the account? +* Should it be `ReserveCount` or `ReserveAmount`? + * If `ReserveCount`, it can be decremented to keep track of how much is left. + * Pro: easier to reason about how much you're giving someone. + * Con: sponsors have to update the number if reserves increase and your cost-benefit analysis changes. + * If `ReserveAmount`, you'd need a separate "`OwnerCount`" field to keep track of how many objects the thing owns. And probably a flag for whether the account itself is sponsored. + * Pro: people are used to thinking in terms of XRP. + * Con: rippled math might get more complicated. You have to keep track of potential reserve sponsorships in two places (`Sponsorship` and `AccountRoot`) +* Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve? +* Should there be a "max XRP per transaction" field in `Sponsorship`? +* If a transaction doesn't take a fee (first `SetRegularKey`) or doesn't increase reserve (e.g. `AccountDelete`), and is sponsored, should that transaction fail or succeed. +* Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? +* Should we allow sponsorship of creating another account? e.g. Account A is sponsored by Sponsor, A creates B, does Sponsor also sponsor B or does this fail if A doesn't have the funds to create B? +* Should `account_sponsoring` be Clio-only? # Appendix @@ -604,37 +731,31 @@ If a sponsored object is deleted (either due to normal object deletion processes ### A.3: What if a sponsor that is sponsoring a few objects wants to delete their account? -An account cannot be deleted if it is sponsoring **any** existing accounts or objects. They will need to either delete those objects (by asking the owner to do so) or use the `SponsorTransfer` transaction to relinquish control of them. +An account cannot be deleted if it is sponsoring **any** existing accounts or objects. They will need to either delete those objects (by asking the owner to do so, as they cannot do so directly) or use the `SponsorshipTransfer` transaction to relinquish control of them. ### A.4: Does a sponsor have any powers over an object they pay the reserve for? I.e. can they delete the object? -No. If a sponsor no longer wants to support an object, they can always use the `SponsorTransfer` transaction instead. - - +No. If a sponsor no longer wants to support an object, they can always use the `SponsorshipTransfer` transaction instead to transfer the reserve burden back to the sponsee. ### A.5: What if a sponsee refuses to delete their account when a sponsor wants to stop supporting their account? -The sponsor will have the standard problem of trying to get ahold of a debtor to make them pay. They may be able to use `SponsorTransfer` transaction to put the onus on the sponsee, though the sponsee would need to have enough XRP in their account to cover the reserve. +The sponsor will have the standard problem of trying to get ahold of a debtor to make them pay. They may use the `SponsorshipTransfer` transaction to put the onus on the sponsee. If the sponsee does not have enough XRP to cover the reserve for those objects, they will not be able to create any more objects until they do so. -### A.6: What happens if the sponsor tries to `SponsorTransfer` but the sponsee doesn't have enough funds to cover the reserve? +### A.6: What happens if the sponsor tries to `SponsorshipTransfer` but the sponsee doesn't have enough funds to cover the reserve? -If the sponsor really needs to get out of the sponsor relationship ASAP without recouping the value of the reserve, they can pay the sponsee the amount of XRP they need to cover the reserve. These steps can be executed atomically via a [Batch transaction](https://github.com/XRPLF/XRPL-Standards/discussions/162), to ensure that the sponsee can't do something else with the funds before the `SponsorTransfer` transaction is validated. +If the sponsor really needs to get out of the sponsor relationship ASAP without recouping the value of the reserve, they can pay the sponsee the amount of XRP they need to cover the reserve. These steps can be executed atomically via a [Batch transaction](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), to ensure that the sponsee can't do something else with the funds before the `SponsorshipTransfer` transaction is validated. ### A.7: Would sponsored accounts carry a lower reserve? -No, they would still carry a reserve of 10 XRP at current levels. +No, they would still carry a reserve of 1 XRP at current levels. ### A.8: Can an existing unsponsored ledger object/account be sponsored? -Yes, with the `SponsorTransfer` transaction. +Yes, with the `SponsorshipTransfer` transaction. ### A.9: Can a sponsored account be a sponsor for other accounts/objects? -No. +Yes, though they will have to use their own XRP for this (not from another sponsor). ### A.10: Can a sponsored account hold unsponsored objects, or objects sponsored by a different sponsor? @@ -652,17 +773,17 @@ This is something that good tooling can solve. It could work similarly to how mu See Appendix B for the alternate designs that were considered and why this one was preferred. If you have another one in mind, please describe it in the comments and we can discuss. -### A.14: How is this account sponsorship model different from/better than [XLS-23d, Lite Accounts](https://github.com/XRPLF/XRPL-Standards/discussions/56)? +### A.14: How is this account sponsorship model different from/better than [XLS-23d, Lite Accounts](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0023-lite-accounts)? -- Sponsored accounts do not have any restrictions, and can hold objects. -- Sponsored accounts require the same reserve as a normal account (this was one of the objections to the Lite Account proposal). -- Lite accounts can be deleted by their sponsor. +* Sponsored accounts do not have any restrictions, and can hold objects. +* Sponsored accounts require the same reserve as a normal account (this was one of the objections to the Lite Account proposal). +* Lite accounts can be deleted by their sponsor. ### A.15: How will this work for objects like trustlines, where multiple accounts might be holding reserves for it? The answer to this question is still being explored. One possible solution is to add a second field, `Sponsor2`, to handle the other reserve. -### A.16: How does this proposal work in conjunction with [XLS-49d](https://github.com/XRPLF/XRPL-Standards/discussions/144)? What signer list(s) have the power to sponsor fees or reserves? +### A.16: How does this proposal work in conjunction with [XLS-49d](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0049-multiple-signer-lists)? What signer list(s) have the power to sponsor fees or reserves? Currently, only the global signer list is supported. Another `SignerListID` value could be added to support sponsorship. Transaction values can only go up to $2^{16}$, since the `TransactionType` field is a `UInt16`, but the `SignerListID` field goes up to $2^{32}$, so there is room in the design for additional values that do not correlate to a specific transaction type. @@ -672,7 +793,7 @@ Currently, only the global signer list is supported. Another `SignerListID` valu This design involved updating `AccountSet` to allow users to add a `Sponsor` to their account (with a signature from the sponsor as well). The sponsor would then sponsor every object from that account while the field was active, and either the sponsor or the account could remove the sponsorship at any time. -This was a previous version of the spec, but it made more sense for the relationship to be specific to a transaction/transactions, to prevent abuse (the sponsor should decide what objects they want to support and what objects they don't want to support). +This was a previous version of the spec, but it made more sense for the relationship to be specific to a specific transaction(s), to prevent abuse (the sponsor should decide what objects they want to support and what objects they don't want to support). The current design also supports having different sponsors for different objects, which allows users to use a broad set of services and platforms, instead of being locked into one. @@ -682,7 +803,7 @@ The current design also supports having different sponsors for different objects There would be a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56d](https://github.com/XRPLF/XRPL-Standards/discussions/162), that the sponsor would sign. It would contain a sub-transaction from the sponsee. -It would look something like this: +It would look something like this: |FieldName | Required? | JSON Type | Internal Type | |:---------|:-----------|:---------------|:------------| |`TransactionType`|✔️|`string`|`UInt16`| From b66f87ae4121411a3cdb1fbfd7db93b3611059db Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 9 Sep 2025 11:46:00 -0400 Subject: [PATCH 02/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ From bc6cc35383af9b94278e0b0689a7dd55670583af Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 9 Sep 2025 11:57:35 -0400 Subject: [PATCH 03/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- XLS-0068-sponsored-fees-and-reserves/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 968e1680d..7ffd82479 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -589,10 +589,10 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t Flags: 1, SigningPubKey: "03072BBE5F93D4906FC31A690A2C269F2B9A56D60DA9C2C6C0D88FB51B644C6F94", // rSponsor's public key Signature: "3045022100C15AFB7C0C4F5EDFEC4667B292DAB165B96DAF3FFA6C7BBB3361E9EE19E04BC70220106C04B90185B67DB2C67864EB0A11AE6FB62280588954C6E4D9C1EF3710904D" - } -}, -SigningPubKey: "03A8D0093B0CD730F25E978BF414CA93084B3A2CBB290D5E0E312021ED2D2C1C8B", // rAccount's public key -TxnSignature: "3045022100F2AAF90D8F9BB6C94C0C95BA31E320FC601C7BAFFF536CC07076A2833CB4C7FF02203F3C76EB34ABAD61A71CEBD42307169CDA65D9B3CA0EEE871210BEAB824E524B" + }, + SigningPubKey: "03A8D0093B0CD730F25E978BF414CA93084B3A2CBB290D5E0E312021ED2D2C1C8B", // rAccount's public key + TxnSignature: "3045022100F2AAF90D8F9BB6C94C0C95BA31E320FC601C7BAFFF536CC07076A2833CB4C7FF02203F3C76EB34ABAD61A71CEBD42307169CDA65D9B3CA0EEE871210BEAB824E524B" +} ``` From 0e366d100920dea0eb800ff38db20bebddc4990a Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 10 Sep 2025 21:30:12 -0400 Subject: [PATCH 04/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- .../README.md | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 7ffd82479..3afbbda3b 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -48,8 +48,8 @@ This feature will require an amendment, tentatively titled `Sponsor`. * **Sponsor**: The account that is covering the reserve or paying the transaction fee on behalf of another account. * **Sponsee**: The account that the sponsor is paying a transaction fee or reserve on behalf of. * **Owner**: The account that owns a given object (or the account itself). This is often the same as the sponsee. -* **Sponsored account**: An account that a sponsor is covering the reserve for (currently priced at 10 XRP). -* **Sponsored object**: A non-account ledger object that a sponsor is covering the reserve for (currently priced at 2 XRP). +* **Sponsored account**: An account that a sponsor is covering the reserve for (currently priced at 1 XRP). +* **Sponsored object**: A non-account ledger object that a sponsor is covering the reserve for (currently priced at 0.2 XRP). * **Sponsor relationship**: The relationship between a sponsor and sponsee. * **Sponsorship type**: The "type" of sponsorship - sponsoring transaction fees vs. sponsoring reserves. @@ -111,7 +111,10 @@ We propose this additional field: ### 4.1. Object ID -The object ID for a `Sponsorship` object will be the space key (defined during implementation), plus the `Sponsor` and `Sponsee` accounts, hashed together. +The key of the `Sponsorship` 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: +* The `Sponsorship` space key (defined during implementation) +* The `AccountID` of the `Sponsor` +* The `AccountID` of the `Sponsee` ### 4.2. Fields @@ -128,8 +131,10 @@ The object ID for a `Sponsorship` object will be the space key (defined during i There are two flags on this object: -* `lsfRequireSignatureForFee`: Every use of this sponsor for sponsoring fees requires a signature from the sponsor. -* `lsfRequireSignatureForReserve`: Every use of this sponsor for sponsoring reserves requires a signature from the sponsor. +| Flag Name | Flag Value | Modifiable? | Description | +| ----------------- | :----------: | :---------: | :------------------------------------------: | +| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | ### 4.4. Ownership @@ -145,7 +150,7 @@ This object will be deleted any time the `FeeAmount` and `ReserveCount` are both ### 4.7. Invariant Checks -* At least one of `FeeAmount` and `ReserveCount` is nonzero +* `FeeAmount` >= 0 || `ReserveCount` >= 0 * `SponsorAccount` != `Sponsee` * `FeeAmount` is nonnegative and denominated in XRP @@ -258,8 +263,11 @@ This field **will** be a signing field (it will be included in transaction signa The `Flags` field allows the user to specify which sponsorship type(s) they wish to participate in. At least one flag **must** be specified if the `Sponsor` field is included in a transaction. There are two flag values that are supported: -* `0x00000001`: `tfSponsorFee`, sponsoring (paying for) the fee of the transaction. -* `0x00000002`: `tfSponsorReserve`, sponsoring the reserve for any objects created in the transaction. + +| Flag Name | Flag Value | Description | +| ----------------- | :----------: | :------------------------------------------: | +| `tfSponsorFee` | `0x00000001` | Sponsoring (paying for) the fee of the transaction. | +| `tfSponsorReserve` | `0x00000002` | Sponsoring the reserve for any objects created in the transaction. | This field **will** be a signing field (it will be included in transaction signatures). @@ -316,7 +324,7 @@ All transactions (other than pseudo-transactions) may use the `tfSponsorFee` fla However, some transactions will not support the `tfSponsorReserve` flag. * [`Batch` transactions](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch) - * It doesn't make any sense for `Batch` to support that flag. The inner transactions should use `tfSponsorReserve` instead. + * `Batch` does not create any objects on its own, and therefore its use in the outer transaction would be confusing, as users may think that that means that all inner transactions are sponsored. The inner transactions should use `tfSponsorReserve` instead. * All pseudo-transactions (currently `EnableAmendment`, `SetFee`, and `UNLModify`) * The reserves for those objects are covered by the network, not by any one account. @@ -340,6 +348,8 @@ The `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` ## 7. Transaction: `SponsorshipSet` +This transaction creates and updates the `Sponsorship` object. + ### 7.1. Fields | Field Name | Required? | JSON Type | Internal Type | Description | @@ -350,16 +360,28 @@ The `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` |`ReserveCount`| |`number`|`UInt32`| The (remaining) amount of reserves that the sponsor has provided for the sponsee to use.| ### 7.2. Flags -same as Sponsorship object + +| Flag Name | Flag Value | Description | +| --------- | ---------- | ----------- | +| `tfSponsorshipSetRequireSignForFee` | `0x00010000` | Adds the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipClearRequireSignForFee` | `0x00020000` | Removes the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipSetRequireSignForReserve` | `0x00040000` | Adds the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfDeleteObject` | `0x00100000` | Removes the ledger object. | ### 7.2. Failure Conditions * `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` +* If `tfDeleteObject` is provided: + * `FeeAmount` is specified + * `ReserveCount` is specified + * `tfSponsorshipSetRequireSignForFee` is enabled + * `tfSponsorshipSetRequireSignForReserve` is enabled ### 7.3. State Changes -* If the object already exists, the amounts are increased by the amounts indicated. +* If the object already exists, `Sponsorship.Amount += tx.FeeAmount` and `Sponsorship.ReserveCount += tx.ReserveCount`. * If the object doesn't exist, it will be created. -* If the `tfDeleteObject` flag is used, it will delete the object (the amount/count fields must not be specified in this case). All funds remaining in the object will be sent back to the `SponsorAccount`. +* If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. * Both sponsor and sponsee can delete the object. * Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process. From 0f04e810409246bd0fe7a8d60c3a9101e12e2eb7 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 10 Sep 2025 21:31:30 -0400 Subject: [PATCH 05/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- .../README.md | 429 ++++++++++-------- 1 file changed, 228 insertions(+), 201 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 3afbbda3b..f97fc5648 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -30,12 +30,13 @@ Accounts can include signatures from sponsors in their transactions that will al Sponsors can also pre-fund fees or reserves, if they do not want to deal with the burden of co-signing every sponsored transaction. We propose: -* Creating the `Sponsorship` ledger entry -* Modifying the `AccountRoot` ledger entry -* Creating the `SponsorshipSet` transaction type -* Creating the `SponsorshipTransfer` transaction type -* Modifying the `AccountDelete` transaction type (behavior only, not fields) -* Adding two additional granular permissions (`SponsorFee`, `SponsorReserve`) + +- Creating the `Sponsorship` ledger entry +- Modifying the `AccountRoot` ledger entry +- Creating the `SponsorshipSet` transaction type +- Creating the `SponsorshipTransfer` transaction type +- Modifying the `AccountDelete` transaction type (behavior only, not fields) +- Adding two additional granular permissions (`SponsorFee`, `SponsorReserve`) The common fields for all ledger objects and all transactions will also be modified. @@ -45,33 +46,33 @@ This feature will require an amendment, tentatively titled `Sponsor`. ### 2.1. Terminology -* **Sponsor**: The account that is covering the reserve or paying the transaction fee on behalf of another account. -* **Sponsee**: The account that the sponsor is paying a transaction fee or reserve on behalf of. -* **Owner**: The account that owns a given object (or the account itself). This is often the same as the sponsee. -* **Sponsored account**: An account that a sponsor is covering the reserve for (currently priced at 1 XRP). -* **Sponsored object**: A non-account ledger object that a sponsor is covering the reserve for (currently priced at 0.2 XRP). -* **Sponsor relationship**: The relationship between a sponsor and sponsee. -* **Sponsorship type**: The "type" of sponsorship - sponsoring transaction fees vs. sponsoring reserves. +- **Sponsor**: The account that is covering the reserve or paying the transaction fee on behalf of another account. +- **Sponsee**: The account that the sponsor is paying a transaction fee or reserve on behalf of. +- **Owner**: The account that owns a given object (or the account itself). This is often the same as the sponsee. +- **Sponsored account**: An account that a sponsor is covering the reserve for (currently priced at 1 XRP). +- **Sponsored object**: A non-account ledger object that a sponsor is covering the reserve for (currently priced at 0.2 XRP). +- **Sponsor relationship**: The relationship between a sponsor and sponsee. +- **Sponsorship type**: The "type" of sponsorship - sponsoring transaction fees vs. sponsoring reserves. ### 2.2. The Sponsorship Flow (Not Pre-Funded) In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or reserve for the sponsee Alice's transaction. -* Alice constructs her transaction and autofills it (so that all fields, including the fee and sequence number, are included in the transaction). She adds Spencer's account and sponsorship type to the transaction as well. -* Spencer signs the transaction and provides his signature to Alice. -* Alice adds Spencer's public key and signature to her transaction. -* Alice signs and submits her transaction as normal. +- Alice constructs her transaction and autofills it (so that all fields, including the fee and sequence number, are included in the transaction). She adds Spencer's account and sponsorship type to the transaction as well. +- Spencer signs the transaction and provides his signature to Alice. +- Alice adds Spencer's public key and signature to her transaction. +- Alice signs and submits her transaction as normal. ### 2.3. The Sponsorship Flow (Pre-Funded) In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or reserve for the sponsee Alice's transaction, but would prefer to pre-fund the XRP necessary, so that he does not have to co-sign every single one of Alice's transactions. -* Spencer submits a transaction to initialize the sponsorship relationship and pre-fund Alice's sponsorship (note: these funds are not sent directly to Alice. She may only use the allocated funds for fees and reserves, and these are separate buckets). - * Alice does not need to do anything to accept this. -* Alice constructs her transaction and autofills it (so that all fields, including the fee and sequence number, are included in the transaction). She adds Spencer's account and sponsorship type to the transaction as well. -* Alice signs and submits her transaction as normal. +- Spencer submits a transaction to initialize the sponsorship relationship and pre-fund Alice's sponsorship (note: these funds are not sent directly to Alice. She may only use the allocated funds for fees and reserves, and these are separate buckets). + - Alice does not need to do anything to accept this. +- Alice constructs her transaction and autofills it (so that all fields, including the fee and sequence number, are included in the transaction). She adds Spencer's account and sponsorship type to the transaction as well. +- Alice signs and submits her transaction as normal. -*Note that Spencer does not need to be a part of Alice's signing and submission flow in this example.* +_Note that Spencer does not need to be a part of Alice's signing and submission flow in this example._ ### 2.4. Recouping a Sponsored Object Reserve @@ -84,8 +85,9 @@ Spencer can submit a `SponsorshipTransfer` transaction, which allows him to pass In this scenario, the sponsor, Spencer, would like to retrieve his reserve from sponsoring Alice's account. There are two ways in which he could do this: -* If Alice is done using her account, she can submit an `AccountDelete` transaction, which will send all remaining funds in the account back to Spencer. -* If Alice would like to keep using her account, or would like to switch to a different provider, she (or Spencer) can submit a `SponsorshipTransfer` transaction to either remove sponsorship or transfer it to the new provider. + +- If Alice is done using her account, she can submit an `AccountDelete` transaction, which will send all remaining funds in the account back to Spencer. +- If Alice would like to keep using her account, or would like to switch to a different provider, she (or Spencer) can submit a `SponsorshipTransfer` transaction to either remove sponsorship or transfer it to the new provider. ## 3. Ledger Entries: Common Fields @@ -93,10 +95,10 @@ There are two ways in which he could do this: As a reference, here are the fields that all ledger objects currently have: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-----------|---------------|-------------| -|`LedgerEntryType`| ✔️| ✔️|N/A|`string`|`UInt16`| -|`Flags`| ✔️| ✔️|N/A|`number`|`UInt16`| +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ----------------- | --------- | --------- | ------------- | --------- | ------------- | ----------- | +| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | +| `Flags` | ✔️ | ✔️ | N/A | `number` | `UInt16` | We propose this additional field: | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | @@ -107,34 +109,35 @@ We propose this additional field: `Sponsorship` is an object that reflects a sponsoring relationship between two accounts, `SponsorAccount` and `Sponsee`. This allows sponsors to "pre-fund" sponsees, if they so desire. -*Note: this object does not need to be created in order to sponsor accounts. It is an offered convenience, so that sponsors do not have to co-sign every sponsored transaction if they don't want to, especially for transaction fees. It also allows them to set a maximum balance even if they still want to co-sign transactions.* +_Note: this object does not need to be created in order to sponsor accounts. It is an offered convenience, so that sponsors do not have to co-sign every sponsored transaction if they don't want to, especially for transaction fees. It also allows them to set a maximum balance even if they still want to co-sign transactions._ ### 4.1. Object ID The key of the `Sponsorship` 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: -* The `Sponsorship` space key (defined during implementation) -* The `AccountID` of the `Sponsor` -* The `AccountID` of the `Sponsee` + +- The `Sponsorship` space key (defined during implementation) +- The `AccountID` of the `Sponsor` +- The `AccountID` of the `Sponsee` ### 4.2. Fields -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-----------|---------------|-------------| -|`SponsorAccount`|✔️ |✔️ | N/A |`string`|`AccountID`|The sponsor associated with this relationship. This account also pays for the reserve of this object.| -|`Sponsee`|✔️ |✔️ | N/A |`string`|`AccountID`|The sponsee associated with this relationship.| -|`SponsorNode`|✔️ |✔️| N/A |`string`|`UInt64`|A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages.| -|`SponseeNode`|✔️ |✔️| N/A |`string`|`UInt64`|A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages.| -|`FeeAmount`| | | `0` |`string`|`Amount`| The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.| -|`ReserveCount`| | | `0` |`string`|`UInt32`| The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves.| +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ---------------- | --------- | --------- | ------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `SponsorAccount` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | +| `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | +| `SponsorNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. | +| `SponseeNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. | +| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | ### 4.3. Flags There are two flags on this object: -| Flag Name | Flag Value | Modifiable? | Description | -| ----------------- | :----------: | :---------: | :------------------------------------------: | -| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| Flag Name | Flag Value | Modifiable? | Description | +| ------------------------------------- | :----------: | :---------: | :---------------------------------------------------------------------------------------------------------: | +| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | ### 4.4. Ownership @@ -150,9 +153,9 @@ This object will be deleted any time the `FeeAmount` and `ReserveCount` are both ### 4.7. Invariant Checks -* `FeeAmount` >= 0 || `ReserveCount` >= 0 -* `SponsorAccount` != `Sponsee` -* `FeeAmount` is nonnegative and denominated in XRP +- `FeeAmount` >= 0 || `ReserveCount` >= 0 +- `SponsorAccount` != `Sponsee` +- `FeeAmount` is nonnegative and denominated in XRP ### 4.8. RPC Name @@ -166,32 +169,34 @@ The `snake_case` form of the ledger object name is `sponsorship`. As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/accountroot/#accountroot-fields) are the fields that the `AccountRoot` ledger object currently has. + -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-----------|---------------|-------------| -|`Account`|✔️ |✔️ |N/A|`string`|`AccountID`|The identifying (classic) address of this account.| -|`AccountTxnID`| | |N/A|`string`|`Hash256`|The identifying hash of the transaction most recently sent by this account.| -|`AMMID`|✔️ | |N/A|`string`|`Hash256`|The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account.| -|`Balance`| | |N/A|`string`|`Amount`|The account's current XRP balance.| -|`BurnedNFTokens`| | |`0`|`number`|`UInt32`| How many total of this account's issued NFTs have been burned.| -|`Domain`| | |N/A|`string`|`Blob`|A domain associated with this account.| -|`EmailHash`| | |N/A|`string`|`Hash128`|The md5 hash of an email address.| -|`FirstNFTokenSequence`|✔️ | |N/A|`number`|`UInt32`|The account's Sequence Number at the time it minted its first non-fungible-token.| -|`LedgerEntryType`|✔️ |✔️ |N/A|`string`|`UInt16`|The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object.| -|`MessageKey`| | |N/A|`string`|`Blob`|A public key that may be used to send encrypted messages to this account.| -|`MintedNFTokens`| | |`0`|`number`|`UInt32`|How many total non-fungible tokens have been minted by/on behalf of this account.| -|`NFTokenMinter`| | |N/A|`string`|`AccountID`|Another account that can mint NFTs on behalf of this account.| -|`OwnerCount`| |✔️ |N/A|`number`|`UInt32`|The number of objects this account owns in the ledger, which contributes to its owner reserve.| -|`PreviousTxnID`| |✔️ |N/A|`string`|`Hash256`|The identifying hash of the transaction that most recently modified this object.| -|`PreviousTxnLgrSeq`| |✔️ |N/A|`number`|`UInt32`|The ledger index that contains the transaction that most recently modified this object.| -|`RegularKey`| | |N/A|`string`|`AccountID`|The address of a key pair that can be used to sign transactions for this account instead of the master key.| -|`Sequence`| |✔️ |N/A|`number`|`UInt32`|The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account.| -|`TicketCount`| | |N/A|`number`|`UInt32`|How many Tickets this account owns in the ledger.| -|`TickSize`| | |N/A|`number`|`UInt8`|[How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize)| -|`TransferRate`| | |N/A|`number`|`UInt32`|A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other.| -|`WalletLocator`| | |N/A|`string`|`Hash256`|An arbitrary 256-bit value that users can set.| -|`WalletSize`| | |N/A|`number`|`UInt32`|Unused.| +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ---------------------- | --------- | --------- | ------------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Account` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The identifying (classic) address of this account. | +| `AccountTxnID` | | | N/A | `string` | `Hash256` | The identifying hash of the transaction most recently sent by this account. | +| `AMMID` | ✔️ | | N/A | `string` | `Hash256` | The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account. | +| `Balance` | | | N/A | `string` | `Amount` | The account's current XRP balance. | +| `BurnedNFTokens` | | | `0` | `number` | `UInt32` | How many total of this account's issued NFTs have been burned. | +| `Domain` | | | N/A | `string` | `Blob` | A domain associated with this account. | +| `EmailHash` | | | N/A | `string` | `Hash128` | The md5 hash of an email address. | +| `FirstNFTokenSequence` | ✔️ | | N/A | `number` | `UInt32` | The account's Sequence Number at the time it minted its first non-fungible-token. | +| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object. | +| `MessageKey` | | | N/A | `string` | `Blob` | A public key that may be used to send encrypted messages to this account. | +| `MintedNFTokens` | | | `0` | `number` | `UInt32` | How many total non-fungible tokens have been minted by/on behalf of this account. | +| `NFTokenMinter` | | | N/A | `string` | `AccountID` | Another account that can mint NFTs on behalf of this account. | +| `OwnerCount` | | ✔️ | N/A | `number` | `UInt32` | The number of objects this account owns in the ledger, which contributes to its owner reserve. | +| `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this object. | +| `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | +| `RegularKey` | | | N/A | `string` | `AccountID` | The address of a key pair that can be used to sign transactions for this account instead of the master key. | +| `Sequence` | | ✔️ | N/A | `number` | `UInt32` | The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account. | +| `TicketCount` | | | N/A | `number` | `UInt32` | How many Tickets this account owns in the ledger. | +| `TickSize` | | | N/A | `number` | `UInt8` | [How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize) | +| `TransferRate` | | | N/A | `number` | `UInt32` | A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other. | +| `WalletLocator` | | | N/A | `string` | `Hash256` | An arbitrary 256-bit value that users can set. | +| `WalletSize` | | | N/A | `number` | `UInt32` | Unused. | + We propose these additional fields: @@ -208,21 +213,23 @@ The `SponsorAccount` field is already added in the ledger common fields (see sec This field is included if the account was created with a sponsor paying its account reserve. If this sponsored account is deleted, the destination of the `AccountDelete` transaction must equal `SponsorAccount`, so that the sponsor can recoup their fees. -*Note: The `Destination` field of `AccountDelete` will still work as-is if the account is not sponsored, where it can be set to any account.* +_Note: The `Destination` field of `AccountDelete` will still work as-is if the account is not sponsored, where it can be set to any account._ ### 5.2. Account Reserve Calculation The existing reserve calculation is: -$$ acctReserve + objReserve * acct.OwnerCount $$ +$$ acctReserve + objReserve \* acct.OwnerCount $$ The total account reserve should now be calculated as: -$$ \displaylines{ +$$ +\displaylines{ (acct.SponsorAccount \text{ ? } 0 : acctReserve) + \\ objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + \\ acctReserve * acct.SponsoringAccountCount -} $$ +} +$$ ## 6. Transactions: Common Fields @@ -234,9 +241,9 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/co We propose these modifications: -| Field Name | Required? | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|---------------| -|`Sponsor`| |`object`|`STObject`| This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. +| Field Name | Required? | JSON Type | Internal Type | Description | +| ---------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | #### 6.1.1. `Sponsor` @@ -244,13 +251,13 @@ The `Sponsor` inner object contains all of the information for the sponsorship h The fields contained in this object are: -| Field Name | Required? | JSON Type | Internal Type | -|------------|-----------|-----------|---------------| -|`SponsorAccount`|✔️|`string`|`AccountID`| The sponsoring account.| -|`Flags`| ✔️|`number`|`UInt16`| Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve).| -|`SigningPubKey`| |`string`|`STBlob`|The `SigningPubKey` for `SponsorAccount`, if single-signing.| -|`Signature`| |`string`|`STBlob`| A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`.| -|`Signers`| |`array`|`STArray`|An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`.| +| Field Name | Required? | JSON Type | Internal Type | +| ---------------- | --------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsoring account. | +| `Flags` | ✔️ | `number` | `UInt16` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | +| `SigningPubKey` | | `string` | `STBlob` | The `SigningPubKey` for `SponsorAccount`, if single-signing. | +| `Signature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | +| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | ##### 6.1.1.1. `Account` @@ -264,9 +271,9 @@ The `Flags` field allows the user to specify which sponsorship type(s) they wish There are two flag values that are supported: -| Flag Name | Flag Value | Description | -| ----------------- | :----------: | :------------------------------------------: | -| `tfSponsorFee` | `0x00000001` | Sponsoring (paying for) the fee of the transaction. | +| Flag Name | Flag Value | Description | +| ------------------ | :----------: | :----------------------------------------------------------------: | +| `tfSponsorFee` | `0x00000001` | Sponsoring (paying for) the fee of the transaction. | | `tfSponsorReserve` | `0x00000002` | Sponsoring the reserve for any objects created in the transaction. | This field **will** be a signing field (it will be included in transaction signatures). @@ -290,32 +297,39 @@ Either `SigningPubKey`+`Signature` or `Signers` must be included in the transact ### 6.3. Failure Conditions #### 6.3.1. General Failures -* `Sponsor.Signature` is invalid. -* `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). -* The `SponsorAccount` doesn't exist on the ledger. -* An invalid sponsorship flag is used. -* `Sponsor.SigningPubKey`, `Sponsor.Signature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). + +- `Sponsor.Signature` is invalid. +- `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). +- The `SponsorAccount` doesn't exist on the ledger. +- An invalid sponsorship flag is used. +- `Sponsor.SigningPubKey`, `Sponsor.Signature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). #### 6.3.2. Fee Sponsorship Failures -* The sponsor does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) + +- The sponsor does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) If a `Sponsorship` object exists: -* The `lsfRequireSignatureForFee` flag is enabled and there is no sponsor signature included. -* There is not enough XRP in the `FeeAmount` to pay for the transaction. + +- The `lsfRequireSignatureForFee` flag is enabled and there is no sponsor signature included. +- There is not enough XRP in the `FeeAmount` to pay for the transaction. If a `Sponsorship` object does not exist: -* There is no sponsor signature included. + +- There is no sponsor signature included. #### 6.3.3. Reserve Sponsorship Failures -* The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) -* The transaction does not support reserve sponsorship (see section 6.3.4) + +- The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) +- The transaction does not support reserve sponsorship (see section 6.3.4) If a `Sponsorship` object exists: -* The `lsfRequireSignatureForReserve` flag is enabled and there is no sponsor signature included. -* There is not enough remaining count in the `ReserveCount` to pay for the transaction. + +- The `lsfRequireSignatureForReserve` flag is enabled and there is no sponsor signature included. +- There is not enough remaining count in the `ReserveCount` to pay for the transaction. If a `Sponsorship` object does not exist: -* There is no sponsor signature included. + +- There is no sponsor signature included. #### 6.3.4. Transactions that cannot be sponsored @@ -323,21 +337,23 @@ All transactions (other than pseudo-transactions) may use the `tfSponsorFee` fla However, some transactions will not support the `tfSponsorReserve` flag. -* [`Batch` transactions](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch) - * `Batch` does not create any objects on its own, and therefore its use in the outer transaction would be confusing, as users may think that that means that all inner transactions are sponsored. The inner transactions should use `tfSponsorReserve` instead. -* All pseudo-transactions (currently `EnableAmendment`, `SetFee`, and `UNLModify`) - * The reserves for those objects are covered by the network, not by any one account. +- [`Batch` transactions](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch) + - `Batch` does not create any objects on its own, and therefore its use in the outer transaction would be confusing, as users may think that that means that all inner transactions are sponsored. The inner transactions should use `tfSponsorReserve` instead. +- All pseudo-transactions (currently `EnableAmendment`, `SetFee`, and `UNLModify`) + - The reserves for those objects are covered by the network, not by any one account. Also, many transactions, such as `AccountSet`, will have no change in output when using the `tfSponsorReserve` flag, if they do not create any new objects or accounts. ### 6.4. State Changes #### 6.4.1. Fee Sponsorship State Changes + If a `Sponsorship` object exists, the `tx.Fee` value is decremented from the `Sponsorship.FeeAmount`. If a `Sponsorship` object does not exist, the `tx.Fee` value is decremented from the sponsor's `AccountRoot.Balance`. #### 6.4.2. Reserve Sponsorship State Changes + Any account/object that is created as a part of the transaction will have a `Sponsor` field. The sponsor's `SponsoringOwnerCount` field will be incremented by the number of objects that are sponsored as a part of the transaction, and the `SponsoringAccountCount` field will be incremented by the number of new accounts that are sponsored as a part of the transaction. @@ -352,38 +368,39 @@ This transaction creates and updates the `Sponsorship` object. ### 7.1. Fields -| Field Name | Required? | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-------------| -|`SponsorAccount`|✔️ |`string`|`AccountID`|The sponsor associated with this relationship. This account also pays for the reserve of this object.| -|`Sponsee`|✔️ |`string`|`AccountID`|The sponsee associated with this relationship.| -|`FeeAmount`| |`string`|`Amount`| The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.| -|`ReserveCount`| |`number`|`UInt32`| The (remaining) amount of reserves that the sponsor has provided for the sponsee to use.| +| Field Name | Required? | JSON Type | Internal Type | Description | +| ---------------- | --------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------- | +| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | +| `Sponsee` | ✔️ | `string` | `AccountID` | The sponsee associated with this relationship. | +| `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | ### 7.2. Flags -| Flag Name | Flag Value | Description | -| --------- | ---------- | ----------- | -| `tfSponsorshipSetRequireSignForFee` | `0x00010000` | Adds the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfSponsorshipClearRequireSignForFee` | `0x00020000` | Removes the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfSponsorshipSetRequireSignForReserve` | `0x00040000` | Adds the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfDeleteObject` | `0x00100000` | Removes the ledger object. | +| Flag Name | Flag Value | Description | +| ----------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------- | +| `tfSponsorshipSetRequireSignForFee` | `0x00010000` | Adds the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipClearRequireSignForFee` | `0x00020000` | Removes the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipSetRequireSignForReserve` | `0x00040000` | Adds the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfDeleteObject` | `0x00100000` | Removes the ledger object. | ### 7.2. Failure Conditions -* `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` -* If `tfDeleteObject` is provided: - * `FeeAmount` is specified - * `ReserveCount` is specified - * `tfSponsorshipSetRequireSignForFee` is enabled - * `tfSponsorshipSetRequireSignForReserve` is enabled + +- `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` +- If `tfDeleteObject` is provided: + - `FeeAmount` is specified + - `ReserveCount` is specified + - `tfSponsorshipSetRequireSignForFee` is enabled + - `tfSponsorshipSetRequireSignForReserve` is enabled ### 7.3. State Changes -* If the object already exists, `Sponsorship.Amount += tx.FeeAmount` and `Sponsorship.ReserveCount += tx.ReserveCount`. -* If the object doesn't exist, it will be created. -* If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. - * Both sponsor and sponsee can delete the object. - * Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process. +- If the object already exists, `Sponsorship.Amount += tx.FeeAmount` and `Sponsorship.ReserveCount += tx.ReserveCount`. +- If the object doesn't exist, it will be created. +- If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. + - Both sponsor and sponsee can delete the object. + - Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process. ## 8. Transaction: `SponsorshipTransfer` @@ -391,12 +408,12 @@ This transaction transfers a sponsor relationship for a particular ledger object ### 8.1. Fields -| Field Name | Required? | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-------------| -|`TransactionType`| ✔️|`string`|`UInt16`| -|`Account`|✔️|`string`|`AccountID`| -|`ObjectID`| |`string`|`UInt256`| -|`Sponsor`| |`object`|`STObject`| +| Field Name | Required? | JSON Type | Internal Type | Description | +| ----------------- | --------- | --------- | ------------- | ----------- | +| `TransactionType` | ✔️ | `string` | `UInt16` | +| `Account` | ✔️ | `string` | `AccountID` | +| `ObjectID` | | `string` | `UInt256` | +| `Sponsor` | | `object` | `STObject` | #### 8.1.1. `ObjectID` @@ -428,17 +445,17 @@ The sponsor will likely only rarely want to do this (such as if they are transfe ### 8.4. Failure Conditions -* If transferring the sponsorship, the new sponsor does not have enough reserve for this object/account. -* If dissolving the sponsorship, the owner does not have enough reserve for this object/account. -* The new sponsor does not exist. -* The `tx.Account` neither the sponsor nor the owner of `ObjectID`. +- If transferring the sponsorship, the new sponsor does not have enough reserve for this object/account. +- If dissolving the sponsorship, the owner does not have enough reserve for this object/account. +- The new sponsor does not exist. +- The `tx.Account` neither the sponsor nor the owner of `ObjectID`. ### 8.5. State Changes -* The `Sponsor` field on the object is changed or deleted. -* The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. -* The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. -* If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. +- The `Sponsor` field on the object is changed or deleted. +- The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. +- The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. +- If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. ## 9. Transaction: `AccountDelete` @@ -451,7 +468,8 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/ty Existing failure conditions still apply. If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` field: -* The `Destination` is not equal to `AccountRoot.SponsorAccount`. + +- The `Destination` is not equal to `AccountRoot.SponsorAccount`. If the `AccountRoot` associated with the `tx.Account` has a `SponsoringOwnerCount` or `SponsoringAccountCount` field, the transaction will fail with `tecHAS_OBLIGATIONS`. @@ -477,21 +495,21 @@ This delegatable granular permission allows an account to sponsor reserves on be The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts: -| Field Name | Required? | JSON Type | Description | -|------------|-----------|-----------|-------------| -|`account`|✔️|`string`|Get ledger entries associated with this account.| -|`deletion_blockers_only`| |`boolean`|If `true`, only return ledger entries that would block this account from being deleted. The default is `false`. | -|`ledger_hash`| |`string`|The unique hash of the ledger version to use.| -|`ledger_index`| |`number` or `string`|The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically.| -|`limit`| |`number`|The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`.| -|`marker`| |`any`|Value from a previous paginated response. Resume retrieving data where that response left off.| -|`type`| |`string`|Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types.| +| Field Name | Required? | JSON Type | Description | +| ------------------------ | --------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `account` | ✔️ | `string` | Get ledger entries associated with this account. | +| `deletion_blockers_only` | | `boolean` | If `true`, only return ledger entries that would block this account from being deleted. The default is `false`. | +| `ledger_hash` | | `string` | The unique hash of the ledger version to use. | +| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | +| `limit` | | `number` | The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`. | +| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | +| `type` | | `string` | Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types. | We propose this additional field: -| Field Name | Required? | JSON Type | Description | -|------------|-----------|-----------|-------------| -|`sponsored`| |`boolean`|If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | +| Field Name | Required? | JSON Type | Description | +| ----------- | --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | ### 12.2. Response Fields @@ -503,30 +521,30 @@ The `account_sponsoring` RPC method is used to fetch a list of objects that an a ### 13.1. Request Fields -| Field Name | Required? | JSON Type | Description | -|------------|-----------|-----------|-------------| -|`account`|✔️|`string`|The sponsor in question.| -|`deletion_blockers_only`| |`boolean`|If `true`, the response only includes objects that would block this account from being deleted. The default is `false`.| -|`ledger_hash`| |`string`|A hash representing the ledger version to use.| -|`ledger_index`| |`number` or `string`|The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically.| -|`limit`| |`number`|The maximum number of objects to include in the results.| -|`marker`| |`any`|Value from a previous paginated response. Resume retrieving data where that response left off.| -|`type`| |`string`|Filter results by a ledger entry type. Some examples are `offer` and `escrow`.| +| Field Name | Required? | JSON Type | Description | +| ------------------------ | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `account` | ✔️ | `string` | The sponsor in question. | +| `deletion_blockers_only` | | `boolean` | If `true`, the response only includes objects that would block this account from being deleted. The default is `false`. | +| `ledger_hash` | | `string` | A hash representing the ledger version to use. | +| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | +| `limit` | | `number` | The maximum number of objects to include in the results. | +| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | +| `type` | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`. | ### 13.2. Response Fields The response fields are nearly identical to `account_objects`. -| Field Name | Always Present? | JSON Type | Description | -|------------|-----------|-----------|-------------| -|`account`|✔️|`string`|The account this request corresponds to.| -|`sponsored_objects`|✔️|`array`|Array of ledger entries in this account's owner directory. This includes entries that are owned by this account and entries that are linked to this account but owned by someone else, such as escrows where this account is the destination. Each member is a ledger entry in its raw ledger format. This may contain fewer entries than the maximum specified in the `limit` field.| -|`ledger_hash`| |`string`|The identifying hash of the ledger that was used to generate this response.| -|`ledger_index`| |`number`|The ledger index of the ledger that was used to generate this response.| -|`ledger_current_index`| |`number`|The ledger index of the open ledger that was used to generate this response.| -|`limit`| |`number`|The limit that was used in this request, if any.| -|`marker`| |`any`|Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one.| -|`validated`| |`boolean`|If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change.| +| Field Name | Always Present? | JSON Type | Description | +| ---------------------- | --------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `account` | ✔️ | `string` | The account this request corresponds to. | +| `sponsored_objects` | ✔️ | `array` | Array of ledger entries in this account's owner directory. This includes entries that are owned by this account and entries that are linked to this account but owned by someone else, such as escrows where this account is the destination. Each member is a ledger entry in its raw ledger format. This may contain fewer entries than the maximum specified in the `limit` field. | +| `ledger_hash` | | `string` | The identifying hash of the ledger that was used to generate this response. | +| `ledger_index` | | `number` | The ledger index of the ledger that was used to generate this response. | +| `ledger_current_index` | | `number` | The ledger index of the open ledger that was used to generate this response. | +| `limit` | | `number` | The limit that was used in this request, if any. | +| `marker` | | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. | +| `validated` | | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change. | ## 14. Security @@ -536,7 +554,7 @@ Both the sponsee _and_ the sponsor must agree to enter into a sponsor relationsh A sponsor will never be stuck sponsoring an sponsee's account or object it doesn't want to support anymore, because it can submit a `SponsorshipTransfer` transaction at any point. -The sponsor's signature must *always* include the `Account` and `Sequence` fields, to prevent signature replay attacks (where the sponsor's signature can be reused to sponsor an object or account that they did not want to sponsor). +The sponsor's signature must _always_ include the `Account` and `Sequence` fields, to prevent signature replay attacks (where the sponsor's signature can be reused to sponsor an object or account that they did not want to sponsor). When sponsoring transaction fees, the sponsor must approve of the `Fee` value of the transaction, since that is the amount that they will be paying. @@ -544,7 +562,7 @@ When sponsoring reserves, the sponsor's signature must include any aspects of th A sponsee cannot take advantage of the generosity of their sponsor, since the sponsor must sign every transaction it wants to sponsor the ledger objects for. A sponsee also must not be able to change the sponsorship type that the sponsor is willing to engage in, as this could lock up to 500 of the sponsor's XRP (in the case of 250 tickets being created in one `TicketCreate` transaction). -An axiom that is out of scope: the sponsee *will not* have any control over a sponsorship transfer. This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency. +An axiom that is out of scope: the sponsee _will not_ have any control over a sponsorship transfer. This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency. ### 14.2. Signatures @@ -557,12 +575,14 @@ A sponsor's `Signature` cannot be replayed or attached to a different transactio An [invariant](https://xrpl.org/docs/concepts/consensus-protocol/invariant-checking/) is a statement, usually an equation, that must always be true for every valid ledger state on the XRPL. Invariant checks serve as a last line of defense against bugs; the `tecINVARIANT_FAILED` error is thrown if an invariant is violated (which ideally should never happen). ### 15.1. Tracking Owner Counts + A transaction that creates a ledger object either increments an account's `OwnerCount` by 1 or increments two separate accounts' `SponsoringOwnerCount` and `SponsoredOwnerCount` by 1. The opposite happens when a ledger object is deleted. The equivalent also should happen with `SponsoringAccountCount`. ### 15.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount` -$$ \sum_{accounts} Account.SponsoredOwnerCount = \sum_{accounts} Account.SponsoringOwnerCount $$ + +$$ \sum*{accounts} Account.SponsoredOwnerCount = \sum*{accounts} Account.SponsoringOwnerCount $$ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to the sum of all accounts' `SponsoringOwnerCount`s. This ensures that every sponsored object is logged as being sponsored and also has a sponsor. @@ -592,6 +612,7 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t } } ``` + #### 16.1.2. The Signed Transaction @@ -616,6 +637,7 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t TxnSignature: "3045022100F2AAF90D8F9BB6C94C0C95BA31E320FC601C7BAFFF536CC07076A2833CB4C7FF02203F3C76EB34ABAD61A71CEBD42307169CDA65D9B3CA0EEE871210BEAB824E524B" } ``` + ### 16.2. Account Sponsorship @@ -640,6 +662,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp } } ``` + #### 16.2.2. The Signed Transaction @@ -664,6 +687,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp TxnSignature: "30440220245217F931FDA0C5E68B935ABB4920211D5B6182878583124DE4663B19F00BEC022070BE036264760551CF40E9DAFC8B84036FA70E7EE7257BB7E39AEB7354B2EB86" } ``` + ### 16.3. Object Sponsorship @@ -685,6 +709,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp } } ``` + #### 16.3.2. The Signed Transaction @@ -708,6 +733,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp TxnSignature: "3044022047CB72DA297B067C0E69045B7828AD660F8198A6FA03982E31CB6D27F0946DDE022055844EB63E3BFF7D9ABFB26645AA4D2502E143F4ABEE2DE57EB87A1E5426E010" } ``` + ## 17. Rationale @@ -716,24 +742,24 @@ The primary motivation for this design is to enable companies, token issuers, an ## n+1. Remaining TODOs/Open Questions -* Do I need a new type of directory nodes to keep track of sponsored objects? - * Clio could perhaps solve this problem -* How will this work for objects like trustlines, where multiple accounts might be holding reserves for it? - * Maybe a second `Sponsor` field or something? -* How do we handle account creation? The actual account owner's signing keys aren't involved in that at all... Maybe just a new flag on the payment saying you'll pay the reserve for the account? -* Should it be `ReserveCount` or `ReserveAmount`? - * If `ReserveCount`, it can be decremented to keep track of how much is left. - * Pro: easier to reason about how much you're giving someone. - * Con: sponsors have to update the number if reserves increase and your cost-benefit analysis changes. - * If `ReserveAmount`, you'd need a separate "`OwnerCount`" field to keep track of how many objects the thing owns. And probably a flag for whether the account itself is sponsored. - * Pro: people are used to thinking in terms of XRP. - * Con: rippled math might get more complicated. You have to keep track of potential reserve sponsorships in two places (`Sponsorship` and `AccountRoot`) -* Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve? -* Should there be a "max XRP per transaction" field in `Sponsorship`? -* If a transaction doesn't take a fee (first `SetRegularKey`) or doesn't increase reserve (e.g. `AccountDelete`), and is sponsored, should that transaction fail or succeed. -* Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? -* Should we allow sponsorship of creating another account? e.g. Account A is sponsored by Sponsor, A creates B, does Sponsor also sponsor B or does this fail if A doesn't have the funds to create B? -* Should `account_sponsoring` be Clio-only? +- Do I need a new type of directory nodes to keep track of sponsored objects? + - Clio could perhaps solve this problem +- How will this work for objects like trustlines, where multiple accounts might be holding reserves for it? + - Maybe a second `Sponsor` field or something? +- How do we handle account creation? The actual account owner's signing keys aren't involved in that at all... Maybe just a new flag on the payment saying you'll pay the reserve for the account? +- Should it be `ReserveCount` or `ReserveAmount`? + - If `ReserveCount`, it can be decremented to keep track of how much is left. + - Pro: easier to reason about how much you're giving someone. + - Con: sponsors have to update the number if reserves increase and your cost-benefit analysis changes. + - If `ReserveAmount`, you'd need a separate "`OwnerCount`" field to keep track of how many objects the thing owns. And probably a flag for whether the account itself is sponsored. + - Pro: people are used to thinking in terms of XRP. + - Con: rippled math might get more complicated. You have to keep track of potential reserve sponsorships in two places (`Sponsorship` and `AccountRoot`) +- Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve? +- Should there be a "max XRP per transaction" field in `Sponsorship`? +- If a transaction doesn't take a fee (first `SetRegularKey`) or doesn't increase reserve (e.g. `AccountDelete`), and is sponsored, should that transaction fail or succeed. +- Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? +- Should we allow sponsorship of creating another account? e.g. Account A is sponsored by Sponsor, A creates B, does Sponsor also sponsor B or does this fail if A doesn't have the funds to create B? +- Should `account_sponsoring` be Clio-only? # Appendix @@ -797,9 +823,9 @@ See Appendix B for the alternate designs that were considered and why this one w ### A.14: How is this account sponsorship model different from/better than [XLS-23d, Lite Accounts](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0023-lite-accounts)? -* Sponsored accounts do not have any restrictions, and can hold objects. -* Sponsored accounts require the same reserve as a normal account (this was one of the objections to the Lite Account proposal). -* Lite accounts can be deleted by their sponsor. +- Sponsored accounts do not have any restrictions, and can hold objects. +- Sponsored accounts require the same reserve as a normal account (this was one of the objections to the Lite Account proposal). +- Lite accounts can be deleted by their sponsor. ### A.15: How will this work for objects like trustlines, where multiple accounts might be holding reserves for it? @@ -825,7 +851,7 @@ The current design also supports having different sponsors for different objects There would be a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56d](https://github.com/XRPLF/XRPL-Standards/discussions/162), that the sponsor would sign. It would contain a sub-transaction from the sponsee. -It would look something like this: +It would look something like this: |FieldName | Required? | JSON Type | Internal Type | |:---------|:-----------|:---------------|:------------| |`TransactionType`|✔️|`string`|`UInt16`| @@ -842,3 +868,4 @@ In addition, the signing process becomes complicated (as discovered in the proce The rough idea of this design was to have a new set of transactions (e.g. `SponsorCreate`/`SponsorAccept`/`SponsorCancel`/`SponsorFinish`) where a sponsor could take on the reserve for an existing object. This design was never seriously considered, as it felt too complicated and introduced several new transactions. It also doesn't support adding a sponsor to the object at object creation time, which is a much smoother UX and never requires the owner/sponsee to hold enough XRP for the reserve. + From c93bbd5cef6938e039b7b3b0a1a7d5cd094d4ec3 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 10 Sep 2025 21:32:37 -0400 Subject: [PATCH 06/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ From bca8183dedb6991b411a151c91eea9162fb51132 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 10 Sep 2025 21:33:35 -0400 Subject: [PATCH 07/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- XLS-0068-sponsored-fees-and-reserves/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index f97fc5648..129f87c7f 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -868,4 +868,3 @@ In addition, the signing process becomes complicated (as discovered in the proce The rough idea of this design was to have a new set of transactions (e.g. `SponsorCreate`/`SponsorAccept`/`SponsorCancel`/`SponsorFinish`) where a sponsor could take on the reserve for an existing object. This design was never seriously considered, as it felt too complicated and introduced several new transactions. It also doesn't support adding a sponsor to the object at object creation time, which is a much smoother UX and never requires the owner/sponsee to hold enough XRP for the reserve. - From bf18cfa23ae8a91d07a2ca54f5cf1e1deeb9373a Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 10 Sep 2025 21:45:19 -0400 Subject: [PATCH 08/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- .../README.md | 217 +++++++++--------- 1 file changed, 109 insertions(+), 108 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 129f87c7f..0747fcf7c 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -1,3 +1,4 @@ +
   xls: 68
   title: Sponsored Fees and Reserves
@@ -95,14 +96,14 @@ There are two ways in which he could do this:
 
 As a reference, here are the fields that all ledger objects currently have:
 
-| Field Name        | Constant? | Required? | Default Value | JSON Type | Internal Type | Description |
-| ----------------- | --------- | --------- | ------------- | --------- | ------------- | ----------- |
-| `LedgerEntryType` | ✔️        | ✔️        | N/A           | `string`  | `UInt16`      |
-| `Flags`           | ✔️        | ✔️        | N/A           | `number`  | `UInt16`      |
+| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description |
+| ---------- | --------- | --------- | ------------- | ---------- | ------------- | ----------- |
+| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string`  | `UInt16`  |
+| `Flags` | ✔️ | ✔️ | N/A | `number`  | `UInt16`  |
 
 We propose this additional field:
 | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description |
-|------------|-----------|-----------|---------------|-----------|---------------|-------------|
+|------------|-----------|-----------|---------------|------------|---------------|-------------|
 |`SponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the reserve for this ledger object. |
 
 ## 4. Ledger Entry: `Sponsorship`
@@ -121,23 +122,23 @@ The key of the `Sponsorship` object is the result of [`SHA512-Half`](https://xrp
 
 ### 4.2. Fields
 
-| Field Name       | Constant? | Required? | Default Value | JSON Type | Internal Type | Description                                                                                                                           |
-| ---------------- | --------- | --------- | ------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
-| `SponsorAccount` | ✔️        | ✔️        | N/A           | `string`  | `AccountID`   | The sponsor associated with this relationship. This account also pays for the reserve of this object.                                 |
-| `Sponsee`        | ✔️        | ✔️        | N/A           | `string`  | `AccountID`   | The sponsee associated with this relationship.                                                                                        |
-| `SponsorNode`    | ✔️        | ✔️        | N/A           | `string`  | `UInt64`      | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. |
-| `SponseeNode`    | ✔️        | ✔️        | N/A           | `string`  | `UInt64`      | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. |
-| `FeeAmount`      |           |           | `0`           | `string`  | `Amount`      | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.                                          |
-| `ReserveCount`   |           |           | `0`           | `string`  | `UInt32`      | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves.                             |
+| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description |
+| ---------- | --------- | --------- | ------------- | --------- | ------------- | ------------ |
+| `SponsorAccount` | ✔️ | ✔️ | N/A | `string`  | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. |
+| `Sponsee` | ✔️ | ✔️ | N/A | `string`  | `AccountID` | The sponsee associated with this relationship. |
+| `SponsorNode`  | ✔️ | ✔️ | N/A | `string`  | `UInt64`  | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. |
+| `SponseeNode`  | ✔️ | ✔️ | N/A | `string`  | `UInt64`  | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. |
+| `FeeAmount`  | | | `0` | `string`  | `Amount`  | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.  |
+| `ReserveCount` | | | `0` | `string`  | `UInt32`  | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves.  |
 
 ### 4.3. Flags
 
 There are two flags on this object:
 
-| Flag Name                             |  Flag Value  | Modifiable? |                                                 Description                                                 |
-| ------------------------------------- | :----------: | :---------: | :---------------------------------------------------------------------------------------------------------: |
-| `lsfSponsorshipRequireSignForFee`     | `0x00010000` |     Yes     | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. |
-| `lsfSponsorshipRequireSignForReserve` | `0x00020000` |     Yes     | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. |
+| Flag Name |  Flag Value  | Modifiable? | Description |
+| --------- | ------------ | ----------- | ----------- |
+| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. |
+| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. |
 
 ### 4.4. Ownership
 
@@ -172,30 +173,30 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/led
 
 
 
-| Field Name             | Constant? | Required? | Default Value | JSON Type | Internal Type | Description                                                                                                                                                           |
-| ---------------------- | --------- | --------- | ------------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `Account`              | ✔️        | ✔️        | N/A           | `string`  | `AccountID`   | The identifying (classic) address of this account.                                                                                                                    |
-| `AccountTxnID`         |           |           | N/A           | `string`  | `Hash256`     | The identifying hash of the transaction most recently sent by this account.                                                                                           |
-| `AMMID`                | ✔️        |           | N/A           | `string`  | `Hash256`     | The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account.                                                                          |
-| `Balance`              |           |           | N/A           | `string`  | `Amount`      | The account's current XRP balance.                                                                                                                                    |
-| `BurnedNFTokens`       |           |           | `0`           | `number`  | `UInt32`      | How many total of this account's issued NFTs have been burned.                                                                                                        |
-| `Domain`               |           |           | N/A           | `string`  | `Blob`        | A domain associated with this account.                                                                                                                                |
-| `EmailHash`            |           |           | N/A           | `string`  | `Hash128`     | The md5 hash of an email address.                                                                                                                                     |
-| `FirstNFTokenSequence` | ✔️        |           | N/A           | `number`  | `UInt32`      | The account's Sequence Number at the time it minted its first non-fungible-token.                                                                                     |
-| `LedgerEntryType`      | ✔️        | ✔️        | N/A           | `string`  | `UInt16`      | The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object.                                                               |
-| `MessageKey`           |           |           | N/A           | `string`  | `Blob`        | A public key that may be used to send encrypted messages to this account.                                                                                             |
-| `MintedNFTokens`       |           |           | `0`           | `number`  | `UInt32`      | How many total non-fungible tokens have been minted by/on behalf of this account.                                                                                     |
-| `NFTokenMinter`        |           |           | N/A           | `string`  | `AccountID`   | Another account that can mint NFTs on behalf of this account.                                                                                                         |
-| `OwnerCount`           |           | ✔️        | N/A           | `number`  | `UInt32`      | The number of objects this account owns in the ledger, which contributes to its owner reserve.                                                                        |
-| `PreviousTxnID`        |           | ✔️        | N/A           | `string`  | `Hash256`     | The identifying hash of the transaction that most recently modified this object.                                                                                      |
-| `PreviousTxnLgrSeq`    |           | ✔️        | N/A           | `number`  | `UInt32`      | The ledger index that contains the transaction that most recently modified this object.                                                                               |
-| `RegularKey`           |           |           | N/A           | `string`  | `AccountID`   | The address of a key pair that can be used to sign transactions for this account instead of the master key.                                                           |
-| `Sequence`             |           | ✔️        | N/A           | `number`  | `UInt32`      | The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account.        |
-| `TicketCount`          |           |           | N/A           | `number`  | `UInt32`      | How many Tickets this account owns in the ledger.                                                                                                                     |
-| `TickSize`             |           |           | N/A           | `number`  | `UInt8`       | [How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize) |
-| `TransferRate`         |           |           | N/A           | `number`  | `UInt32`      | A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other.               |
-| `WalletLocator`        |           |           | N/A           | `string`  | `Hash256`     | An arbitrary 256-bit value that users can set.                                                                                                                        |
-| `WalletSize`           |           |           | N/A           | `number`  | `UInt32`      | Unused.                                                                                                                                                               |
+| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description |
+| ---------- | --------- | --------- | ------------- | --------- | ------------- | ------------ |
+| `Account` | ✔️ | ✔️ | N/A | `string`  | `AccountID` | The identifying (classic) address of this account.  |
+| `AccountTxnID` | | | N/A | `string`  | `Hash256` | The identifying hash of the transaction most recently sent by this account. |
+| `AMMID` | ✔️ | | N/A | `string`  | `Hash256` | The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account. |
+| `Balance` | | | N/A | `string`  | `Amount`  | The account's current XRP balance. |
+| `BurnedNFTokens` | | | `0` | `number`  | `UInt32`  | How many total of this account's issued NFTs have been burned.  |
+| `Domain`  | | | N/A | `string`  | `Blob` | A domain associated with this account. |
+| `EmailHash`  | | | N/A | `string`  | `Hash128` | The md5 hash of an email address.  |
+| `FirstNFTokenSequence` | ✔️ | | N/A | `number`  | `UInt32`  | The account's Sequence Number at the time it minted its first non-fungible-token.  |
+| `LedgerEntryType`  | ✔️ | ✔️ | N/A | `string`  | `UInt16`  | The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object. |
+| `MessageKey` | | | N/A | `string`  | `Blob` | A public key that may be used to send encrypted messages to this account. |
+| `MintedNFTokens` | | | `0` | `number`  | `UInt32`  | How many total non-fungible tokens have been minted by/on behalf of this account.  |
+| `NFTokenMinter` | | | N/A | `string`  | `AccountID` | Another account that can mint NFTs on behalf of this account. |
+| `OwnerCount` | | ✔️ | N/A | `number`  | `UInt32`  | The number of objects this account owns in the ledger, which contributes to its owner reserve. |
+| `PreviousTxnID` | | ✔️ | N/A | `string`  | `Hash256` | The identifying hash of the transaction that most recently modified this object. |
+| `PreviousTxnLgrSeq`  | | ✔️ | N/A | `number`  | `UInt32`  | The ledger index that contains the transaction that most recently modified this object.  |
+| `RegularKey` | | | N/A | `string`  | `AccountID` | The address of a key pair that can be used to sign transactions for this account instead of the master key. |
+| `Sequence` | | ✔️ | N/A | `number`  | `UInt32`  | The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account. |
+| `TicketCount`  | | | N/A | `number`  | `UInt32`  | How many Tickets this account owns in the ledger. |
+| `TickSize` | | | N/A | `number`  | `UInt8` | [How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize) |
+| `TransferRate` | | | N/A | `number`  | `UInt32`  | A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other.  |
+| `WalletLocator` | | | N/A | `string`  | `Hash256` | An arbitrary 256-bit value that users can set. |
+| `WalletSize` | | | N/A | `number`  | `UInt32`  | Unused. |
 
 
 
@@ -241,9 +242,9 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/co
 
 We propose these modifications:
 
-| Field Name | Required? | JSON Type | Internal Type | Description                                                                                                                                                   |
-| ---------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `Sponsor`  |           | `object`  | `STObject`    | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. |
+| Field Name | Required? | JSON Type | Internal Type | Description |
+| ---------- | --------- | --------- | ------------- | ------------ |
+| `Sponsor`  | | `object`  | `STObject`  | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. |
 
 #### 6.1.1. `Sponsor`
 
@@ -251,13 +252,13 @@ The `Sponsor` inner object contains all of the information for the sponsorship h
 
 The fields contained in this object are:
 
-| Field Name       | Required? | JSON Type | Internal Type |
-| ---------------- | --------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `SponsorAccount` | ✔️        | `string`  | `AccountID`   | The sponsoring account.                                                                                                                                                                                                                            |
-| `Flags`          | ✔️        | `number`  | `UInt16`      | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve).                                                                                                                                                           |
-| `SigningPubKey`  |           | `string`  | `STBlob`      | The `SigningPubKey` for `SponsorAccount`, if single-signing.                                                                                                                                                                                       |
-| `Signature`      |           | `string`  | `STBlob`      | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`.                  |
-| `Signers`        |           | `array`   | `STArray`     | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. |
+| Field Name | Required? | JSON Type | Internal Type | Description |
+| ---------- | --------- | --------- | ------------- | ------------ |
+| `SponsorAccount` | ✔️ | `string`  | `AccountID` | The sponsoring account. |
+| `Flags`  | ✔️ | `number`  | `UInt16`  | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). |
+| `SigningPubKey`  | | `string`  | `STBlob`  | The `SigningPubKey` for `SponsorAccount`, if single-signing.  |
+| `Signature`  | | `string`  | `STBlob`  | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. |
+| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. |
 
 ##### 6.1.1.1. `Account`
 
@@ -271,9 +272,9 @@ The `Flags` field allows the user to specify which sponsorship type(s) they wish
 
 There are two flag values that are supported:
 
-| Flag Name          |  Flag Value  |                            Description                             |
-| ------------------ | :----------: | :----------------------------------------------------------------: |
-| `tfSponsorFee`     | `0x00000001` |        Sponsoring (paying for) the fee of the transaction.         |
+| Flag Name  |  Flag Value  | Description  |
+| ---------- | ------------ | ------------ |
+| `tfSponsorFee` | `0x00000001` | Sponsoring (paying for) the fee of the transaction. |
 | `tfSponsorReserve` | `0x00000002` | Sponsoring the reserve for any objects created in the transaction. |
 
 This field **will** be a signing field (it will be included in transaction signatures).
@@ -368,22 +369,22 @@ This transaction creates and updates the `Sponsorship` object.
 
 ### 7.1. Fields
 
-| Field Name       | Required? | JSON Type | Internal Type | Description                                                                                           |
-| ---------------- | --------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------- |
-| `SponsorAccount` | ✔️        | `string`  | `AccountID`   | The sponsor associated with this relationship. This account also pays for the reserve of this object. |
-| `Sponsee`        | ✔️        | `string`  | `AccountID`   | The sponsee associated with this relationship.                                                        |
-| `FeeAmount`      |           | `string`  | `Amount`      | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.          |
-| `ReserveCount`   |           | `number`  | `UInt32`      | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use.              |
+| Field Name | Required? | JSON Type | Internal Type | Description |
+| ---------- | --------- | --------- | ------------- | ------------ |
+| `SponsorAccount` | ✔️ | `string`  | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. |
+| `Sponsee` | ✔️ | `string`  | `AccountID` | The sponsee associated with this relationship. |
+| `FeeAmount`  | | `string`  | `Amount`  | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.  |
+| `ReserveCount` | | `number`  | `UInt32`  | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. |
 
 ### 7.2. Flags
 
-| Flag Name                                 | Flag Value   | Description                                                                                                       |
-| ----------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------- |
-| `tfSponsorshipSetRequireSignForFee`       | `0x00010000` | Adds the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor.    |
-| `tfSponsorshipClearRequireSignForFee`     | `0x00020000` | Removes the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. |
-| `tfSponsorshipSetRequireSignForReserve`   | `0x00040000` | Adds the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor.         |
-| `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor.      |
-| `tfDeleteObject`                          | `0x00100000` | Removes the ledger object.                                                                                        |
+| Flag Name | Flag Value | Description |
+| --------- | ---------- | ----------- |
+| `tfSponsorshipSetRequireSignForFee` | `0x00010000` | Adds the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor.  |
+| `tfSponsorshipClearRequireSignForFee` | `0x00020000` | Removes the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. |
+| `tfSponsorshipSetRequireSignForReserve` | `0x00040000` | Adds the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. |
+| `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor.  |
+| `tfDeleteObject` | `0x00100000` | Removes the ledger object. |
 
 ### 7.2. Failure Conditions
 
@@ -408,12 +409,12 @@ This transaction transfers a sponsor relationship for a particular ledger object
 
 ### 8.1. Fields
 
-| Field Name        | Required? | JSON Type | Internal Type | Description |
-| ----------------- | --------- | --------- | ------------- | ----------- |
-| `TransactionType` | ✔️        | `string`  | `UInt16`      |
-| `Account`         | ✔️        | `string`  | `AccountID`   |
-| `ObjectID`        |           | `string`  | `UInt256`     |
-| `Sponsor`         |           | `object`  | `STObject`    |
+| Field Name | Required? | JSON Type | Internal Type | Description |
+| ---------- | --------- | --------- | ------------- | ------------ |
+| `TransactionType` | ✔️ | `string`  | `UInt16`  |
+| `Account` | ✔️ | `string`  | `AccountID` |
+| `ObjectID` | | `string`  | `UInt256` |
+| `Sponsor` | | `object`  | `STObject`  |
 
 #### 8.1.1. `ObjectID`
 
@@ -495,21 +496,21 @@ This delegatable granular permission allows an account to sponsor reserves on be
 
 The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts:
 
-| Field Name               | Required? | JSON Type            | Description                                                                                                                                                                                                                                                                |
-| ------------------------ | --------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `account`                | ✔️        | `string`             | Get ledger entries associated with this account.                                                                                                                                                                                                                           |
-| `deletion_blockers_only` |           | `boolean`            | If `true`, only return ledger entries that would block this account from being deleted. The default is `false`.                                                                                                                                                            |
-| `ledger_hash`            |           | `string`             | The unique hash of the ledger version to use.                                                                                                                                                                                                                              |
-| `ledger_index`           |           | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically.                                                                                                                                                                              |
-| `limit`                  |           | `number`             | The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`.                                                                                                           |
-| `marker`                 |           | `any`                | Value from a previous paginated response. Resume retrieving data where that response left off.                                                                                                                                                                             |
-| `type`                   |           | `string`             | Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types. |
+| Field Name  | Required? | JSON Type  | Description |
+| ----------- | --------- | ---------- | ----------- |
+| `account` | ✔️ | `string` | Get ledger entries associated with this account. |
+| `deletion_blockers_only` | | `boolean`  | If `true`, only return ledger entries that would block this account from being deleted. The default is `false`.  |
+| `ledger_hash`  | | `string` | The unique hash of the ledger version to use. |
+| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically.  |
+| `limit` | | `number` | The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`. |
+| `marker`  | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. |
+| `type`  | | `string` | Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types. |
 
 We propose this additional field:
 
-| Field Name  | Required? | JSON Type | Description                                                                                                                                              |
-| ----------- | --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `sponsored` |           | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. |
+| Field Name  | Required? | JSON Type | Description |
+| ----------- | --------- | --------- | ----------- |
+| `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. |
 
 ### 12.2. Response Fields
 
@@ -521,38 +522,38 @@ The `account_sponsoring` RPC method is used to fetch a list of objects that an a
 
 ### 13.1. Request Fields
 
-| Field Name               | Required? | JSON Type            | Description                                                                                                             |
-| ------------------------ | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------- |
-| `account`                | ✔️        | `string`             | The sponsor in question.                                                                                                |
-| `deletion_blockers_only` |           | `boolean`            | If `true`, the response only includes objects that would block this account from being deleted. The default is `false`. |
-| `ledger_hash`            |           | `string`             | A hash representing the ledger version to use.                                                                          |
-| `ledger_index`           |           | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically.                           |
-| `limit`                  |           | `number`             | The maximum number of objects to include in the results.                                                                |
-| `marker`                 |           | `any`                | Value from a previous paginated response. Resume retrieving data where that response left off.                          |
-| `type`                   |           | `string`             | Filter results by a ledger entry type. Some examples are `offer` and `escrow`.                                          |
+| Field Name  | Required? | JSON Type  | Description  |
+| ----------- | --------- | ---------- | ------------ |
+| `account` | ✔️ | `string` | The sponsor in question.  |
+| `deletion_blockers_only` | | `boolean`  | If `true`, the response only includes objects that would block this account from being deleted. The default is `false`. |
+| `ledger_hash`  | | `string` | A hash representing the ledger version to use. |
+| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. |
+| `limit` | | `number` | The maximum number of objects to include in the results. |
+| `marker`  | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. |
+| `type`  | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`.  |
 
 ### 13.2. Response Fields
 
 The response fields are nearly identical to `account_objects`.
 
-| Field Name             | Always Present? | JSON Type | Description                                                                                                                                                                                                                                                                                                                                                                           |
-| ---------------------- | --------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `account`              | ✔️              | `string`  | The account this request corresponds to.                                                                                                                                                                                                                                                                                                                                              |
-| `sponsored_objects`    | ✔️              | `array`   | Array of ledger entries in this account's owner directory. This includes entries that are owned by this account and entries that are linked to this account but owned by someone else, such as escrows where this account is the destination. Each member is a ledger entry in its raw ledger format. This may contain fewer entries than the maximum specified in the `limit` field. |
-| `ledger_hash`          |                 | `string`  | The identifying hash of the ledger that was used to generate this response.                                                                                                                                                                                                                                                                                                           |
-| `ledger_index`         |                 | `number`  | The ledger index of the ledger that was used to generate this response.                                                                                                                                                                                                                                                                                                               |
-| `ledger_current_index` |                 | `number`  | The ledger index of the open ledger that was used to generate this response.                                                                                                                                                                                                                                                                                                          |
-| `limit`                |                 | `number`  | The limit that was used in this request, if any.                                                                                                                                                                                                                                                                                                                                      |
-| `marker`               |                 | `any`     | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one.                                                                                                                                                                                                  |
-| `validated`            |                 | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change.                                                                                                                                                                                                                                                   |
+| Field Name | Always Present? | JSON Type | Description |
+| ---------- | --------------- | --------- | ----------- |
+| `account` | ✔️ | `string`  | The account this request corresponds to. |
+| `sponsored_objects`  | ✔️ | `array` | Array of ledger entries in this account's owner directory. This includes entries that are owned by this account and entries that are linked to this account but owned by someone else, such as escrows where this account is the destination. Each member is a ledger entry in its raw ledger format. This may contain fewer entries than the maximum specified in the `limit` field. |
+| `ledger_hash`  |  | `string`  | The identifying hash of the ledger that was used to generate this response.  |
+| `ledger_index` |  | `number`  | The ledger index of the ledger that was used to generate this response.  |
+| `ledger_current_index` |  | `number`  | The ledger index of the open ledger that was used to generate this response. |
+| `limit` |  | `number`  | The limit that was used in this request, if any. |
+| `marker`  |  | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. |
+| `validated`  |  | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change.  |
 
 ## 14. Security
 
 ### 14.1. Security Axioms
 
-Both the sponsee _and_ the sponsor must agree to enter into a sponsor relationship. The sponsee must be okay with the sponsor handling the reserve, and the sponsor must be willing to take on that reserve. A signature from both parties ensures that this is the case.
+Both the sponsee _and_ the sponsor must agree to enter into a sponsor relationship. The sponsee must actively consent to the sponsor handling the reserve, and the sponsor must be willing to take on that reserve. A signature from both parties ensures that this is the case.
 
-A sponsor will never be stuck sponsoring an sponsee's account or object it doesn't want to support anymore, because it can submit a `SponsorshipTransfer` transaction at any point.
+A sponsor will never be stuck sponsoring an sponsee's account or object it no longer wants to support, because it can submit a `SponsorshipTransfer` transaction at any point.
 
 The sponsor's signature must _always_ include the `Account` and `Sequence` fields, to prevent signature replay attacks (where the sponsor's signature can be reused to sponsor an object or account that they did not want to sponsor).
 
@@ -562,7 +563,7 @@ When sponsoring reserves, the sponsor's signature must include any aspects of th
 
 A sponsee cannot take advantage of the generosity of their sponsor, since the sponsor must sign every transaction it wants to sponsor the ledger objects for. A sponsee also must not be able to change the sponsorship type that the sponsor is willing to engage in, as this could lock up to 500 of the sponsor's XRP (in the case of 250 tickets being created in one `TicketCreate` transaction).
 
-An axiom that is out of scope: the sponsee _will not_ have any control over a sponsorship transfer. This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency.
+An axiom that is out of scope: the sponsee may not have any control over a sponsorship transfer (the sponsor may transfer a sponsorship without the sponsee's consent). This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency.
 
 ### 14.2. Signatures
 
@@ -853,7 +854,7 @@ There would be a wrapper transaction (tentatively named `Relay`), similar to `Ba
 
 It would look something like this:
 |FieldName | Required? | JSON Type | Internal Type |
-|:---------|:-----------|:---------------|:------------|
+|----------|------------|----------|---------------|
 |`TransactionType`|✔️|`string`|`UInt16`|
 |`Account`|✔️|`string`|`STAccount`|
 |`Fee`|✔️|`string`|`STAmount`|

From 4a3466837ca1ab7d9d8afb7b0720ab22634a29a2 Mon Sep 17 00:00:00 2001
From: Mayukha Vadari 
Date: Fri, 12 Sep 2025 10:50:09 -0400
Subject: [PATCH 09/53] XLS-0068-sponsored-fees-and-reserves/README.md updated
 from https://stackedit.io/

---
 .../README.md                                 | 25 ++++++++-----------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md
index 0747fcf7c..f7521aa1d 100644
--- a/XLS-0068-sponsored-fees-and-reserves/README.md
+++ b/XLS-0068-sponsored-fees-and-reserves/README.md
@@ -318,6 +318,8 @@ If a `Sponsorship` object does not exist:
 
 - There is no sponsor signature included.
 
+Note: if a transaction doesn't charge a fee (such as an account's first `SetRegularKey` transaction), the transaction will still succeed.
+
 #### 6.3.3. Reserve Sponsorship Failures
 
 - The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`)
@@ -332,6 +334,8 @@ If a `Sponsorship` object does not exist:
 
 - There is no sponsor signature included.
 
+Note: if a transaction doesn't charge a reserve (such as `AccountSet`), the transaction will still succeed.
+
 #### 6.3.4. Transactions that cannot be sponsored
 
 All transactions (other than pseudo-transactions) may use the `tfSponsorFee` flag, since they all have a fee.
@@ -340,8 +344,8 @@ However, some transactions will not support the `tfSponsorReserve` flag.
 
 - [`Batch` transactions](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch)
   - `Batch` does not create any objects on its own, and therefore its use in the outer transaction would be confusing, as users may think that that means that all inner transactions are sponsored. The inner transactions should use `tfSponsorReserve` instead.
-- All pseudo-transactions (currently `EnableAmendment`, `SetFee`, and `UNLModify`)
-  - The reserves for those objects are covered by the network, not by any one account.
+- All [pseudo-transactions](https://xrpl.org/docs/references/protocol/transactions/pseudo-transaction-types/pseudo-transaction-types) (currently `EnableAmendment`, `SetFee`, and `UNLModify`)
+  - The fees and reserves for those objects are covered by the network, not by any one account.
 
 Also, many transactions, such as `AccountSet`, will have no change in output when using the `tfSponsorReserve` flag, if they do not create any new objects or accounts.
 
@@ -743,24 +747,15 @@ The primary motivation for this design is to enable companies, token issuers, an
 
 ## n+1. Remaining TODOs/Open Questions
 
-- Do I need a new type of directory nodes to keep track of sponsored objects?
-  - Clio could perhaps solve this problem
 - How will this work for objects like trustlines, where multiple accounts might be holding reserves for it?
   - Maybe a second `Sponsor` field or something?
 - How do we handle account creation? The actual account owner's signing keys aren't involved in that at all... Maybe just a new flag on the payment saying you'll pay the reserve for the account?
-- Should it be `ReserveCount` or `ReserveAmount`?
-  - If `ReserveCount`, it can be decremented to keep track of how much is left.
-    - Pro: easier to reason about how much you're giving someone.
-    - Con: sponsors have to update the number if reserves increase and your cost-benefit analysis changes.
-  - If `ReserveAmount`, you'd need a separate "`OwnerCount`" field to keep track of how many objects the thing owns. And probably a flag for whether the account itself is sponsored.
-    - Pro: people are used to thinking in terms of XRP.
-    - Con: rippled math might get more complicated. You have to keep track of potential reserve sponsorships in two places (`Sponsorship` and `AccountRoot`)
 - Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve?
-- Should there be a "max XRP per transaction" field in `Sponsorship`?
-- If a transaction doesn't take a fee (first `SetRegularKey`) or doesn't increase reserve (e.g. `AccountDelete`), and is sponsored, should that transaction fail or succeed.
-- Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account?
-- Should we allow sponsorship of creating another account? e.g. Account A is sponsored by Sponsor, A creates B, does Sponsor also sponsor B or does this fail if A doesn't have the funds to create B?
+- Should there be a "max XRP per transaction" field in `Sponsorship`? Yes, TODO
+- Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? Pull from the `SponsorAccount`'s account, TODO
+- Should we allow sponsorship of creating another account? e.g. Account A is sponsored by Sponsor, A creates B, does Sponsor also sponsor B or does this fail if A doesn't have the funds to create B? No
 - Should `account_sponsoring` be Clio-only?
+- Should a sponsored account be prevented from sponsoring other accounts? By default the answer is no, so unless there's a reason to do so, we should leave it as is.
 
 # Appendix
 

From a611269416dfba6e3c44c570c2e067f158b81ed1 Mon Sep 17 00:00:00 2001
From: Mayukha Vadari 
Date: Fri, 12 Sep 2025 14:11:13 -0400
Subject: [PATCH 10/53] XLS-0068-sponsored-fees-and-reserves/README.md updated
 from https://stackedit.io/

---
 .../README.md                                 | 92 ++++++++++++-------
 1 file changed, 60 insertions(+), 32 deletions(-)

diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md
index f7521aa1d..2467daff7 100644
--- a/XLS-0068-sponsored-fees-and-reserves/README.md
+++ b/XLS-0068-sponsored-fees-and-reserves/README.md
@@ -124,9 +124,9 @@ The key of the `Sponsorship` object is the result of [`SHA512-Half`](https://xrp
 
 | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description |
 | ---------- | --------- | --------- | ------------- | --------- | ------------- | ------------ |
-| `SponsorAccount` | ✔️ | ✔️ | N/A | `string`  | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. |
+| `Owner` | ✔️ | ✔️ | N/A | `string`  | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. |
 | `Sponsee` | ✔️ | ✔️ | N/A | `string`  | `AccountID` | The sponsee associated with this relationship. |
-| `SponsorNode`  | ✔️ | ✔️ | N/A | `string`  | `UInt64`  | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. |
+| `OwnerNode`  | ✔️ | ✔️ | N/A | `string`  | `UInt64`  | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. |
 | `SponseeNode`  | ✔️ | ✔️ | N/A | `string`  | `UInt64`  | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. |
 | `FeeAmount`  | | | `0` | `string`  | `Amount`  | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees.  |
 | `ReserveCount` | | | `0` | `string`  | `UInt32`  | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves.  |
@@ -462,13 +462,37 @@ The sponsor will likely only rarely want to do this (such as if they are transfe
 - The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one.
 - If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one.
 
-## 9. Transaction: `AccountDelete`
+## 9. Transaction: `Payment`
+
+A Payment transaction represents a transfer of value from one account to another. (Depending on the path taken, this can involve additional exchanges of value, which occur atomically.) This transaction type can be used for several  [types of payments](https://xrpl.org/docs/references/protocol/transactions/types/payment#types-of-payments).
+
+Payments are also the only way to  [create accounts](https://xrpl.org/docs/references/protocol/transactions/types/payment#creating-accounts).
+
+As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment) are the fields that `Payment` currently has. This amendment proposes no changes to the fields, only to the flags and behavior.
+
+### 9.1. Flags
+
+As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment#payment-flags) are the flags that `Payment` currently has:
+
+| Flag Name | Flag Value | Description |
+|-----------|------------|-------------|
+| `tfNoRippleDirect` | `0x00010000` | Do not use the default path; only use paths included in the `Paths` field. This is intended to force the transaction to take arbitrage opportunities. Most clients do not need this. |
+| `tfPartialPayment` | `0x00020000` | If the specified `Amount` cannot be sent without spending more than `SendMax`, reduce the received amount instead of failing outright. See [Partial Payments](#partial-payments) for more details. |
+| `tfLimitQuality`   | `0x00040000` | Only take paths where all the conversions have an input:output ratio that is equal or better than the ratio of `Amount`:`SendMax`. See [Limit Quality](#limit-quality) for details. |
+
+This spec proposes the following additions:
+
+| Flag Name | Flag Value | Description |
+|-----------|------------|-------------|
+| `tfSponsorCreatedAccount` | `0x00080000` | This flag is only valid if the `Payment` is used to create an account. If it is enabled, the created account will be sponsored by the `tx.Account`. |
+
+## 10. Transaction: `AccountDelete`
 
 This transaction deletes an account.
 
 As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete) are the fields that `AccountDelete` currently has. This amendment proposes no changes to the fields, only to the behavior.
 
-### 9.1. Failure Conditions
+### 10.1. Failure Conditions
 
 Existing failure conditions still apply.
 
@@ -478,7 +502,7 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie
 
 If the `AccountRoot` associated with the `tx.Account` has a `SponsoringOwnerCount` or `SponsoringAccountCount` field, the transaction will fail with `tecHAS_OBLIGATIONS`.
 
-### 9.2. State Changes
+### 10.2. State Changes
 
 Existing state changes still apply, including rules around deletion blockers.
 
@@ -486,17 +510,17 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie
 
 If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`.
 
-## 10. Permission: `SponsorFee`
+## 11. Permission: `SponsorFee`
 
 This delegatable granular permission allows an account to sponsor fees on behalf of another account.
 
-## 11. Permission: `SponsorReserve`
+## 12. Permission: `SponsorReserve`
 
 This delegatable granular permission allows an account to sponsor reserves on behalf of another account.
 
-## 12. RPC: `account_objects`
+## 13. RPC: `account_objects`
 
-### 12.1. Request Fields
+### 13.1. Request Fields
 
 The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts:
 
@@ -516,15 +540,15 @@ We propose this additional field:
 | ----------- | --------- | --------- | ----------- |
 | `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. |
 
-### 12.2. Response Fields
+### 13.2. Response Fields
 
 The response fields remain the same.
 
-## 13. RPC: `account_sponsoring`
+## 14. RPC: `account_sponsoring`
 
 The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `SponsorAccount` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html).
 
-### 13.1. Request Fields
+### 14.1. Request Fields
 
 | Field Name  | Required? | JSON Type  | Description  |
 | ----------- | --------- | ---------- | ------------ |
@@ -536,7 +560,7 @@ The `account_sponsoring` RPC method is used to fetch a list of objects that an a
 | `marker`  | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. |
 | `type`  | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`.  |
 
-### 13.2. Response Fields
+### 14.2. Response Fields
 
 The response fields are nearly identical to `account_objects`.
 
@@ -551,9 +575,9 @@ The response fields are nearly identical to `account_objects`.
 | `marker`  |  | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. |
 | `validated`  |  | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change.  |
 
-## 14. Security
+## 15. Security
 
-### 14.1. Security Axioms
+### 15.1. Security Axioms
 
 Both the sponsee _and_ the sponsor must agree to enter into a sponsor relationship. The sponsee must actively consent to the sponsor handling the reserve, and the sponsor must be willing to take on that reserve. A signature from both parties ensures that this is the case.
 
@@ -569,37 +593,37 @@ A sponsee cannot take advantage of the generosity of their sponsor, since the sp
 
 An axiom that is out of scope: the sponsee may not have any control over a sponsorship transfer (the sponsor may transfer a sponsorship without the sponsee's consent). This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency.
 
-### 14.2. Signatures
+### 15.2. Signatures
 
 Since a fee sponsorship must approve of the `Fee` field, and a reserve sponsorship must approve of a broad set of transaction fields, the sponsor must always sign the whole transaction. This also avoids needing to have different sponsorship processes for different sponsorship types. This includes the non-signature parts of the `Sponsor` object (`Sponsor.Account` and `Sponsor.Flags`). The same is true for the sponsee's transaction signature; the sponsee must approve of the sponsor and sponsorship type.
 
 A sponsor's `Signature` cannot be replayed or attached to a different transaction, since the whole transaction (including the `Account` and `Sequence` values) must be signed.
 
-## 15. Invariants
+## 16. Invariants
 
 An [invariant](https://xrpl.org/docs/concepts/consensus-protocol/invariant-checking/) is a statement, usually an equation, that must always be true for every valid ledger state on the XRPL. Invariant checks serve as a last line of defense against bugs; the `tecINVARIANT_FAILED` error is thrown if an invariant is violated (which ideally should never happen).
 
-### 15.1. Tracking Owner Counts
+### 16.1. Tracking Owner Counts
 
 A transaction that creates a ledger object either increments an account's `OwnerCount` by 1 or increments two separate accounts' `SponsoringOwnerCount` and `SponsoredOwnerCount` by 1. The opposite happens when a ledger object is deleted.
 
 The equivalent also should happen with `SponsoringAccountCount`.
 
-### 15.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount`
+### 16.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount`
 
 $$ \sum*{accounts} Account.SponsoredOwnerCount = \sum*{accounts} Account.SponsoringOwnerCount $$
 
 In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to the sum of all accounts' `SponsoringOwnerCount`s. This ensures that every sponsored object is logged as being sponsored and also has a sponsor.
 
-## 16. Example Flows
+## 17. Example Flows
 
 Each example will show what the transaction will look like before **and** after both the sponsor and sponsee sign the transaction.
 
 The unsigned transaction must be autofilled before it is passed to the sponsor to sign. Tooling can be updated to handle combining the sponsor and sponsee signatures, similar to helper functions that already exist for multisigning.
 
-### 16.1. Fee Sponsorship
+### 17.1. Fee Sponsorship
 
-#### 16.1.1. The Unsigned Transaction
+#### 17.1.1. The Unsigned Transaction
 
 
@@ -620,7 +644,7 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t
-#### 16.1.2. The Signed Transaction +#### 17.1.2. The Signed Transaction
@@ -645,11 +669,11 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t
-### 16.2. Account Sponsorship +### 17.2. Account Sponsorship The only way an account can be created is via a `Payment` transaction. So the sponsor relationship must be initiated on the `Payment` transaction. -#### 16.2.1. The Unsigned Transaction +#### 17.2.1. The Unsigned Transaction
@@ -670,7 +694,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-#### 16.2.2. The Signed Transaction +#### 17.2.2. The Signed Transaction
@@ -695,9 +719,9 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-### 16.3. Object Sponsorship +### 17.3. Object Sponsorship -#### 16.3.1. The Unsigned Transaction +#### 17.3.1. The Unsigned Transaction
@@ -717,7 +741,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-#### 16.3.2. The Signed Transaction +#### 17.3.2. The Signed Transaction
@@ -741,7 +765,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-## 17. Rationale +## 18. Rationale The primary motivation for this design is to enable companies, token issuers, and other entities to reduce onboarding friction for end users by covering transaction fees and reserve requirements on their behalf. Today, users must self-fund both, or companies must essentially donate XRP to users with no controls over how they use it, before interacting with the XRPL. This creates a barrier to entry for use cases such as token distribution, NFT minting, or enterprise onboarding. Sponsorship provides a mechanism for entities with established XRP balances to subsidize these costs while maintaining strong on-chain accountability. @@ -751,12 +775,16 @@ The primary motivation for this design is to enable companies, token issuers, an - Maybe a second `Sponsor` field or something? - How do we handle account creation? The actual account owner's signing keys aren't involved in that at all... Maybe just a new flag on the payment saying you'll pay the reserve for the account? - Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve? -- Should there be a "max XRP per transaction" field in `Sponsorship`? Yes, TODO -- Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? Pull from the `SponsorAccount`'s account, TODO - Should we allow sponsorship of creating another account? e.g. Account A is sponsored by Sponsor, A creates B, does Sponsor also sponsor B or does this fail if A doesn't have the funds to create B? No - Should `account_sponsoring` be Clio-only? - Should a sponsored account be prevented from sponsoring other accounts? By default the answer is no, so unless there's a reason to do so, we should leave it as is. +### Answered and TODO + +- Should there be a "max XRP per transaction" field in `Sponsorship`? Yes, TODO +- Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? Pull from the `SponsorAccount`'s account, TODO +- + # Appendix ## Appendix A: FAQ From 07ae18cf657252724aba728bfd2fc5de2966c10a Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 12 Sep 2025 14:20:58 -0400 Subject: [PATCH 11/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- .../README.md | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 2467daff7..ed812fd4f 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -375,8 +375,10 @@ This transaction creates and updates the `Sponsorship` object. | Field Name | Required? | JSON Type | Internal Type | Description | | ---------- | --------- | --------- | ------------- | ------------ | -| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | -| `Sponsee` | ✔️ | `string` | `AccountID` | The sponsee associated with this relationship. | +|`TransactionType`|✔️|`string`|`UInt16`|The transaction type (`SponsorshipSet`).| +|`Account`|✔️|`string`|`AccountID`|The account sending the transaction. This may be either the sponsor or the sponsee. | +| `SponsorAccount` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | +| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `SponsorAccount`. | | `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | | `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | @@ -393,7 +395,9 @@ This transaction creates and updates the `Sponsorship` object. ### 7.2. Failure Conditions - `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` -- If `tfDeleteObject` is provided: +- Both `SponsorAccount` and `Sponsee` are specified +- `SponsorAccount` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled +- If `tfDeleteObject` is enabled: - `FeeAmount` is specified - `ReserveCount` is specified - `tfSponsorshipSetRequireSignForFee` is enabled @@ -415,10 +419,10 @@ This transaction transfers a sponsor relationship for a particular ledger object | Field Name | Required? | JSON Type | Internal Type | Description | | ---------- | --------- | --------- | ------------- | ------------ | -| `TransactionType` | ✔️ | `string` | `UInt16` | -| `Account` | ✔️ | `string` | `AccountID` | -| `ObjectID` | | `string` | `UInt256` | -| `Sponsor` | | `object` | `STObject` | +| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | +| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | +| `ObjectID` | | `string` | `UInt256` | The +| `Sponsor` | | `object` | `STObject` | #### 8.1.1. `ObjectID` @@ -428,7 +432,7 @@ If it is not included, then it refers to the account sending the transaction. #### 8.1.2. `Sponsor` -The `Sponsor` field is already added in the ledger common fields (see section [5.1.1](#511-sponsor)), but it has some additional rules associated with it on the `SponsorshipTransfer` transaction. +The `Sponsor` field is already added in the transaction common fields (see section [6.1.1](#611-sponsor)), but it has some additional rules associated with it on the `SponsorshipTransfer` transaction. In this case, if `Sponsor` is included with the `tfSponsorReserve` flag, then the reserve sponsorship for the provided object will be transferred to the `Sponsor.Account` instead of passing back to the ledger object's owner. @@ -448,14 +452,20 @@ Two accounts are allowed to submit a `SponsorshipTransfer` relationship to migra The sponsor will likely only rarely want to do this (such as if they are transferring accounts), but the sponsee may want to migrate if they change providers. -### 8.4. Failure Conditions +### 8.4. Transaction Fee + +The transaction fee for this transaction will be the base fee (currently 10 drops). + +### 8.5. Failure Conditions + +All failure conditions mentioned in section [6.3](#63-failure-conditions) still apply here. - If transferring the sponsorship, the new sponsor does not have enough reserve for this object/account. - If dissolving the sponsorship, the owner does not have enough reserve for this object/account. - The new sponsor does not exist. -- The `tx.Account` neither the sponsor nor the owner of `ObjectID`. +- The `tx.Account` neither the sponsor nor the owner (sponsee) of `ObjectID`. -### 8.5. State Changes +### 8.6. State Changes - The `Sponsor` field on the object is changed or deleted. - The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. From 9b8bcde1c302a6c7e663d729847f2d0124b115e6 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 12 Sep 2025 14:41:51 -0400 Subject: [PATCH 12/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- .../README.md | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index ed812fd4f..8af69d06b 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -126,10 +126,11 @@ The key of the `Sponsorship` object is the result of [`SHA512-Half`](https://xrp | ---------- | --------- | --------- | ------------- | --------- | ------------- | ------------ | | `Owner` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | | `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | +| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `MaxFee` | | | N/A | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | +| `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | | `OwnerNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. | | `SponseeNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. | -| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | -| `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | ### 4.3. Flags @@ -307,12 +308,13 @@ Either `SigningPubKey`+`Signature` or `Signers` must be included in the transact #### 6.3.2. Fee Sponsorship Failures -- The sponsor does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) +- The sponsor's account does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) If a `Sponsorship` object exists: - The `lsfRequireSignatureForFee` flag is enabled and there is no sponsor signature included. - There is not enough XRP in the `FeeAmount` to pay for the transaction. +- The fee in `tx.Fee` is greater than `Sponsorship.MaxFee` If a `Sponsorship` object does not exist: @@ -375,11 +377,12 @@ This transaction creates and updates the `Sponsorship` object. | Field Name | Required? | JSON Type | Internal Type | Description | | ---------- | --------- | --------- | ------------- | ------------ | -|`TransactionType`|✔️|`string`|`UInt16`|The transaction type (`SponsorshipSet`).| -|`Account`|✔️|`string`|`AccountID`|The account sending the transaction. This may be either the sponsor or the sponsee. | +| `TransactionType` |✔️|`string`|`UInt16`|The transaction type (`SponsorshipSet`).| +| `Account` |✔️|`string`|`AccountID`|The account sending the transaction. This may be either the sponsor or the sponsee. | | `SponsorAccount` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | | `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `SponsorAccount`. | -| `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | | `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | ### 7.2. Flags @@ -397,19 +400,24 @@ This transaction creates and updates the `Sponsorship` object. - `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` - Both `SponsorAccount` and `Sponsee` are specified - `SponsorAccount` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled +- `MaxFee` is less than the base fee or is not denominated in XRP +- `FeeAmount` is less than the base fee or is not denominated in XRP - If `tfDeleteObject` is enabled: - `FeeAmount` is specified + - `MaxFee` is specified - `ReserveCount` is specified - `tfSponsorshipSetRequireSignForFee` is enabled - `tfSponsorshipSetRequireSignForReserve` is enabled ### 7.3. State Changes -- If the object already exists, `Sponsorship.Amount += tx.FeeAmount` and `Sponsorship.ReserveCount += tx.ReserveCount`. -- If the object doesn't exist, it will be created. +- If the object already exists: + - `Sponsorship.Amount = tx.FeeAmount` + - `Sponsorship.MaxFee` = `tx.MaxFee` + - `Sponsorship.ReserveCount = tx.ReserveCount` +- If the object doesn't exist, it will be created with the provided fields. - If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. - - Both sponsor and sponsee can delete the object. - - Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process. + - _Note: this does not affect already-sponsored entries and accounts. Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process._ ## 8. Transaction: `SponsorshipTransfer` @@ -791,9 +799,7 @@ The primary motivation for this design is to enable companies, token issuers, an ### Answered and TODO -- Should there be a "max XRP per transaction" field in `Sponsorship`? Yes, TODO - Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? Pull from the `SponsorAccount`'s account, TODO -- # Appendix From 1dfa61afec7bc06a885c81a8ae2c79cf6a4c3ddb Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 12 Sep 2025 14:42:58 -0400 Subject: [PATCH 13/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- XLS-0068-sponsored-fees-and-reserves/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 8af69d06b..afd45f6c8 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -286,16 +286,16 @@ Either `Signature` or `Signers` must be included in the final transaction. There will be no additional transaction fee required for the use of the `Signature` field. -If the `Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\\#signatures+1)*base\textunderscore fee$. - -The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$. - `Signature` and `Signers` **will not** be signing fields (they will not be included in transaction signatures, though they will still be included in the stored transaction). Either `SigningPubKey`+`Signature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. ### 6.2. Transaction Fee +If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\\# signatures+1)*base\textunderscore fee$. + +The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$. + ### 6.3. Failure Conditions #### 6.3.1. General Failures From bbcad900b81feab7f96d237e01ba650304cebcf7 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 12 Sep 2025 14:46:10 -0400 Subject: [PATCH 14/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- XLS-0068-sponsored-fees-and-reserves/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index afd45f6c8..e2fc76bf8 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -294,7 +294,7 @@ Either `SigningPubKey`+`Signature` or `Signers` must be included in the transact If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\\# signatures+1)*base\textunderscore fee$. -The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$. +The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$ (plus transaction-specific fees). ### 6.3. Failure Conditions @@ -797,10 +797,6 @@ The primary motivation for this design is to enable companies, token issuers, an - Should `account_sponsoring` be Clio-only? - Should a sponsored account be prevented from sponsoring other accounts? By default the answer is no, so unless there's a reason to do so, we should leave it as is. -### Answered and TODO - -- Should the `Sponsorship` hold the XRP or pull from the `SponsorAccount`'s account? Pull from the `SponsorAccount`'s account, TODO - # Appendix ## Appendix A: FAQ From d70261bb710b4701f8498abb511b68130be16ca9 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 12 Sep 2025 14:54:35 -0400 Subject: [PATCH 15/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index e2fc76bf8..57f0576d3 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -1,4 +1,3 @@ -
   xls: 68
   title: Sponsored Fees and Reserves
@@ -793,7 +792,6 @@ The primary motivation for this design is to enable companies, token issuers, an
   - Maybe a second `Sponsor` field or something?
 - How do we handle account creation? The actual account owner's signing keys aren't involved in that at all... Maybe just a new flag on the payment saying you'll pay the reserve for the account?
 - Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve?
-- Should we allow sponsorship of creating another account? e.g. Account A is sponsored by Sponsor, A creates B, does Sponsor also sponsor B or does this fail if A doesn't have the funds to create B? No
 - Should `account_sponsoring` be Clio-only?
 - Should a sponsored account be prevented from sponsoring other accounts? By default the answer is no, so unless there's a reason to do so, we should leave it as is.
 

From c604296d6c2e60de2bbeee780c91ca0bb6e5af47 Mon Sep 17 00:00:00 2001
From: Mayukha Vadari 
Date: Tue, 16 Sep 2025 14:49:39 -0400
Subject: [PATCH 16/53] XLS-0068-sponsored-fees-and-reserves/README.md updated
 from https://stackedit.io/

---
 .../README.md                                 | 172 ++++++++++++------
 1 file changed, 112 insertions(+), 60 deletions(-)

diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md
index 57f0576d3..04e7b5ac9 100644
--- a/XLS-0068-sponsored-fees-and-reserves/README.md
+++ b/XLS-0068-sponsored-fees-and-reserves/README.md
@@ -130,6 +130,8 @@ The key of the `Sponsorship` object is the result of [`SHA512-Half`](https://xrp
 | `ReserveCount` | | | `0` | `string`  | `UInt32`  | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. |
 | `OwnerNode`  | ✔️ | ✔️ | N/A | `string`  | `UInt64`  | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. |
 | `SponseeNode`  | ✔️ | ✔️ | N/A | `string`  | `UInt64`  | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. |
+|`PreviousTxnID`||✔️|N/A|`string`|`Hash256`|The identifying hash of the transaction that most recently modified this entry.|
+|`PreviousTxnLgrSeq`||✔️|N/A|`number`|`UInt32`|The ledger index that contains the transaction that most recently modified this object.|
 
 ### 4.3. Flags
 
@@ -152,6 +154,8 @@ This object charges 1 reserve.
 
 This object will be deleted any time the `FeeAmount` and `ReserveCount` are both `0`. This can be done directly via `SponsorshipSet`, or can occur in the regular flow of transactions, if the sponsorship runs out.
 
+This object is a [deletion blocker](https://xrpl.org/docs/concepts/accounts/deleting-accounts/#requirements).
+
 ### 4.7. Invariant Checks
 
 - `FeeAmount` >= 0 || `ReserveCount` >= 0
@@ -164,6 +168,8 @@ The `snake_case` form of the ledger object name is `sponsorship`.
 
 ## 5. Ledger Entry: `AccountRoot`
 
+An `AccountRoot` ledger entry type describes a single [account](https://xrpl.org/docs/concepts/accounts), its settings, and XRP balance.
+
 ### 5.1. Fields
 
 
@@ -200,7 +206,7 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/led
-We propose these additional fields: +This spec proposes these additional fields: | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | |------------|-----------|-----------|---------------|-----------|---------------|-------------| |`SponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the account reserve for this account. | @@ -232,10 +238,57 @@ acctReserve * acct.SponsoringAccountCount } $$ -## 6. Transactions: Common Fields +## 6. Ledger Entry: `RippleState` + +A `RippleState` ledger entry represents a [trust line](https://xrpl.org/docs/concepts/tokens/fungible-tokens) between two accounts. Each account can change its own limit and other settings, but the balance is a single shared value. A trust line that is entirely in its default state is considered the same as a trust line that does not exist and is automatically deleted. You can create or modify a trust line with a [TrustSet transaction](https://xrpl.org/docs/references/protocol/transactions/types/trustset). ### 6.1. Fields +
+ + +As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/ripplestate#ripplestate-fields) are the fields that the `RippleState` ledger object currently has. + + + +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +|----------------------|-----------|-----------|---------------|-------------------|--------------|-------------| +| `Balance` | No | Yes | N/A | `object` | `Amount` | The balance of the trust line, from the perspective of the low account. A negative balance indicates that the high account holds tokens issued by the low account. The issuer in this is always set to the neutral value [ACCOUNT_ONE](https://xrpl.org/docs/concepts/accounts/addresses#special-addresses). | +| `Flags` | No | Yes | N/A | `number` | `UInt32` | A bit-map of boolean options enabled for this entry. | +| `HighLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the high account has set on the trust line. The `issuer` is the address of the high account that set this limit. | +| `HighNode` | Yes | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the high account's owner directory links to this entry, in case the directory consists of multiple pages. | +| `HighQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the high account, as an integer in the implied ratio `HighQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `HighQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the high account, as an integer in the implied ratio `HighQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `LedgerEntryType` | Yes | Yes | `RippleState` | `string` | `UInt16` | The value `0x0072`, mapped to the string `RippleState`, indicates that this is a RippleState entry. | +| `LockCount` | No | No | N/A | `object` | `Amount` | The total number of lock balances on a `RippleState` ledger object. | +| `LockedBalance` | No | No | N/A | `object` or `string` | `Amount` | The total number of locked tokens on a `RippleState` ledger object. | +| `LowLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the low account has set on the trust line. The `issuer` is the address of the low account that set this limit. | +| `LowNode` | No | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this entry, in case the directory consists of multiple pages. | +| `LowQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the low account, as an integer in the implied ratio `LowQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `LowQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the low account, as an integer in the implied ratio `LowQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `PreviousTxnID` | No | Yes | N/A | `string` | `UInt256` | The identifying hash of the transaction that most recently modified this entry. | +| `PreviousTxnLgrSeq` | No | Yes | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this entry. | + +
+ +This spec proposes these additional fields: +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +|------------|-----------|-----------|---------------|-----------|---------------|-------------| +|`HighSponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the reserve on behalf of the "high" account on the trustline. | +|`LowSponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the reserve on behalf of the "low" account on the trustline. | + +These additional fields are necessary for a trustline since the reserve for this object may be held by two accounts (in the case of a bidirectional trustline). + +### 6.2. Invariants + +Existing invariants remain. + +The common field `SponsorAccount` must not be on any `RippleState` objects (they must use `HighSponsorAccount` and `LowSponsorAccount` instead). + +## 7. Transactions: Common Fields + +### 7.1. Fields + As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/common-fields/) are the fields that all transactions currently have. @@ -246,7 +299,7 @@ We propose these modifications: | ---------- | --------- | --------- | ------------- | ------------ | | `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | -#### 6.1.1. `Sponsor` +#### 7.1.1. `Sponsor` The `Sponsor` inner object contains all of the information for the sponsorship happening in the transaction. @@ -260,13 +313,13 @@ The fields contained in this object are: | `Signature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | | `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | -##### 6.1.1.1. `Account` +##### 7.1.1.1. `Account` The `Sponsor.Account` field represents the sponsor. This field **will** be a signing field (it will be included in transaction signatures). -##### 6.1.1.2. `Flags` +##### 7.1.1.2. `Flags` The `Flags` field allows the user to specify which sponsorship type(s) they wish to participate in. At least one flag **must** be specified if the `Sponsor` field is included in a transaction. @@ -279,7 +332,7 @@ There are two flag values that are supported: This field **will** be a signing field (it will be included in transaction signatures). -##### 6.1.1.3. `SigningPubKey`, `Signature` and `Signers` +##### 7.1.1.3. `SigningPubKey`, `Signature` and `Signers` Either `Signature` or `Signers` must be included in the final transaction. @@ -289,15 +342,15 @@ There will be no additional transaction fee required for the use of the `Signatu Either `SigningPubKey`+`Signature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. -### 6.2. Transaction Fee +### 7.2. Transaction Fee If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\\# signatures+1)*base\textunderscore fee$. The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$ (plus transaction-specific fees). -### 6.3. Failure Conditions +### 7.3. Failure Conditions -#### 6.3.1. General Failures +#### 7.3.1. General Failures - `Sponsor.Signature` is invalid. - `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). @@ -305,7 +358,7 @@ The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# t - An invalid sponsorship flag is used. - `Sponsor.SigningPubKey`, `Sponsor.Signature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). -#### 6.3.2. Fee Sponsorship Failures +#### 7.3.2. Fee Sponsorship Failures - The sponsor's account does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) @@ -321,7 +374,7 @@ If a `Sponsorship` object does not exist: Note: if a transaction doesn't charge a fee (such as an account's first `SetRegularKey` transaction), the transaction will still succeed. -#### 6.3.3. Reserve Sponsorship Failures +#### 7.3.3. Reserve Sponsorship Failures - The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) - The transaction does not support reserve sponsorship (see section 6.3.4) @@ -337,7 +390,7 @@ If a `Sponsorship` object does not exist: Note: if a transaction doesn't charge a reserve (such as `AccountSet`), the transaction will still succeed. -#### 6.3.4. Transactions that cannot be sponsored +#### 7.3.4. Transactions that cannot be sponsored All transactions (other than pseudo-transactions) may use the `tfSponsorFee` flag, since they all have a fee. @@ -350,15 +403,15 @@ However, some transactions will not support the `tfSponsorReserve` flag. Also, many transactions, such as `AccountSet`, will have no change in output when using the `tfSponsorReserve` flag, if they do not create any new objects or accounts. -### 6.4. State Changes +### 7.4. State Changes -#### 6.4.1. Fee Sponsorship State Changes +#### 7.4.1. Fee Sponsorship State Changes If a `Sponsorship` object exists, the `tx.Fee` value is decremented from the `Sponsorship.FeeAmount`. If a `Sponsorship` object does not exist, the `tx.Fee` value is decremented from the sponsor's `AccountRoot.Balance`. -#### 6.4.2. Reserve Sponsorship State Changes +#### 7.4.2. Reserve Sponsorship State Changes Any account/object that is created as a part of the transaction will have a `Sponsor` field. @@ -368,11 +421,11 @@ The sponsee's `SponsoredOwnerCount` field will be incremented by the number of o The `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` fields will be decremented when those objects/accounts are deleted. -## 7. Transaction: `SponsorshipSet` +## 8. Transaction: `SponsorshipSet` This transaction creates and updates the `Sponsorship` object. -### 7.1. Fields +### 8.1. Fields | Field Name | Required? | JSON Type | Internal Type | Description | | ---------- | --------- | --------- | ------------- | ------------ | @@ -384,7 +437,7 @@ This transaction creates and updates the `Sponsorship` object. | `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | | `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | -### 7.2. Flags +### 8.2. Flags | Flag Name | Flag Value | Description | | --------- | ---------- | ----------- | @@ -394,7 +447,7 @@ This transaction creates and updates the `Sponsorship` object. | `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | | `tfDeleteObject` | `0x00100000` | Removes the ledger object. | -### 7.2. Failure Conditions +### 8.2. Failure Conditions - `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` - Both `SponsorAccount` and `Sponsee` are specified @@ -408,7 +461,7 @@ This transaction creates and updates the `Sponsorship` object. - `tfSponsorshipSetRequireSignForFee` is enabled - `tfSponsorshipSetRequireSignForReserve` is enabled -### 7.3. State Changes +### 8.3. State Changes - If the object already exists: - `Sponsorship.Amount = tx.FeeAmount` @@ -418,11 +471,11 @@ This transaction creates and updates the `Sponsorship` object. - If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. - _Note: this does not affect already-sponsored entries and accounts. Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process._ -## 8. Transaction: `SponsorshipTransfer` +## 9. Transaction: `SponsorshipTransfer` This transaction transfers a sponsor relationship for a particular ledger object's object reserve. The sponsor relationship can either be passed on to a new sponsor, or dissolved entirely (with the sponsee taking on the reserve). Either the sponsor or sponsee may submit this transaction at any point in time. -### 8.1. Fields +### 9.1. Fields | Field Name | Required? | JSON Type | Internal Type | Description | | ---------- | --------- | --------- | ------------- | ------------ | @@ -431,13 +484,13 @@ This transaction transfers a sponsor relationship for a particular ledger object | `ObjectID` | | `string` | `UInt256` | The | `Sponsor` | | `object` | `STObject` | -#### 8.1.1. `ObjectID` +#### 9.1.1. `ObjectID` This field should be included if this transaction is dealing with sponsored object, rather than on a sponsored account. This field indicates which object the relationship is changing for. If it is not included, then it refers to the account sending the transaction. -#### 8.1.2. `Sponsor` +#### 9.1.2. `Sponsor` The `Sponsor` field is already added in the transaction common fields (see section [6.1.1](#611-sponsor)), but it has some additional rules associated with it on the `SponsorshipTransfer` transaction. @@ -445,13 +498,13 @@ In this case, if `Sponsor` is included with the `tfSponsorReserve` flag, then th If there is no `Sponsor` field, or if the `tfSponsorReserve` flag is not included, then the burden of the reserve will be passed back to the ledger object's owner (the former sponsee). -### 8.2. Ending the Sponsorship for a Sponsored Ledger Object +### 9.2. Ending the Sponsorship for a Sponsored Ledger Object A sponsored ledger object will have the `Sponsor` field attached to it. Ending the sponsor relationship for a sponsored ledger object requires the `ObjectID` parameter, to specify which ledger object. Two accounts are allowed to submit a `SponsorshipTransfer` relationship to end the sponsor relationship for a sponsored ledger object: either the sponsor for that object or the owner of that object (the sponsee). -### 8.3. Migrating a Sponsorship to a New Account +### 9.3. Migrating a Sponsorship to a New Account A sponsorship can be migrated to a new account by including the `Sponsor` field with the `tfSponsorReserve` flag. This can be done for either a sponsored account or a sponsored ledger object. @@ -459,11 +512,11 @@ Two accounts are allowed to submit a `SponsorshipTransfer` relationship to migra The sponsor will likely only rarely want to do this (such as if they are transferring accounts), but the sponsee may want to migrate if they change providers. -### 8.4. Transaction Fee +### 9.4. Transaction Fee The transaction fee for this transaction will be the base fee (currently 10 drops). -### 8.5. Failure Conditions +### 9.5. Failure Conditions All failure conditions mentioned in section [6.3](#63-failure-conditions) still apply here. @@ -472,14 +525,14 @@ All failure conditions mentioned in section [6.3](#63-failure-conditions) still - The new sponsor does not exist. - The `tx.Account` neither the sponsor nor the owner (sponsee) of `ObjectID`. -### 8.6. State Changes +### 9.6. State Changes - The `Sponsor` field on the object is changed or deleted. - The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. - The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. - If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. -## 9. Transaction: `Payment` +## 10. Transaction: `Payment` A Payment transaction represents a transfer of value from one account to another. (Depending on the path taken, this can involve additional exchanges of value, which occur atomically.) This transaction type can be used for several [types of payments](https://xrpl.org/docs/references/protocol/transactions/types/payment#types-of-payments). @@ -487,7 +540,7 @@ Payments are also the only way to [create accounts](https://xrpl.org/docs/refer As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment) are the fields that `Payment` currently has. This amendment proposes no changes to the fields, only to the flags and behavior. -### 9.1. Flags +### 10.1. Flags As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment#payment-flags) are the flags that `Payment` currently has: @@ -503,13 +556,13 @@ This spec proposes the following additions: |-----------|------------|-------------| | `tfSponsorCreatedAccount` | `0x00080000` | This flag is only valid if the `Payment` is used to create an account. If it is enabled, the created account will be sponsored by the `tx.Account`. | -## 10. Transaction: `AccountDelete` +## 11. Transaction: `AccountDelete` This transaction deletes an account. As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete) are the fields that `AccountDelete` currently has. This amendment proposes no changes to the fields, only to the behavior. -### 10.1. Failure Conditions +### 11.1. Failure Conditions Existing failure conditions still apply. @@ -519,7 +572,7 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie If the `AccountRoot` associated with the `tx.Account` has a `SponsoringOwnerCount` or `SponsoringAccountCount` field, the transaction will fail with `tecHAS_OBLIGATIONS`. -### 10.2. State Changes +### 11.2. State Changes Existing state changes still apply, including rules around deletion blockers. @@ -527,17 +580,17 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. -## 11. Permission: `SponsorFee` +## 12. Permission: `SponsorFee` This delegatable granular permission allows an account to sponsor fees on behalf of another account. -## 12. Permission: `SponsorReserve` +## 13. Permission: `SponsorReserve` This delegatable granular permission allows an account to sponsor reserves on behalf of another account. -## 13. RPC: `account_objects` +## 14. RPC: `account_objects` -### 13.1. Request Fields +### 14.1. Request Fields The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts: @@ -557,15 +610,15 @@ We propose this additional field: | ----------- | --------- | --------- | ----------- | | `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | -### 13.2. Response Fields +### 14.2. Response Fields The response fields remain the same. -## 14. RPC: `account_sponsoring` +## 15. RPC: `account_sponsoring` The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `SponsorAccount` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). -### 14.1. Request Fields +### 15.1. Request Fields | Field Name | Required? | JSON Type | Description | | ----------- | --------- | ---------- | ------------ | @@ -577,7 +630,7 @@ The `account_sponsoring` RPC method is used to fetch a list of objects that an a | `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | | `type` | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`. | -### 14.2. Response Fields +### 15.2. Response Fields The response fields are nearly identical to `account_objects`. @@ -592,9 +645,9 @@ The response fields are nearly identical to `account_objects`. | `marker` | | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. | | `validated` | | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change. | -## 15. Security +## 16. Security -### 15.1. Security Axioms +### 16.1. Security Axioms Both the sponsee _and_ the sponsor must agree to enter into a sponsor relationship. The sponsee must actively consent to the sponsor handling the reserve, and the sponsor must be willing to take on that reserve. A signature from both parties ensures that this is the case. @@ -610,37 +663,37 @@ A sponsee cannot take advantage of the generosity of their sponsor, since the sp An axiom that is out of scope: the sponsee may not have any control over a sponsorship transfer (the sponsor may transfer a sponsorship without the sponsee's consent). This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency. -### 15.2. Signatures +### 16.2. Signatures Since a fee sponsorship must approve of the `Fee` field, and a reserve sponsorship must approve of a broad set of transaction fields, the sponsor must always sign the whole transaction. This also avoids needing to have different sponsorship processes for different sponsorship types. This includes the non-signature parts of the `Sponsor` object (`Sponsor.Account` and `Sponsor.Flags`). The same is true for the sponsee's transaction signature; the sponsee must approve of the sponsor and sponsorship type. A sponsor's `Signature` cannot be replayed or attached to a different transaction, since the whole transaction (including the `Account` and `Sequence` values) must be signed. -## 16. Invariants +## 17. Invariants An [invariant](https://xrpl.org/docs/concepts/consensus-protocol/invariant-checking/) is a statement, usually an equation, that must always be true for every valid ledger state on the XRPL. Invariant checks serve as a last line of defense against bugs; the `tecINVARIANT_FAILED` error is thrown if an invariant is violated (which ideally should never happen). -### 16.1. Tracking Owner Counts +### 17.1. Tracking Owner Counts A transaction that creates a ledger object either increments an account's `OwnerCount` by 1 or increments two separate accounts' `SponsoringOwnerCount` and `SponsoredOwnerCount` by 1. The opposite happens when a ledger object is deleted. The equivalent also should happen with `SponsoringAccountCount`. -### 16.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount` +### 17.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount` $$ \sum*{accounts} Account.SponsoredOwnerCount = \sum*{accounts} Account.SponsoringOwnerCount $$ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to the sum of all accounts' `SponsoringOwnerCount`s. This ensures that every sponsored object is logged as being sponsored and also has a sponsor. -## 17. Example Flows +## 18. Example Flows Each example will show what the transaction will look like before **and** after both the sponsor and sponsee sign the transaction. The unsigned transaction must be autofilled before it is passed to the sponsor to sign. Tooling can be updated to handle combining the sponsor and sponsee signatures, similar to helper functions that already exist for multisigning. -### 17.1. Fee Sponsorship +### 18.1. Fee Sponsorship -#### 17.1.1. The Unsigned Transaction +#### 18.1.1. The Unsigned Transaction
@@ -661,7 +714,7 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t
-#### 17.1.2. The Signed Transaction +#### 18.1.2. The Signed Transaction
@@ -686,11 +739,11 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t
-### 17.2. Account Sponsorship +### 18.2. Account Sponsorship The only way an account can be created is via a `Payment` transaction. So the sponsor relationship must be initiated on the `Payment` transaction. -#### 17.2.1. The Unsigned Transaction +#### 18.2.1. The Unsigned Transaction
@@ -711,7 +764,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-#### 17.2.2. The Signed Transaction +#### 18.2.2. The Signed Transaction
@@ -736,9 +789,9 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-### 17.3. Object Sponsorship +### 18.3. Object Sponsorship -#### 17.3.1. The Unsigned Transaction +#### 18.3.1. The Unsigned Transaction
@@ -758,7 +811,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-#### 17.3.2. The Signed Transaction +#### 18.3.2. The Signed Transaction
@@ -782,7 +835,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-## 18. Rationale +## 19. Rationale The primary motivation for this design is to enable companies, token issuers, and other entities to reduce onboarding friction for end users by covering transaction fees and reserve requirements on their behalf. Today, users must self-fund both, or companies must essentially donate XRP to users with no controls over how they use it, before interacting with the XRPL. This creates a barrier to entry for use cases such as token distribution, NFT minting, or enterprise onboarding. Sponsorship provides a mechanism for entities with established XRP balances to subsidize these costs while maintaining strong on-chain accountability. @@ -790,7 +843,6 @@ The primary motivation for this design is to enable companies, token issuers, an - How will this work for objects like trustlines, where multiple accounts might be holding reserves for it? - Maybe a second `Sponsor` field or something? -- How do we handle account creation? The actual account owner's signing keys aren't involved in that at all... Maybe just a new flag on the payment saying you'll pay the reserve for the account? - Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve? - Should `account_sponsoring` be Clio-only? - Should a sponsored account be prevented from sponsoring other accounts? By default the answer is no, so unless there's a reason to do so, we should leave it as is. From 52c2b446c056c948e1e9e5fef355eaf5c90b6346 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 16 Sep 2025 14:56:50 -0400 Subject: [PATCH 17/53] XLS-0068-sponsored-fees-and-reserves/README.md updated from https://stackedit.io/ --- XLS-0068-sponsored-fees-and-reserves/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 04e7b5ac9..407deb910 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -366,6 +366,7 @@ If a `Sponsorship` object exists: - The `lsfRequireSignatureForFee` flag is enabled and there is no sponsor signature included. - There is not enough XRP in the `FeeAmount` to pay for the transaction. + - Paying fees via sponsorship will _not_ be able to [go below the reserve requirement](https://xrpl.org/docs/concepts/accounts/reserves#going-below-the-reserve-requirement). - The fee in `tx.Fee` is greater than `Sponsorship.MaxFee` If a `Sponsorship` object does not exist: From c027a698d1e7b82cfee3e6e6afc6766f7899ea00 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 22 Sep 2025 12:31:21 -0400 Subject: [PATCH 18/53] Sync Public/Sponsor XLS.md from Obsidian vault --- .../README.md | 350 +++++++++--------- 1 file changed, 178 insertions(+), 172 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 407deb910..a491a7e0c 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -5,7 +5,7 @@ author: Mayukha Vadari (@mvadari) proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 created: 2024-05-02 - updated: 2025-09-08 + updated: 2025-09-19 status: Draft category: Amendment requires: 74 @@ -31,16 +31,18 @@ Sponsors can also pre-fund fees or reserves, if they do not want to deal with th We propose: +- Modifying the ledger entry common fields - Creating the `Sponsorship` ledger entry - Modifying the `AccountRoot` ledger entry +- Modifying the `RippleState` ledger entry +- Modifying the transaction common fields - Creating the `SponsorshipSet` transaction type - Creating the `SponsorshipTransfer` transaction type +- Modifying the `Payment` transaction type (only flags) - Modifying the `AccountDelete` transaction type (behavior only, not fields) - Adding two additional granular permissions (`SponsorFee`, `SponsorReserve`) -The common fields for all ledger objects and all transactions will also be modified. - -In addition, there will be a modification to the `account_objects` RPC method, and a new RPC method called `account_sponsoring`. +In addition, there will be a modification to the `account_objects` RPC method, and a new Clio RPC method called `account_sponsoring`. This feature will require an amendment, tentatively titled `Sponsor`. @@ -95,15 +97,16 @@ There are two ways in which he could do this: As a reference, here are the fields that all ledger objects currently have: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | ---------- | ------------- | ----------- | -| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | -| `Flags` | ✔️ | ✔️ | N/A | `number` | `UInt16` | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ----------------- | --------- | --------- | ------------- | --------- | ------------- | --------------------------------------- | +| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The type of ledger entry. | +| `Flags` | ✔️ | ✔️ | N/A | `number` | `UInt16` | Set of bit-flags for this ledger entry. | We propose this additional field: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|------------|---------------|-------------| -|`SponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the reserve for this ledger object. | + +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ---------------- | --------- | --------- | ------------- | --------- | ------------- | -------------------------------------------------------------- | +| `SponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve for this ledger object. | ## 4. Ledger Entry: `Sponsorship` @@ -121,26 +124,26 @@ The key of the `Sponsorship` object is the result of [`SHA512-Half`](https://xrp ### 4.2. Fields -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | --------- | ------------- | ------------ | -| `Owner` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | -| `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | -| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | -| `MaxFee` | | | N/A | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | -| `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | -| `OwnerNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. | -| `SponseeNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. | -|`PreviousTxnID`||✔️|N/A|`string`|`Hash256`|The identifying hash of the transaction that most recently modified this entry.| -|`PreviousTxnLgrSeq`||✔️|N/A|`number`|`UInt32`|The ledger index that contains the transaction that most recently modified this object.| +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ------------------- | --------- | --------- | ------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `Owner` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | +| `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | +| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `MaxFee` | | | N/A | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | +| `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | +| `OwnerNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. | +| `SponseeNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. | +| `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this entry. | +| `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | ### 4.3. Flags There are two flags on this object: -| Flag Name | Flag Value | Modifiable? | Description | -| --------- | ------------ | ----------- | ----------- | -| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| Flag Name | Flag Value | Modifiable? | Description | +| ------------------------------------- | ------------ | ----------- | ----------------------------------------------------------------------------------------------------------- | +| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | ### 4.4. Ownership @@ -179,40 +182,41 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/led -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | --------- | ------------- | ------------ | -| `Account` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The identifying (classic) address of this account. | -| `AccountTxnID` | | | N/A | `string` | `Hash256` | The identifying hash of the transaction most recently sent by this account. | -| `AMMID` | ✔️ | | N/A | `string` | `Hash256` | The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account. | -| `Balance` | | | N/A | `string` | `Amount` | The account's current XRP balance. | -| `BurnedNFTokens` | | | `0` | `number` | `UInt32` | How many total of this account's issued NFTs have been burned. | -| `Domain` | | | N/A | `string` | `Blob` | A domain associated with this account. | -| `EmailHash` | | | N/A | `string` | `Hash128` | The md5 hash of an email address. | -| `FirstNFTokenSequence` | ✔️ | | N/A | `number` | `UInt32` | The account's Sequence Number at the time it minted its first non-fungible-token. | -| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object. | -| `MessageKey` | | | N/A | `string` | `Blob` | A public key that may be used to send encrypted messages to this account. | -| `MintedNFTokens` | | | `0` | `number` | `UInt32` | How many total non-fungible tokens have been minted by/on behalf of this account. | -| `NFTokenMinter` | | | N/A | `string` | `AccountID` | Another account that can mint NFTs on behalf of this account. | -| `OwnerCount` | | ✔️ | N/A | `number` | `UInt32` | The number of objects this account owns in the ledger, which contributes to its owner reserve. | -| `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this object. | -| `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | -| `RegularKey` | | | N/A | `string` | `AccountID` | The address of a key pair that can be used to sign transactions for this account instead of the master key. | -| `Sequence` | | ✔️ | N/A | `number` | `UInt32` | The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account. | -| `TicketCount` | | | N/A | `number` | `UInt32` | How many Tickets this account owns in the ledger. | -| `TickSize` | | | N/A | `number` | `UInt8` | [How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize) | -| `TransferRate` | | | N/A | `number` | `UInt32` | A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other. | -| `WalletLocator` | | | N/A | `string` | `Hash256` | An arbitrary 256-bit value that users can set. | -| `WalletSize` | | | N/A | `number` | `UInt32` | Unused. | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ---------------------- | --------- | --------- | ------------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Account` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The identifying (classic) address of this account. | +| `AccountTxnID` | | | N/A | `string` | `Hash256` | The identifying hash of the transaction most recently sent by this account. | +| `AMMID` | ✔️ | | N/A | `string` | `Hash256` | The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account. | +| `Balance` | | | N/A | `string` | `Amount` | The account's current XRP balance. | +| `BurnedNFTokens` | | | `0` | `number` | `UInt32` | How many total of this account's issued NFTs have been burned. | +| `Domain` | | | N/A | `string` | `Blob` | A domain associated with this account. | +| `EmailHash` | | | N/A | `string` | `Hash128` | The md5 hash of an email address. | +| `FirstNFTokenSequence` | ✔️ | | N/A | `number` | `UInt32` | The account's Sequence Number at the time it minted its first non-fungible-token. | +| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object. | +| `MessageKey` | | | N/A | `string` | `Blob` | A public key that may be used to send encrypted messages to this account. | +| `MintedNFTokens` | | | `0` | `number` | `UInt32` | How many total non-fungible tokens have been minted by/on behalf of this account. | +| `NFTokenMinter` | | | N/A | `string` | `AccountID` | Another account that can mint NFTs on behalf of this account. | +| `OwnerCount` | | ✔️ | N/A | `number` | `UInt32` | The number of objects this account owns in the ledger, which contributes to its owner reserve. | +| `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this object. | +| `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | +| `RegularKey` | | | N/A | `string` | `AccountID` | The address of a key pair that can be used to sign transactions for this account instead of the master key. | +| `Sequence` | | ✔️ | N/A | `number` | `UInt32` | The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account. | +| `TicketCount` | | | N/A | `number` | `UInt32` | How many Tickets this account owns in the ledger. | +| `TickSize` | | | N/A | `number` | `UInt8` | [How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize) | +| `TransferRate` | | | N/A | `number` | `UInt32` | A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other. | +| `WalletLocator` | | | N/A | `string` | `Hash256` | An arbitrary 256-bit value that users can set. | +| `WalletSize` | | | N/A | `number` | `UInt32` | Unused. | This spec proposes these additional fields: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-----------|---------------|-------------| -|`SponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the account reserve for this account. | -|`SponsoredOwnerCount`| | |`0`|`number`|`UInt32`|The number of objects the account owns that are being sponsored by a sponsor. -|`SponsoringOwnerCount`| | |`0`|`number`|`UInt32`|The number of objects the account is sponsoring the reserve for.| -|`SponsoringAccountCount`| | |`0`|`number`|`UInt32`|The number of accounts that the account is sponsoring the reserve for.| + +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ------------------------ | --------- | --------- | ------------- | --------- | ------------- | ----------------------------------------------------------------------------- | +| `SponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the account reserve for this account. | +| `SponsoredOwnerCount` | | | `0` | `number` | `UInt32` | The number of objects the account owns that are being sponsored by a sponsor. | +| `SponsoringOwnerCount` | | | `0` | `number` | `UInt32` | The number of objects the account is sponsoring the reserve for. | +| `SponsoringAccountCount` | | | `0` | `number` | `UInt32` | The number of accounts that the account is sponsoring the reserve for. | #### 5.1.1. `SponsorAccount` @@ -232,8 +236,8 @@ The total account reserve should now be calculated as: $$ \displaylines{ -(acct.SponsorAccount \text{ ? } 0 : acctReserve) + \\ -objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + \\ +(acct.SponsorAccount ext{ ? } 0 : acctReserve) + \ +objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + \ acctReserve * acct.SponsoringAccountCount } $$ @@ -251,31 +255,32 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/led -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -|----------------------|-----------|-----------|---------------|-------------------|--------------|-------------| -| `Balance` | No | Yes | N/A | `object` | `Amount` | The balance of the trust line, from the perspective of the low account. A negative balance indicates that the high account holds tokens issued by the low account. The issuer in this is always set to the neutral value [ACCOUNT_ONE](https://xrpl.org/docs/concepts/accounts/addresses#special-addresses). | -| `Flags` | No | Yes | N/A | `number` | `UInt32` | A bit-map of boolean options enabled for this entry. | -| `HighLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the high account has set on the trust line. The `issuer` is the address of the high account that set this limit. | -| `HighNode` | Yes | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the high account's owner directory links to this entry, in case the directory consists of multiple pages. | -| `HighQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the high account, as an integer in the implied ratio `HighQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `HighQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the high account, as an integer in the implied ratio `HighQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `LedgerEntryType` | Yes | Yes | `RippleState` | `string` | `UInt16` | The value `0x0072`, mapped to the string `RippleState`, indicates that this is a RippleState entry. | -| `LockCount` | No | No | N/A | `object` | `Amount` | The total number of lock balances on a `RippleState` ledger object. | -| `LockedBalance` | No | No | N/A | `object` or `string` | `Amount` | The total number of locked tokens on a `RippleState` ledger object. | -| `LowLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the low account has set on the trust line. The `issuer` is the address of the low account that set this limit. | -| `LowNode` | No | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this entry, in case the directory consists of multiple pages. | -| `LowQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the low account, as an integer in the implied ratio `LowQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `LowQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the low account, as an integer in the implied ratio `LowQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `PreviousTxnID` | No | Yes | N/A | `string` | `UInt256` | The identifying hash of the transaction that most recently modified this entry. | -| `PreviousTxnLgrSeq` | No | Yes | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this entry. | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ------------------- | --------- | --------- | ------------- | -------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `Balance` | No | Yes | N/A | `object` | `Amount` | The balance of the trust line, from the perspective of the low account. A negative balance indicates that the high account holds tokens issued by the low account. The issuer in this is always set to the neutral value [ACCOUNT_ONE](https://xrpl.org/docs/concepts/accounts/addresses#special-addresses). | +| `Flags` | No | Yes | N/A | `number` | `UInt32` | A bit-map of boolean options enabled for this entry. | +| `HighLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the high account has set on the trust line. The `issuer` is the address of the high account that set this limit. | +| `HighNode` | Yes | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the high account's owner directory links to this entry, in case the directory consists of multiple pages. | +| `HighQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the high account, as an integer in the implied ratio `HighQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `HighQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the high account, as an integer in the implied ratio `HighQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `LedgerEntryType` | Yes | Yes | `RippleState` | `string` | `UInt16` | The value `0x0072`, mapped to the string `RippleState`, indicates that this is a RippleState entry. | +| `LockCount` | No | No | N/A | `object` | `Amount` | The total number of lock balances on a `RippleState` ledger object. | +| `LockedBalance` | No | No | N/A | `object` or `string` | `Amount` | The total number of locked tokens on a `RippleState` ledger object. | +| `LowLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the low account has set on the trust line. The `issuer` is the address of the low account that set this limit. | +| `LowNode` | No | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this entry, in case the directory consists of multiple pages. | +| `LowQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the low account, as an integer in the implied ratio `LowQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `LowQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the low account, as an integer in the implied ratio `LowQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | +| `PreviousTxnID` | No | Yes | N/A | `string` | `UInt256` | The identifying hash of the transaction that most recently modified this entry. | +| `PreviousTxnLgrSeq` | No | Yes | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this entry. | This spec proposes these additional fields: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-----------|---------------|-------------| -|`HighSponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the reserve on behalf of the "high" account on the trustline. | -|`LowSponsorAccount`| | |N/A|`string`|`AccountID`| The sponsor that is paying the reserve on behalf of the "low" account on the trustline. | + +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| -------------------- | --------- | --------- | ------------- | --------- | ------------- | ---------------------------------------------------------------------------------------- | +| `HighSponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve on behalf of the "high" account on the trustline. | +| `LowSponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve on behalf of the "low" account on the trustline. | These additional fields are necessary for a trustline since the reserve for this object may be held by two accounts (in the case of a bidirectional trustline). @@ -295,9 +300,9 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/co We propose these modifications: -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | ------------ | -| `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ---------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | #### 7.1.1. `Sponsor` @@ -305,13 +310,13 @@ The `Sponsor` inner object contains all of the information for the sponsorship h The fields contained in this object are: -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | ------------ | -| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsoring account. | -| `Flags` | ✔️ | `number` | `UInt16` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | -| `SigningPubKey` | | `string` | `STBlob` | The `SigningPubKey` for `SponsorAccount`, if single-signing. | -| `Signature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | -| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ---------------- | --------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsoring account. | +| `Flags` | ✔️ | `number` | `UInt16` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | +| `SigningPubKey` | | `string` | `STBlob` | The `SigningPubKey` for `SponsorAccount`, if single-signing. | +| `TxnSignature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | +| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | ##### 7.1.1.1. `Account` @@ -325,38 +330,38 @@ The `Flags` field allows the user to specify which sponsorship type(s) they wish There are two flag values that are supported: -| Flag Name | Flag Value | Description | -| ---------- | ------------ | ------------ | -| `tfSponsorFee` | `0x00000001` | Sponsoring (paying for) the fee of the transaction. | +| Flag Name | Flag Value | Description | +| ------------------ | ------------ | ------------------------------------------------------------------ | +| `tfSponsorFee` | `0x00000001` | Sponsoring (paying for) the fee of the transaction. | | `tfSponsorReserve` | `0x00000002` | Sponsoring the reserve for any objects created in the transaction. | This field **will** be a signing field (it will be included in transaction signatures). -##### 7.1.1.3. `SigningPubKey`, `Signature` and `Signers` +##### 7.1.1.3. `SigningPubKey`, `TxnSignature` and `Signers` -Either `Signature` or `Signers` must be included in the final transaction. +Either `TxnSignature` or `Signers` must be included in the final transaction. -There will be no additional transaction fee required for the use of the `Signature` field. +There will be no additional transaction fee required for the use of the `TxnSignature` field. -`Signature` and `Signers` **will not** be signing fields (they will not be included in transaction signatures, though they will still be included in the stored transaction). +`TxnSignature` and `Signers` **will not** be signing fields (they will not be included in transaction signatures, though they will still be included in the stored transaction). -Either `SigningPubKey`+`Signature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. +Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. ### 7.2. Transaction Fee -If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\\# signatures+1)*base\textunderscore fee$. +If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\# signatures+1)*base extunderscore fee$. -The total fee calculation for signatures will now be $( 1+\\# tx.Signers + \\# tx.Sponsor.Signers) * base\textunderscore fee$ (plus transaction-specific fees). +The total fee calculation for signatures will now be $( 1+\# tx.Signers + \# tx.Sponsor.Signers) * base extunderscore fee$ (plus transaction-specific fees). ### 7.3. Failure Conditions #### 7.3.1. General Failures -- `Sponsor.Signature` is invalid. +- `Sponsor.TxnSignature` is invalid. - `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). - The `SponsorAccount` doesn't exist on the ledger. - An invalid sponsorship flag is used. -- `Sponsor.SigningPubKey`, `Sponsor.Signature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). +- `Sponsor.SigningPubKey`, `Sponsor.TxnSignature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). #### 7.3.2. Fee Sponsorship Failures @@ -428,25 +433,25 @@ This transaction creates and updates the `Sponsorship` object. ### 8.1. Fields -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | ------------ | -| `TransactionType` |✔️|`string`|`UInt16`|The transaction type (`SponsorshipSet`).| -| `Account` |✔️|`string`|`AccountID`|The account sending the transaction. This may be either the sponsor or the sponsee. | -| `SponsorAccount` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | -| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `SponsorAccount`. | -| `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | -| `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | -| `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ----------------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipSet`). | +| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the sponsor or the sponsee. | +| `SponsorAccount` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | +| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `SponsorAccount`. | +| `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | +| `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | ### 8.2. Flags -| Flag Name | Flag Value | Description | -| --------- | ---------- | ----------- | -| `tfSponsorshipSetRequireSignForFee` | `0x00010000` | Adds the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfSponsorshipClearRequireSignForFee` | `0x00020000` | Removes the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfSponsorshipSetRequireSignForReserve` | `0x00040000` | Adds the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `tfDeleteObject` | `0x00100000` | Removes the ledger object. | +| Flag Name | Flag Value | Description | +| ----------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------- | +| `tfSponsorshipSetRequireSignForFee` | `0x00010000` | Adds the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipClearRequireSignForFee` | `0x00020000` | Removes the restriction that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipSetRequireSignForReserve` | `0x00040000` | Adds the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| `tfDeleteObject` | `0x00100000` | Removes the ledger object. | ### 8.2. Failure Conditions @@ -478,12 +483,12 @@ This transaction transfers a sponsor relationship for a particular ledger object ### 9.1. Fields -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | ------------ | -| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | -| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | -| `ObjectID` | | `string` | `UInt256` | The -| `Sponsor` | | `object` | `STObject` | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ----------------- | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------- | +| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | +| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | +| `ObjectID` | | `string` | `UInt256` | The | +| `Sponsor` | | `object` | `STObject` | #### 9.1.1. `ObjectID` @@ -535,9 +540,9 @@ All failure conditions mentioned in section [6.3](#63-failure-conditions) still ## 10. Transaction: `Payment` -A Payment transaction represents a transfer of value from one account to another. (Depending on the path taken, this can involve additional exchanges of value, which occur atomically.) This transaction type can be used for several [types of payments](https://xrpl.org/docs/references/protocol/transactions/types/payment#types-of-payments). +A Payment transaction represents a transfer of value from one account to another. (Depending on the path taken, this can involve additional exchanges of value, which occur atomically.) This transaction type can be used for several [types of payments](https://xrpl.org/docs/references/protocol/transactions/types/payment#types-of-payments). -Payments are also the only way to [create accounts](https://xrpl.org/docs/references/protocol/transactions/types/payment#creating-accounts). +Payments are also the only way to [create accounts](https://xrpl.org/docs/references/protocol/transactions/types/payment#creating-accounts). As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment) are the fields that `Payment` currently has. This amendment proposes no changes to the fields, only to the flags and behavior. @@ -545,16 +550,16 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/ty As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment#payment-flags) are the flags that `Payment` currently has: -| Flag Name | Flag Value | Description | -|-----------|------------|-------------| -| `tfNoRippleDirect` | `0x00010000` | Do not use the default path; only use paths included in the `Paths` field. This is intended to force the transaction to take arbitrage opportunities. Most clients do not need this. | +| Flag Name | Flag Value | Description | +| ------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `tfNoRippleDirect` | `0x00010000` | Do not use the default path; only use paths included in the `Paths` field. This is intended to force the transaction to take arbitrage opportunities. Most clients do not need this. | | `tfPartialPayment` | `0x00020000` | If the specified `Amount` cannot be sent without spending more than `SendMax`, reduce the received amount instead of failing outright. See [Partial Payments](#partial-payments) for more details. | -| `tfLimitQuality` | `0x00040000` | Only take paths where all the conversions have an input:output ratio that is equal or better than the ratio of `Amount`:`SendMax`. See [Limit Quality](#limit-quality) for details. | +| `tfLimitQuality` | `0x00040000` | Only take paths where all the conversions have an input:output ratio that is equal or better than the ratio of `Amount`:`SendMax`. See [Limit Quality](#limit-quality) for details. | This spec proposes the following additions: -| Flag Name | Flag Value | Description | -|-----------|------------|-------------| +| Flag Name | Flag Value | Description | +| ------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `tfSponsorCreatedAccount` | `0x00080000` | This flag is only valid if the `Payment` is used to create an account. If it is enabled, the created account will be sponsored by the `tx.Account`. | ## 11. Transaction: `AccountDelete` @@ -595,21 +600,21 @@ This delegatable granular permission allows an account to sponsor reserves on be The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts: -| Field Name | Required? | JSON Type | Description | -| ----------- | --------- | ---------- | ----------- | -| `account` | ✔️ | `string` | Get ledger entries associated with this account. | -| `deletion_blockers_only` | | `boolean` | If `true`, only return ledger entries that would block this account from being deleted. The default is `false`. | -| `ledger_hash` | | `string` | The unique hash of the ledger version to use. | -| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | -| `limit` | | `number` | The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`. | -| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | -| `type` | | `string` | Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types. | +| Field Name | Required? | JSON Type | Description | +| ------------------------ | --------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `account` | ✔️ | `string` | Get ledger entries associated with this account. | +| `deletion_blockers_only` | | `boolean` | If `true`, only return ledger entries that would block this account from being deleted. The default is `false`. | +| `ledger_hash` | | `string` | The unique hash of the ledger version to use. | +| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | +| `limit` | | `number` | The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`. | +| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | +| `type` | | `string` | Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types. | We propose this additional field: -| Field Name | Required? | JSON Type | Description | -| ----------- | --------- | --------- | ----------- | -| `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | +| Field Name | Required? | JSON Type | Description | +| ----------- | --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | ### 14.2. Response Fields @@ -621,30 +626,30 @@ The `account_sponsoring` RPC method is used to fetch a list of objects that an a ### 15.1. Request Fields -| Field Name | Required? | JSON Type | Description | -| ----------- | --------- | ---------- | ------------ | -| `account` | ✔️ | `string` | The sponsor in question. | -| `deletion_blockers_only` | | `boolean` | If `true`, the response only includes objects that would block this account from being deleted. The default is `false`. | -| `ledger_hash` | | `string` | A hash representing the ledger version to use. | -| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | -| `limit` | | `number` | The maximum number of objects to include in the results. | -| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | -| `type` | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`. | +| Field Name | Required? | JSON Type | Description | +| ------------------------ | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `account` | ✔️ | `string` | The sponsor in question. | +| `deletion_blockers_only` | | `boolean` | If `true`, the response only includes objects that would block this account from being deleted. The default is `false`. | +| `ledger_hash` | | `string` | A hash representing the ledger version to use. | +| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | +| `limit` | | `number` | The maximum number of objects to include in the results. | +| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | +| `type` | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`. | ### 15.2. Response Fields The response fields are nearly identical to `account_objects`. -| Field Name | Always Present? | JSON Type | Description | -| ---------- | --------------- | --------- | ----------- | -| `account` | ✔️ | `string` | The account this request corresponds to. | -| `sponsored_objects` | ✔️ | `array` | Array of ledger entries in this account's owner directory. This includes entries that are owned by this account and entries that are linked to this account but owned by someone else, such as escrows where this account is the destination. Each member is a ledger entry in its raw ledger format. This may contain fewer entries than the maximum specified in the `limit` field. | -| `ledger_hash` | | `string` | The identifying hash of the ledger that was used to generate this response. | -| `ledger_index` | | `number` | The ledger index of the ledger that was used to generate this response. | -| `ledger_current_index` | | `number` | The ledger index of the open ledger that was used to generate this response. | -| `limit` | | `number` | The limit that was used in this request, if any. | -| `marker` | | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. | -| `validated` | | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change. | +| Field Name | Always Present? | JSON Type | Description | +| ---------------------- | --------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `account` | ✔️ | `string` | The account this request corresponds to. | +| `sponsored_objects` | ✔️ | `array` | Array of ledger entries in this account's owner directory. This includes entries that are owned by this account and entries that are linked to this account but owned by someone else, such as escrows where this account is the destination. Each member is a ledger entry in its raw ledger format. This may contain fewer entries than the maximum specified in the `limit` field. | +| `ledger_hash` | | `string` | The identifying hash of the ledger that was used to generate this response. | +| `ledger_index` | | `number` | The ledger index of the ledger that was used to generate this response. | +| `ledger_current_index` | | `number` | The ledger index of the open ledger that was used to generate this response. | +| `limit` | | `number` | The limit that was used in this request, if any. | +| `marker` | | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. | +| `validated` | | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change. | ## 16. Security @@ -918,7 +923,7 @@ See Appendix B for the alternate designs that were considered and why this one w The answer to this question is still being explored. One possible solution is to add a second field, `Sponsor2`, to handle the other reserve. -### A.16: How does this proposal work in conjunction with [XLS-49d](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0049-multiple-signer-lists)? What signer list(s) have the power to sponsor fees or reserves? +### A.16: How does this proposal work in conjunction with [XLS-49](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0049-multiple-signer-lists)? What signer list(s) have the power to sponsor fees or reserves? Currently, only the global signer list is supported. Another `SignerListID` value could be added to support sponsorship. Transaction values can only go up to $2^{16}$, since the `TransactionType` field is a `UInt16`, but the `SignerListID` field goes up to $2^{32}$, so there is room in the design for additional values that do not correlate to a specific transaction type. @@ -936,19 +941,20 @@ The current design also supports having different sponsors for different objects ### B.2: A Wrapper Transaction -There would be a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56d](https://github.com/XRPLF/XRPL-Standards/discussions/162), that the sponsor would sign. It would contain a sub-transaction from the sponsee. +There would be a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), that the sponsor would sign. It would contain a sub-transaction from the sponsee. It would look something like this: -|FieldName | Required? | JSON Type | Internal Type | -|----------|------------|----------|---------------| -|`TransactionType`|✔️|`string`|`UInt16`| -|`Account`|✔️|`string`|`STAccount`| -|`Fee`|✔️|`string`|`STAmount`| -|`Transaction`|✔️|`object`|`STTx`| -This was a part of a previous version of the spec (inspired by Stellar's [sandwich transaction design](https://developers.stellar.org/docs/learn/encyclopedia/sponsored-reserves#begin-and-end-sponsorships) for their implementation of sponsored reserves), but the existing design felt cleaner. From an implementation perspective, it's easier to have the fee payer as a part of the existing transaction rather than as a part of a wrapper transaction, since that info needs to somehow get passed down the stack. Also, while the wrapper transaction paradigm will be used in XLS-56d, they should be used sparingly in designs - only when necessary - as their flow is rather complicated in the `rippled` code. +| Field Name | Required | JSON Type | Internal Type | Description | +| ----------------- | -------- | --------- | ------------- | ------------------------------------------------------------------------ | +| `TransactionType` | Yes | `string` | `UInt16` | The transaction type (`Relay`). | +| `Account` | Yes | `string` | `STAccount` | The sponsor of the transaction. | +| `Transaction` | Yes | `object` | `STTx` | The sponsee's transaction. | +| `Fee` | Yes | `string` | `STAmount` | The fee for the transaction. This should match the fee in `Transaction`. | + +This was a part of a previous version of the spec (inspired by Stellar's [sandwich transaction design](https://developers.stellar.org/docs/learn/encyclopedia/sponsored-reserves#begin-and-end-sponsorships) for their implementation of sponsored reserves), but the existing design felt cleaner. From an implementation perspective, it's easier to have the fee payer as a part of the existing transaction rather than as a part of a wrapper transaction, since that info needs to somehow get passed down the stack. Also, while the wrapper transaction paradigm will be used in XLS-56, they should be used sparingly in designs - only when necessary - as their flow is rather complicated in the `rippled` code. -In addition, the signing process becomes complicated (as discovered in the process of developing XLS-56d). You have to somehow prevent the sponsor from submitting the as-is signed transaction to the network, without including it in the wrapper transaction. +In addition, the signing process becomes complicated (as discovered in the process of developing XLS-56). You have to somehow prevent the sponsor from submitting the as-is signed transaction to the network, without including it in the wrapper transaction. ### B.3: A Create-Accept-Cancel Flow From 016be8cf03b314640aa28a8d79d48747bf9b92ff Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 23 Sep 2025 16:50:57 -0400 Subject: [PATCH 19/53] Sync Public/Sponsor XLS.md from Obsidian vault --- .../README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index a491a7e0c..275989388 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -453,7 +453,7 @@ This transaction creates and updates the `Sponsorship` object. | `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | | `tfDeleteObject` | `0x00100000` | Removes the ledger object. | -### 8.2. Failure Conditions +### 8.3. Failure Conditions - `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` - Both `SponsorAccount` and `Sponsee` are specified @@ -467,7 +467,7 @@ This transaction creates and updates the `Sponsorship` object. - `tfSponsorshipSetRequireSignForFee` is enabled - `tfSponsorshipSetRequireSignForReserve` is enabled -### 8.3. State Changes +### 8.4. State Changes - If the object already exists: - `Sponsorship.Amount = tx.FeeAmount` @@ -586,11 +586,11 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. -## 12. Permission: `SponsorFee` +## 12. Granular Permission: `SponsorFee` This delegatable granular permission allows an account to sponsor fees on behalf of another account. -## 13. Permission: `SponsorReserve` +## 13. Granular Permission: `SponsorReserve` This delegatable granular permission allows an account to sponsor reserves on behalf of another account. @@ -620,6 +620,10 @@ We propose this additional field: The response fields remain the same. +### 14.3. Failure Conditions + +There are no additional failure conditions. + ## 15. RPC: `account_sponsoring` The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `SponsorAccount` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). @@ -651,6 +655,13 @@ The response fields are nearly identical to `account_objects`. | `marker` | | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. | | `validated` | | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change. | +### 15.3. Failure Conditions + +- Any of the [universal error types](https://xrpl.org/docs/references/http-websocket-apis/api-conventions/error-formatting#universal-errors). +- `invalidParams` - One or more fields are specified incorrectly, or one or more required fields are missing. +- `actNotFound` - The [address](https://xrpl.org/docs/references/protocol/data-types/basic-data-types#addresses) specified in the `account` field of the request does not correspond to an account in the ledger. +- `lgrNotFound` - The ledger specified by the `ledger_hash` or `ledger_index` does not exist, or it does exist but the server does not have it. + ## 16. Security ### 16.1. Security Axioms From 2205c051d595bacc6ec744379fbdb5278585ef01 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 24 Sep 2025 14:54:03 -0400 Subject: [PATCH 20/53] Sync Public/Sponsor XLS.md from Obsidian vault --- XLS-0068-sponsored-fees-and-reserves/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 275989388..6122fd578 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -487,8 +487,8 @@ This transaction transfers a sponsor relationship for a particular ledger object | ----------------- | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------- | | `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | | `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | -| `ObjectID` | | `string` | `UInt256` | The | -| `Sponsor` | | `object` | `STObject` | +| `ObjectID` | | `string` | `UInt256` | The ID of the object to transfer sponsorship. | +| `Sponsor` | | `object` | `STObject` | The new sponsor of the object. This field contains the same subfields as above. | #### 9.1.1. `ObjectID` @@ -533,7 +533,7 @@ All failure conditions mentioned in section [6.3](#63-failure-conditions) still ### 9.6. State Changes -- The `Sponsor` field on the object is changed or deleted. +- The `Sponsor` field on the object is deleted if the `tx.Sponsor.Account` is the object's `Owner`, otherwise the `Sponsor` field is updated to the new `tx.Sponsor.Account`. - The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. - The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. - If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. From b5797a011d09e8ba393f43bb4276091d863d6937 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 24 Sep 2025 14:57:22 -0400 Subject: [PATCH 21/53] Sync Public/Sponsor XLS.md from Obsidian vault --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 6122fd578..8b19172bb 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -5,7 +5,7 @@ author: Mayukha Vadari (@mvadari) proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 created: 2024-05-02 - updated: 2025-09-19 + updated: 2025-09-24 status: Draft category: Amendment requires: 74 From e447b4374aebc9c49f55884d2fbe14cc70a08d92 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 2 Oct 2025 10:29:00 -0400 Subject: [PATCH 22/53] Sync Public/Sponsor XLS.md from Obsidian vault --- XLS-0068-sponsored-fees-and-reserves/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 8b19172bb..e046ada5f 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -349,9 +349,9 @@ Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the trans ### 7.2. Transaction Fee -If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(\# signatures+1)*base extunderscore fee$. +If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base extunderscore fee$. -The total fee calculation for signatures will now be $( 1+\# tx.Signers + \# tx.Sponsor.Signers) * base extunderscore fee$ (plus transaction-specific fees). +The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Sponsor.Signers|) * base\_fee$ (plus transaction-specific fees). ### 7.3. Failure Conditions From 90481c8e3bd7103b0611ae529733ba203b1bd231 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 23 Dec 2025 16:59:29 -0800 Subject: [PATCH 23/53] Sync Public/Sponsor XLS.md from Obsidian vault --- .../README.md | 220 +++++++++--------- 1 file changed, 111 insertions(+), 109 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index e046ada5f..88e64d2e4 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -3,27 +3,31 @@ title: Sponsored Fees and Reserves description: Allow an account to fund fees and reserves on behalf of another account author: Mayukha Vadari (@mvadari) - proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 - created: 2024-05-02 - updated: 2025-09-24 - status: Draft category: Amendment + status: Draft + proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 requires: 74 + created: 2024-05-02 + updated: 2024-12-24
# Sponsored Fees and Reserves ## 1. Abstract +This proposal adds a process for users to maintain control over their keys and account, but to have another account (e.g. a platform) submit the transaction and pay the transaction fee and/or reserves on their behalf. This proposal supports both account and object reserves. + +Similar features on other chains are often called "sponsored transactions", "meta-transactions", or "relays". + +## 2. Motivation + As the blockchain industry grows, many projects want to be able to build on the blockchain, but abstract away the complexities of using the blockchain - users don't need to submit transactions or deal with transaction fees themselves, they can just pay the platform to handle all that complexity (though, of course, the users still control their own keys). Some projects also want to onboard users more easily, allowing users to create accounts without needing to pay for their own account reserve or needing to gift accounts free XRP to cover the reserve (this could get rather expensive if it is exploited). -In order to handle these sorts of use-cases, this proposal adds a process for users to maintain control over their keys and account, but to have another account (e.g. a platform) submit the transaction and pay the transaction fee and/or reserves on their behalf. This proposal supports both account and object reserves. - -Similar features on other chains are often called "sponsored transactions", "meta-transactions", or "relays". +The primary motivation for this design is to enable companies, token issuers, and other entities to reduce onboarding friction for end users by covering transaction fees and reserve requirements on their behalf. Today, users must self-fund both, or companies must essentially donate XRP to users with no controls over how they use it, before interacting with the XRPL. This creates a barrier to entry for use cases such as token distribution, NFT minting, or enterprise onboarding. Sponsorship provides a mechanism for entities with established XRP balances to subsidize these costs while maintaining strong on-chain accountability. -## 2. Overview +## 3. Overview Accounts can include signatures from sponsors in their transactions that will allow the sponsors to pay the transaction fee for the transaction, and/or the reserve for any accounts/objects created in the transaction. @@ -46,7 +50,7 @@ In addition, there will be a modification to the `account_objects` RPC method, a This feature will require an amendment, tentatively titled `Sponsor`. -### 2.1. Terminology +### 3.1. Terminology - **Sponsor**: The account that is covering the reserve or paying the transaction fee on behalf of another account. - **Sponsee**: The account that the sponsor is paying a transaction fee or reserve on behalf of. @@ -56,7 +60,7 @@ This feature will require an amendment, tentatively titled `Sponsor`. - **Sponsor relationship**: The relationship between a sponsor and sponsee. - **Sponsorship type**: The "type" of sponsorship - sponsoring transaction fees vs. sponsoring reserves. -### 2.2. The Sponsorship Flow (Not Pre-Funded) +### 3.2. The Sponsorship Flow (Not Pre-Funded) In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or reserve for the sponsee Alice's transaction. @@ -65,7 +69,7 @@ In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or - Alice adds Spencer's public key and signature to her transaction. - Alice signs and submits her transaction as normal. -### 2.3. The Sponsorship Flow (Pre-Funded) +### 3.3. The Sponsorship Flow (Pre-Funded) In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or reserve for the sponsee Alice's transaction, but would prefer to pre-fund the XRP necessary, so that he does not have to co-sign every single one of Alice's transactions. @@ -76,13 +80,13 @@ In this scenario, the sponsor, Spencer, wants to pay the transaction fee and/or _Note that Spencer does not need to be a part of Alice's signing and submission flow in this example._ -### 2.4. Recouping a Sponsored Object Reserve +### 3.4. Recouping a Sponsored Object Reserve In this scenario, the sponsor, Spencer, would like to re-obtain the reserve that is currently trapped due to his sponsorship of Alice's object. Spencer can submit a `SponsorshipTransfer` transaction, which allows him to pass the onus of the reserve back to Alice, or pass it onto another sponsor. -### 2.5. Recouping a Sponsored Account Reserve +### 3.5. Recouping a Sponsored Account Reserve In this scenario, the sponsor, Spencer, would like to retrieve his reserve from sponsoring Alice's account. @@ -91,9 +95,9 @@ There are two ways in which he could do this: - If Alice is done using her account, she can submit an `AccountDelete` transaction, which will send all remaining funds in the account back to Spencer. - If Alice would like to keep using her account, or would like to switch to a different provider, she (or Spencer) can submit a `SponsorshipTransfer` transaction to either remove sponsorship or transfer it to the new provider. -## 3. Ledger Entries: Common Fields +## 4. Ledger Entries: Common Fields -### 3.1. Fields +### 4.1. Fields As a reference, here are the fields that all ledger objects currently have: @@ -108,13 +112,13 @@ We propose this additional field: | ---------------- | --------- | --------- | ------------- | --------- | ------------- | -------------------------------------------------------------- | | `SponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve for this ledger object. | -## 4. Ledger Entry: `Sponsorship` +## 5. Ledger Entry: `Sponsorship` `Sponsorship` is an object that reflects a sponsoring relationship between two accounts, `SponsorAccount` and `Sponsee`. This allows sponsors to "pre-fund" sponsees, if they so desire. _Note: this object does not need to be created in order to sponsor accounts. It is an offered convenience, so that sponsors do not have to co-sign every sponsored transaction if they don't want to, especially for transaction fees. It also allows them to set a maximum balance even if they still want to co-sign transactions._ -### 4.1. Object ID +### 5.1. Object ID The key of the `Sponsorship` 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: @@ -122,7 +126,7 @@ The key of the `Sponsorship` object is the result of [`SHA512-Half`](https://xrp - The `AccountID` of the `Sponsor` - The `AccountID` of the `Sponsee` -### 4.2. Fields +### 5.2. Fields | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | | ------------------- | --------- | --------- | ------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- | @@ -136,7 +140,7 @@ The key of the `Sponsorship` object is the result of [`SHA512-Half`](https://xrp | `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this entry. | | `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | -### 4.3. Flags +### 5.3. Flags There are two flags on this object: @@ -145,35 +149,35 @@ There are two flags on this object: | `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | | `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -### 4.4. Ownership +### 5.4. Ownership The object is owned by `Sponsor`, who also pays the reserve. -### 4.5. Reserve +### 5.5. Reserve This object charges 1 reserve. -### 4.6. Deletion +### 5.6. Deletion This object will be deleted any time the `FeeAmount` and `ReserveCount` are both `0`. This can be done directly via `SponsorshipSet`, or can occur in the regular flow of transactions, if the sponsorship runs out. This object is a [deletion blocker](https://xrpl.org/docs/concepts/accounts/deleting-accounts/#requirements). -### 4.7. Invariant Checks +### 5.7. Invariant Checks - `FeeAmount` >= 0 || `ReserveCount` >= 0 - `SponsorAccount` != `Sponsee` - `FeeAmount` is nonnegative and denominated in XRP -### 4.8. RPC Name +### 5.8. RPC Name The `snake_case` form of the ledger object name is `sponsorship`. -## 5. Ledger Entry: `AccountRoot` +## 6. Ledger Entry: `AccountRoot` An `AccountRoot` ledger entry type describes a single [account](https://xrpl.org/docs/concepts/accounts), its settings, and XRP balance. -### 5.1. Fields +### 6.1. Fields
@@ -218,7 +222,7 @@ This spec proposes these additional fields: | `SponsoringOwnerCount` | | | `0` | `number` | `UInt32` | The number of objects the account is sponsoring the reserve for. | | `SponsoringAccountCount` | | | `0` | `number` | `UInt32` | The number of accounts that the account is sponsoring the reserve for. | -#### 5.1.1. `SponsorAccount` +#### 6.1.1. `SponsorAccount` The `SponsorAccount` field is already added in the ledger common fields (see section [3.1.1](#311-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. @@ -226,7 +230,7 @@ This field is included if the account was created with a sponsor paying its acco _Note: The `Destination` field of `AccountDelete` will still work as-is if the account is not sponsored, where it can be set to any account._ -### 5.2. Account Reserve Calculation +### 6.2. Account Reserve Calculation The existing reserve calculation is: @@ -236,17 +240,15 @@ The total account reserve should now be calculated as: $$ \displaylines{ -(acct.SponsorAccount ext{ ? } 0 : acctReserve) + \ -objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + \ -acctReserve * acct.SponsoringAccountCount +(acct.SponsorAccount ext{ ? } 0 : acctReserve) + \nobjReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + \nacctReserve * acct.SponsoringAccountCount } $$ -## 6. Ledger Entry: `RippleState` +## 7. Ledger Entry: `RippleState` A `RippleState` ledger entry represents a [trust line](https://xrpl.org/docs/concepts/tokens/fungible-tokens) between two accounts. Each account can change its own limit and other settings, but the balance is a single shared value. A trust line that is entirely in its default state is considered the same as a trust line that does not exist and is automatically deleted. You can create or modify a trust line with a [TrustSet transaction](https://xrpl.org/docs/references/protocol/transactions/types/trustset). -### 6.1. Fields +### 7.1. Fields
@@ -284,15 +286,15 @@ This spec proposes these additional fields: These additional fields are necessary for a trustline since the reserve for this object may be held by two accounts (in the case of a bidirectional trustline). -### 6.2. Invariants +### 7.2. Invariants Existing invariants remain. The common field `SponsorAccount` must not be on any `RippleState` objects (they must use `HighSponsorAccount` and `LowSponsorAccount` instead). -## 7. Transactions: Common Fields +## 8. Transactions: Common Fields -### 7.1. Fields +### 8.1. Fields As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/common-fields/) are the fields that all transactions currently have. @@ -304,7 +306,7 @@ We propose these modifications: | ---------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | -#### 7.1.1. `Sponsor` +#### 8.1.1. `Sponsor` The `Sponsor` inner object contains all of the information for the sponsorship happening in the transaction. @@ -318,13 +320,13 @@ The fields contained in this object are: | `TxnSignature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | | `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | -##### 7.1.1.1. `Account` +##### 8.1.1.1. `Account` The `Sponsor.Account` field represents the sponsor. This field **will** be a signing field (it will be included in transaction signatures). -##### 7.1.1.2. `Flags` +##### 8.1.1.2. `Flags` The `Flags` field allows the user to specify which sponsorship type(s) they wish to participate in. At least one flag **must** be specified if the `Sponsor` field is included in a transaction. @@ -337,7 +339,7 @@ There are two flag values that are supported: This field **will** be a signing field (it will be included in transaction signatures). -##### 7.1.1.3. `SigningPubKey`, `TxnSignature` and `Signers` +##### 8.1.1.3. `SigningPubKey`, `TxnSignature` and `Signers` Either `TxnSignature` or `Signers` must be included in the final transaction. @@ -347,15 +349,15 @@ There will be no additional transaction fee required for the use of the `TxnSign Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. -### 7.2. Transaction Fee +### 8.2. Transaction Fee If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base extunderscore fee$. The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Sponsor.Signers|) * base\_fee$ (plus transaction-specific fees). -### 7.3. Failure Conditions +### 8.3. Failure Conditions -#### 7.3.1. General Failures +#### 8.3.1. General Failures - `Sponsor.TxnSignature` is invalid. - `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). @@ -363,7 +365,7 @@ The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Spo - An invalid sponsorship flag is used. - `Sponsor.SigningPubKey`, `Sponsor.TxnSignature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). -#### 7.3.2. Fee Sponsorship Failures +#### 8.3.2. Fee Sponsorship Failures - The sponsor's account does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) @@ -380,7 +382,7 @@ If a `Sponsorship` object does not exist: Note: if a transaction doesn't charge a fee (such as an account's first `SetRegularKey` transaction), the transaction will still succeed. -#### 7.3.3. Reserve Sponsorship Failures +#### 8.3.3. Reserve Sponsorship Failures - The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) - The transaction does not support reserve sponsorship (see section 6.3.4) @@ -396,7 +398,7 @@ If a `Sponsorship` object does not exist: Note: if a transaction doesn't charge a reserve (such as `AccountSet`), the transaction will still succeed. -#### 7.3.4. Transactions that cannot be sponsored +#### 8.3.4. Transactions that cannot be sponsored All transactions (other than pseudo-transactions) may use the `tfSponsorFee` flag, since they all have a fee. @@ -409,15 +411,15 @@ However, some transactions will not support the `tfSponsorReserve` flag. Also, many transactions, such as `AccountSet`, will have no change in output when using the `tfSponsorReserve` flag, if they do not create any new objects or accounts. -### 7.4. State Changes +### 8.4. State Changes -#### 7.4.1. Fee Sponsorship State Changes +#### 8.4.1. Fee Sponsorship State Changes If a `Sponsorship` object exists, the `tx.Fee` value is decremented from the `Sponsorship.FeeAmount`. If a `Sponsorship` object does not exist, the `tx.Fee` value is decremented from the sponsor's `AccountRoot.Balance`. -#### 7.4.2. Reserve Sponsorship State Changes +#### 8.4.2. Reserve Sponsorship State Changes Any account/object that is created as a part of the transaction will have a `Sponsor` field. @@ -427,11 +429,11 @@ The sponsee's `SponsoredOwnerCount` field will be incremented by the number of o The `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` fields will be decremented when those objects/accounts are deleted. -## 8. Transaction: `SponsorshipSet` +## 9. Transaction: `SponsorshipSet` This transaction creates and updates the `Sponsorship` object. -### 8.1. Fields +### 9.1. Fields | Field Name | Required? | JSON Type | Internal Type | Description | | ----------------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -443,7 +445,7 @@ This transaction creates and updates the `Sponsorship` object. | `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | | `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | -### 8.2. Flags +### 9.2. Flags | Flag Name | Flag Value | Description | | ----------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------- | @@ -453,7 +455,7 @@ This transaction creates and updates the `Sponsorship` object. | `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | | `tfDeleteObject` | `0x00100000` | Removes the ledger object. | -### 8.3. Failure Conditions +### 9.3. Failure Conditions - `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` - Both `SponsorAccount` and `Sponsee` are specified @@ -467,7 +469,7 @@ This transaction creates and updates the `Sponsorship` object. - `tfSponsorshipSetRequireSignForFee` is enabled - `tfSponsorshipSetRequireSignForReserve` is enabled -### 8.4. State Changes +### 9.4. State Changes - If the object already exists: - `Sponsorship.Amount = tx.FeeAmount` @@ -477,11 +479,11 @@ This transaction creates and updates the `Sponsorship` object. - If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. - _Note: this does not affect already-sponsored entries and accounts. Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process._ -## 9. Transaction: `SponsorshipTransfer` +## 10. Transaction: `SponsorshipTransfer` This transaction transfers a sponsor relationship for a particular ledger object's object reserve. The sponsor relationship can either be passed on to a new sponsor, or dissolved entirely (with the sponsee taking on the reserve). Either the sponsor or sponsee may submit this transaction at any point in time. -### 9.1. Fields +### 10.1. Fields | Field Name | Required? | JSON Type | Internal Type | Description | | ----------------- | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------- | @@ -490,13 +492,13 @@ This transaction transfers a sponsor relationship for a particular ledger object | `ObjectID` | | `string` | `UInt256` | The ID of the object to transfer sponsorship. | | `Sponsor` | | `object` | `STObject` | The new sponsor of the object. This field contains the same subfields as above. | -#### 9.1.1. `ObjectID` +#### 10.1.1. `ObjectID` This field should be included if this transaction is dealing with sponsored object, rather than on a sponsored account. This field indicates which object the relationship is changing for. If it is not included, then it refers to the account sending the transaction. -#### 9.1.2. `Sponsor` +#### 10.1.2. `Sponsor` The `Sponsor` field is already added in the transaction common fields (see section [6.1.1](#611-sponsor)), but it has some additional rules associated with it on the `SponsorshipTransfer` transaction. @@ -504,13 +506,13 @@ In this case, if `Sponsor` is included with the `tfSponsorReserve` flag, then th If there is no `Sponsor` field, or if the `tfSponsorReserve` flag is not included, then the burden of the reserve will be passed back to the ledger object's owner (the former sponsee). -### 9.2. Ending the Sponsorship for a Sponsored Ledger Object +### 10.2. Ending the Sponsorship for a Sponsored Ledger Object A sponsored ledger object will have the `Sponsor` field attached to it. Ending the sponsor relationship for a sponsored ledger object requires the `ObjectID` parameter, to specify which ledger object. Two accounts are allowed to submit a `SponsorshipTransfer` relationship to end the sponsor relationship for a sponsored ledger object: either the sponsor for that object or the owner of that object (the sponsee). -### 9.3. Migrating a Sponsorship to a New Account +### 10.3. Migrating a Sponsorship to a New Account A sponsorship can be migrated to a new account by including the `Sponsor` field with the `tfSponsorReserve` flag. This can be done for either a sponsored account or a sponsored ledger object. @@ -518,11 +520,11 @@ Two accounts are allowed to submit a `SponsorshipTransfer` relationship to migra The sponsor will likely only rarely want to do this (such as if they are transferring accounts), but the sponsee may want to migrate if they change providers. -### 9.4. Transaction Fee +### 10.4. Transaction Fee The transaction fee for this transaction will be the base fee (currently 10 drops). -### 9.5. Failure Conditions +### 10.5. Failure Conditions All failure conditions mentioned in section [6.3](#63-failure-conditions) still apply here. @@ -531,14 +533,14 @@ All failure conditions mentioned in section [6.3](#63-failure-conditions) still - The new sponsor does not exist. - The `tx.Account` neither the sponsor nor the owner (sponsee) of `ObjectID`. -### 9.6. State Changes +### 10.6. State Changes - The `Sponsor` field on the object is deleted if the `tx.Sponsor.Account` is the object's `Owner`, otherwise the `Sponsor` field is updated to the new `tx.Sponsor.Account`. - The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. - The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. - If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. -## 10. Transaction: `Payment` +## 11. Transaction: `Payment` A Payment transaction represents a transfer of value from one account to another. (Depending on the path taken, this can involve additional exchanges of value, which occur atomically.) This transaction type can be used for several [types of payments](https://xrpl.org/docs/references/protocol/transactions/types/payment#types-of-payments). @@ -546,7 +548,7 @@ Payments are also the only way to [create accounts](https://xrpl.org/docs/refere As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment) are the fields that `Payment` currently has. This amendment proposes no changes to the fields, only to the flags and behavior. -### 10.1. Flags +### 11.1. Flags As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment#payment-flags) are the flags that `Payment` currently has: @@ -562,13 +564,13 @@ This spec proposes the following additions: | ------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `tfSponsorCreatedAccount` | `0x00080000` | This flag is only valid if the `Payment` is used to create an account. If it is enabled, the created account will be sponsored by the `tx.Account`. | -## 11. Transaction: `AccountDelete` +## 12. Transaction: `AccountDelete` This transaction deletes an account. As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete) are the fields that `AccountDelete` currently has. This amendment proposes no changes to the fields, only to the behavior. -### 11.1. Failure Conditions +### 12.1. Failure Conditions Existing failure conditions still apply. @@ -578,7 +580,7 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie If the `AccountRoot` associated with the `tx.Account` has a `SponsoringOwnerCount` or `SponsoringAccountCount` field, the transaction will fail with `tecHAS_OBLIGATIONS`. -### 11.2. State Changes +### 12.2. State Changes Existing state changes still apply, including rules around deletion blockers. @@ -586,17 +588,17 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. -## 12. Granular Permission: `SponsorFee` +## 13. Granular Permission: `SponsorFee` This delegatable granular permission allows an account to sponsor fees on behalf of another account. -## 13. Granular Permission: `SponsorReserve` +## 14. Granular Permission: `SponsorReserve` This delegatable granular permission allows an account to sponsor reserves on behalf of another account. -## 14. RPC: `account_objects` +## 15. RPC: `account_objects` -### 14.1. Request Fields +### 15.1. Request Fields The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts: @@ -616,19 +618,19 @@ We propose this additional field: | ----------- | --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | -### 14.2. Response Fields +### 15.2. Response Fields The response fields remain the same. -### 14.3. Failure Conditions +### 15.3. Failure Conditions There are no additional failure conditions. -## 15. RPC: `account_sponsoring` +## 16. RPC: `account_sponsoring` The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `SponsorAccount` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). -### 15.1. Request Fields +### 16.1. Request Fields | Field Name | Required? | JSON Type | Description | | ------------------------ | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------- | @@ -640,7 +642,7 @@ The `account_sponsoring` RPC method is used to fetch a list of objects that an a | `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | | `type` | | `string` | Filter results by a ledger entry type. Some examples are `offer` and `escrow`. | -### 15.2. Response Fields +### 16.2. Response Fields The response fields are nearly identical to `account_objects`. @@ -655,16 +657,16 @@ The response fields are nearly identical to `account_objects`. | `marker` | | `any` | Server-defined value indicating the response is paginated. Pass this to the next call to resume where this call left off. Omitted when there are no additional pages after this one. | | `validated` | | `boolean` | If `true`, the information in this response comes from a validated ledger version. Otherwise, the information is subject to change. | -### 15.3. Failure Conditions +### 16.3. Failure Conditions - Any of the [universal error types](https://xrpl.org/docs/references/http-websocket-apis/api-conventions/error-formatting#universal-errors). - `invalidParams` - One or more fields are specified incorrectly, or one or more required fields are missing. - `actNotFound` - The [address](https://xrpl.org/docs/references/protocol/data-types/basic-data-types#addresses) specified in the `account` field of the request does not correspond to an account in the ledger. - `lgrNotFound` - The ledger specified by the `ledger_hash` or `ledger_index` does not exist, or it does exist but the server does not have it. -## 16. Security +## 17. Security -### 16.1. Security Axioms +### 17.1. Security Axioms Both the sponsee _and_ the sponsor must agree to enter into a sponsor relationship. The sponsee must actively consent to the sponsor handling the reserve, and the sponsor must be willing to take on that reserve. A signature from both parties ensures that this is the case. @@ -680,37 +682,37 @@ A sponsee cannot take advantage of the generosity of their sponsor, since the sp An axiom that is out of scope: the sponsee may not have any control over a sponsorship transfer (the sponsor may transfer a sponsorship without the sponsee's consent). This is akin to a loanee having no control over a bank selling their mortgage to some other company, or a lender selling debt to a debt collection agency. -### 16.2. Signatures +### 17.2. Signatures Since a fee sponsorship must approve of the `Fee` field, and a reserve sponsorship must approve of a broad set of transaction fields, the sponsor must always sign the whole transaction. This also avoids needing to have different sponsorship processes for different sponsorship types. This includes the non-signature parts of the `Sponsor` object (`Sponsor.Account` and `Sponsor.Flags`). The same is true for the sponsee's transaction signature; the sponsee must approve of the sponsor and sponsorship type. A sponsor's `Signature` cannot be replayed or attached to a different transaction, since the whole transaction (including the `Account` and `Sequence` values) must be signed. -## 17. Invariants +## 18. Invariants An [invariant](https://xrpl.org/docs/concepts/consensus-protocol/invariant-checking/) is a statement, usually an equation, that must always be true for every valid ledger state on the XRPL. Invariant checks serve as a last line of defense against bugs; the `tecINVARIANT_FAILED` error is thrown if an invariant is violated (which ideally should never happen). -### 17.1. Tracking Owner Counts +### 18.1. Tracking Owner Counts A transaction that creates a ledger object either increments an account's `OwnerCount` by 1 or increments two separate accounts' `SponsoringOwnerCount` and `SponsoredOwnerCount` by 1. The opposite happens when a ledger object is deleted. The equivalent also should happen with `SponsoringAccountCount`. -### 17.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount` +### 18.2. Balancing `SponsoredOwnerCount` and `SponsoringOwnerCount` $$ \sum*{accounts} Account.SponsoredOwnerCount = \sum*{accounts} Account.SponsoringOwnerCount $$ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to the sum of all accounts' `SponsoringOwnerCount`s. This ensures that every sponsored object is logged as being sponsored and also has a sponsor. -## 18. Example Flows +## 19. Example Flows Each example will show what the transaction will look like before **and** after both the sponsor and sponsee sign the transaction. The unsigned transaction must be autofilled before it is passed to the sponsor to sign. Tooling can be updated to handle combining the sponsor and sponsee signatures, similar to helper functions that already exist for multisigning. -### 18.1. Fee Sponsorship +### 19.1. Fee Sponsorship -#### 18.1.1. The Unsigned Transaction +#### 19.1.1. The Unsigned Transaction
@@ -731,7 +733,7 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t
-#### 18.1.2. The Signed Transaction +#### 19.1.2. The Signed Transaction
@@ -756,11 +758,11 @@ The unsigned transaction must be autofilled before it is passed to the sponsor t
-### 18.2. Account Sponsorship +### 19.2. Account Sponsorship The only way an account can be created is via a `Payment` transaction. So the sponsor relationship must be initiated on the `Payment` transaction. -#### 18.2.1. The Unsigned Transaction +#### 19.2.1. The Unsigned Transaction
@@ -781,7 +783,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-#### 18.2.2. The Signed Transaction +#### 19.2.2. The Signed Transaction
@@ -806,9 +808,9 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-### 18.3. Object Sponsorship +### 19.3. Object Sponsorship -#### 18.3.1. The Unsigned Transaction +#### 19.3.1. The Unsigned Transaction
@@ -828,7 +830,7 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-#### 18.3.2. The Signed Transaction +#### 19.3.2. The Signed Transaction
@@ -852,11 +854,11 @@ The only way an account can be created is via a `Payment` transaction. So the sp
-## 19. Rationale +## 20. Rationale The primary motivation for this design is to enable companies, token issuers, and other entities to reduce onboarding friction for end users by covering transaction fees and reserve requirements on their behalf. Today, users must self-fund both, or companies must essentially donate XRP to users with no controls over how they use it, before interacting with the XRPL. This creates a barrier to entry for use cases such as token distribution, NFT minting, or enterprise onboarding. Sponsorship provides a mechanism for entities with established XRP balances to subsidize these costs while maintaining strong on-chain accountability. -## n+1. Remaining TODOs/Open Questions +## 21. n+1. Remaining TODOs/Open Questions - How will this work for objects like trustlines, where multiple accounts might be holding reserves for it? - Maybe a second `Sponsor` field or something? @@ -866,13 +868,13 @@ The primary motivation for this design is to enable companies, token issuers, an # Appendix -## Appendix A: FAQ +## 1. Appendix A: FAQ -### A.1: Does the sponsee receive any XRP for the reserve? +### 1.1. A.1: Does the sponsee receive any XRP for the reserve? No, there is no XRP transfer in a sponsorship relationship - the XRP stays in the sponsor's account. The burden of the reserve for that object/account is just transferred to the sponsor. -### A.2: What happens if you try to delete your account and you have sponsored objects? +### 1.2. A.2: What happens if you try to delete your account and you have sponsored objects? If the account itself is sponsored, then it can be deleted, but the destination of the `AccountDelete` transaction (in other words, where the leftover XRP goes) **must** be the sponsor's account. This ensures that the sponsor gets their reserve back, and the sponsee cannot run away with those funds. @@ -880,27 +882,27 @@ If the sponsee still has sponsored objects, those objects will follow the same r If a sponsored object is deleted (either due to normal object deletion processes or, in the case of objects that aren't deletion blockers, because the owner account is deleted), the sponsor's reserve becomes available again. -### A.3: What if a sponsor that is sponsoring a few objects wants to delete their account? +### 1.3. A.3: What if a sponsor that is sponsoring a few objects wants to delete their account? An account cannot be deleted if it is sponsoring **any** existing accounts or objects. They will need to either delete those objects (by asking the owner to do so, as they cannot do so directly) or use the `SponsorshipTransfer` transaction to relinquish control of them. -### A.4: Does a sponsor have any powers over an object they pay the reserve for? I.e. can they delete the object? +### 1.4. A.4: Does a sponsor have any powers over an object they pay the reserve for? I.e. can they delete the object? No. If a sponsor no longer wants to support an object, they can always use the `SponsorshipTransfer` transaction instead to transfer the reserve burden back to the sponsee. -### A.5: What if a sponsee refuses to delete their account when a sponsor wants to stop supporting their account? +### 1.5. A.5: What if a sponsee refuses to delete their account when a sponsor wants to stop supporting their account? The sponsor will have the standard problem of trying to get ahold of a debtor to make them pay. They may use the `SponsorshipTransfer` transaction to put the onus on the sponsee. If the sponsee does not have enough XRP to cover the reserve for those objects, they will not be able to create any more objects until they do so. -### A.6: What happens if the sponsor tries to `SponsorshipTransfer` but the sponsee doesn't have enough funds to cover the reserve? +### 1.6. A.6: What happens if the sponsor tries to `SponsorshipTransfer` but the sponsee doesn't have enough funds to cover the reserve? If the sponsor really needs to get out of the sponsor relationship ASAP without recouping the value of the reserve, they can pay the sponsee the amount of XRP they need to cover the reserve. These steps can be executed atomically via a [Batch transaction](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), to ensure that the sponsee can't do something else with the funds before the `SponsorshipTransfer` transaction is validated. -### A.7: Would sponsored accounts carry a lower reserve? +### 1.7. A.7: Would sponsored accounts carry a lower reserve? No, they would still carry a reserve of 1 XRP at current levels. -### A.8: Can an existing unsponsored ledger object/account be sponsored? +### 1.8. A.8: Can an existing unsponsored ledger object/account be sponsored? Yes, with the `SponsorshipTransfer` transaction. @@ -938,9 +940,9 @@ The answer to this question is still being explored. One possible solution is to Currently, only the global signer list is supported. Another `SignerListID` value could be added to support sponsorship. Transaction values can only go up to $2^{16}$, since the `TransactionType` field is a `UInt16`, but the `SignerListID` field goes up to $2^{32}$, so there is room in the design for additional values that do not correlate to a specific transaction type. -## Appendix B: Alternate Designs +## 2. Appendix B: Alternate Designs -### B.1: Add a `Sponsor` to the account +### 2.1. B.1: Add a `Sponsor` to the account This design involved updating `AccountSet` to allow users to add a `Sponsor` to their account (with a signature from the sponsor as well). The sponsor would then sponsor every object from that account while the field was active, and either the sponsor or the account could remove the sponsorship at any time. @@ -950,7 +952,7 @@ The current design also supports having different sponsors for different objects -### B.2: A Wrapper Transaction +### 2.2. B.2: A Wrapper Transaction There would be a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), that the sponsor would sign. It would contain a sub-transaction from the sponsee. @@ -967,7 +969,7 @@ This was a part of a previous version of the spec (inspired by Stellar's [sandwi In addition, the signing process becomes complicated (as discovered in the process of developing XLS-56). You have to somehow prevent the sponsor from submitting the as-is signed transaction to the network, without including it in the wrapper transaction. -### B.3: A Create-Accept-Cancel Flow +### 2.3. B.3: A Create-Accept-Cancel Flow The rough idea of this design was to have a new set of transactions (e.g. `SponsorCreate`/`SponsorAccept`/`SponsorCancel`/`SponsorFinish`) where a sponsor could take on the reserve for an existing object. From c257fe93e8c125a6fbb2dd79c29cf7a3f979cd71 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 23 Dec 2025 17:04:57 -0800 Subject: [PATCH 24/53] oops --- .../README.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 88e64d2e4..d963e8eba 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -868,13 +868,13 @@ The primary motivation for this design is to enable companies, token issuers, an # Appendix -## 1. Appendix A: FAQ +## Appendix A: FAQ -### 1.1. A.1: Does the sponsee receive any XRP for the reserve? +### A.1: Does the sponsee receive any XRP for the reserve? No, there is no XRP transfer in a sponsorship relationship - the XRP stays in the sponsor's account. The burden of the reserve for that object/account is just transferred to the sponsor. -### 1.2. A.2: What happens if you try to delete your account and you have sponsored objects? +### A.2: What happens if you try to delete your account and you have sponsored objects? If the account itself is sponsored, then it can be deleted, but the destination of the `AccountDelete` transaction (in other words, where the leftover XRP goes) **must** be the sponsor's account. This ensures that the sponsor gets their reserve back, and the sponsee cannot run away with those funds. @@ -882,27 +882,27 @@ If the sponsee still has sponsored objects, those objects will follow the same r If a sponsored object is deleted (either due to normal object deletion processes or, in the case of objects that aren't deletion blockers, because the owner account is deleted), the sponsor's reserve becomes available again. -### 1.3. A.3: What if a sponsor that is sponsoring a few objects wants to delete their account? +### A.3: What if a sponsor that is sponsoring a few objects wants to delete their account? An account cannot be deleted if it is sponsoring **any** existing accounts or objects. They will need to either delete those objects (by asking the owner to do so, as they cannot do so directly) or use the `SponsorshipTransfer` transaction to relinquish control of them. -### 1.4. A.4: Does a sponsor have any powers over an object they pay the reserve for? I.e. can they delete the object? +### A.4: Does a sponsor have any powers over an object they pay the reserve for? I.e. can they delete the object? No. If a sponsor no longer wants to support an object, they can always use the `SponsorshipTransfer` transaction instead to transfer the reserve burden back to the sponsee. -### 1.5. A.5: What if a sponsee refuses to delete their account when a sponsor wants to stop supporting their account? +### A.5: What if a sponsee refuses to delete their account when a sponsor wants to stop supporting their account? The sponsor will have the standard problem of trying to get ahold of a debtor to make them pay. They may use the `SponsorshipTransfer` transaction to put the onus on the sponsee. If the sponsee does not have enough XRP to cover the reserve for those objects, they will not be able to create any more objects until they do so. -### 1.6. A.6: What happens if the sponsor tries to `SponsorshipTransfer` but the sponsee doesn't have enough funds to cover the reserve? +### A.6: What happens if the sponsor tries to `SponsorshipTransfer` but the sponsee doesn't have enough funds to cover the reserve? If the sponsor really needs to get out of the sponsor relationship ASAP without recouping the value of the reserve, they can pay the sponsee the amount of XRP they need to cover the reserve. These steps can be executed atomically via a [Batch transaction](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), to ensure that the sponsee can't do something else with the funds before the `SponsorshipTransfer` transaction is validated. -### 1.7. A.7: Would sponsored accounts carry a lower reserve? +### A.7: Would sponsored accounts carry a lower reserve? No, they would still carry a reserve of 1 XRP at current levels. -### 1.8. A.8: Can an existing unsponsored ledger object/account be sponsored? +### A.8: Can an existing unsponsored ledger object/account be sponsored? Yes, with the `SponsorshipTransfer` transaction. @@ -940,9 +940,9 @@ The answer to this question is still being explored. One possible solution is to Currently, only the global signer list is supported. Another `SignerListID` value could be added to support sponsorship. Transaction values can only go up to $2^{16}$, since the `TransactionType` field is a `UInt16`, but the `SignerListID` field goes up to $2^{32}$, so there is room in the design for additional values that do not correlate to a specific transaction type. -## 2. Appendix B: Alternate Designs +## Appendix B: Alternate Designs -### 2.1. B.1: Add a `Sponsor` to the account +### B.1: Add a `Sponsor` to the account This design involved updating `AccountSet` to allow users to add a `Sponsor` to their account (with a signature from the sponsor as well). The sponsor would then sponsor every object from that account while the field was active, and either the sponsor or the account could remove the sponsorship at any time. @@ -952,7 +952,7 @@ The current design also supports having different sponsors for different objects -### 2.2. B.2: A Wrapper Transaction +### B.2: A Wrapper Transaction There would be a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), that the sponsor would sign. It would contain a sub-transaction from the sponsee. @@ -969,7 +969,7 @@ This was a part of a previous version of the spec (inspired by Stellar's [sandwi In addition, the signing process becomes complicated (as discovered in the process of developing XLS-56). You have to somehow prevent the sponsor from submitting the as-is signed transaction to the network, without including it in the wrapper transaction. -### 2.3. B.3: A Create-Accept-Cancel Flow +### B.3: A Create-Accept-Cancel Flow The rough idea of this design was to have a new set of transactions (e.g. `SponsorCreate`/`SponsorAccept`/`SponsorCancel`/`SponsorFinish`) where a sponsor could take on the reserve for an existing object. From 24a627ae9f95ce2047542447aa99f8b5e867e691 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 23 Dec 2025 17:11:40 -0800 Subject: [PATCH 25/53] add some examples --- .../README.md | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index d963e8eba..39a32500c 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -173,6 +173,24 @@ This object is a [deletion blocker](https://xrpl.org/docs/concepts/accounts/dele The `snake_case` form of the ledger object name is `sponsorship`. +### 5.9. Example JSON + +```json +{ + "LedgerEntryType": "Sponsorship", + "Owner": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "Sponsee": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", + "FeeAmount": "1000000", + "MaxFee": "1000", + "ReserveCount": 5, + "Flags": 0, + "OwnerNode": "0000000000000000", + "SponseeNode": "0000000000000000", + "PreviousTxnID": "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", + "PreviousTxnLgrSeq": 12345678 +} +``` + ## 6. Ledger Entry: `AccountRoot` An `AccountRoot` ledger entry type describes a single [account](https://xrpl.org/docs/concepts/accounts), its settings, and XRP balance. @@ -479,6 +497,21 @@ This transaction creates and updates the `Sponsorship` object. - If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. - _Note: this does not affect already-sponsored entries and accounts. Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process._ +### 9.5. Example JSON + +```json +{ + "TransactionType": "SponsorshipSet", + "Account": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "Sponsee": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", + "FeeAmount": "1000000", + "MaxFee": "1000", + "ReserveCount": 5, + "Fee": "12", + "Sequence": 42 +} +``` + ## 10. Transaction: `SponsorshipTransfer` This transaction transfers a sponsor relationship for a particular ledger object's object reserve. The sponsor relationship can either be passed on to a new sponsor, or dissolved entirely (with the sponsee taking on the reserve). Either the sponsor or sponsee may submit this transaction at any point in time. @@ -588,6 +621,18 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. +### 12.3. Example JSON + +```json +{ + "TransactionType": "AccountDelete", + "Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm", + "Destination": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "Fee": "5000000", + "Sequence": 2470665 +} +``` + ## 13. Granular Permission: `SponsorFee` This delegatable granular permission allows an account to sponsor fees on behalf of another account. @@ -626,6 +671,56 @@ The response fields remain the same. There are no additional failure conditions. +### 15.4. Example Request + +```json +{ + "command": "account_objects", + "account": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "sponsored": true, + "ledger_index": "validated", + "type": "state" +} +``` + +### 15.5. Example Response + +```json +{ + "account": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "account_objects": [ + { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "100" + }, + "Flags": 65536, + "HighLimit": { + "currency": "USD", + "issuer": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "value": "1000" + }, + "HighNode": "0000000000000000", + "HighSponsorAccount": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "issuer": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", + "value": "0" + }, + "LowNode": "0000000000000000", + "PreviousTxnID": "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", + "PreviousTxnLgrSeq": 12345678, + "index": "ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890" + } + ], + "ledger_hash": "FEDCBA0987654321FEDCBA0987654321FEDCBA0987654321FEDCBA0987654321", + "ledger_index": 56789012, + "validated": true +} +``` + ## 16. RPC: `account_sponsoring` The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `SponsorAccount` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). From a564f352938528fa44e9c3027c65083e1a2f2483 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 23 Dec 2025 17:21:19 -0800 Subject: [PATCH 26/53] add rationale --- .../README.md | 89 +++++++++++-------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 39a32500c..ef5bac4d8 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -951,7 +951,59 @@ The only way an account can be created is via a `Payment` transaction. So the sp ## 20. Rationale -The primary motivation for this design is to enable companies, token issuers, and other entities to reduce onboarding friction for end users by covering transaction fees and reserve requirements on their behalf. Today, users must self-fund both, or companies must essentially donate XRP to users with no controls over how they use it, before interacting with the XRPL. This creates a barrier to entry for use cases such as token distribution, NFT minting, or enterprise onboarding. Sponsorship provides a mechanism for entities with established XRP balances to subsidize these costs while maintaining strong on-chain accountability. +This section explains the key technical design decisions, why particular choices were made, and how this design compares to similar features on other chains. + +### 20.1. Related Work + +Sponsored transactions and reserves are a common feature across blockchain ecosystems: + +- **Stellar** implements [sponsored reserves](https://developers.stellar.org/docs/learn/encyclopedia/sponsored-reserves) using a "sandwich transaction" pattern with `BeginSponsoringFutureReserves` and `EndSponsoringFutureReserves` operations wrapped around the sponsored operations. +- **Ethereum** supports meta-transactions through various standards (EIP-2771, EIP-3009) and account abstraction (ERC-4337), where a relayer submits transactions on behalf of users and pays the gas fees. +- **Solana** allows fee payers to be specified separately from the transaction signer. + +This proposal draws inspiration from these implementations while adapting the concepts to fit the XRPL's account-based model and existing transaction structure. + +### 20.2. Pre-Funded vs. Co-Signed Sponsorship + +The design supports two modes of sponsorship: pre-funded (via the `Sponsorship` ledger object) and co-signed (via the `Sponsor` transaction field). This dual approach was chosen to accommodate different use cases: + +- **Co-signed sponsorship** gives sponsors fine-grained control over each transaction, ideal for high-value or sensitive operations. +- **Pre-funded sponsorship** reduces operational overhead for sponsors who want to enable many transactions without being involved in each one, while still maintaining limits via `MaxFee` and `ReserveCount`. + +### 20.3. Other Designs Considered + +#### 20.3.1. Per-Transaction Sponsorship vs. Account-Level Sponsorship + +An earlier design involved updating `AccountSet` to allow users to add a `Sponsor` to their account (with a signature from the sponsor as well). The sponsor would then sponsor every object from that account while the field was active, and either the sponsor or the account could remove the sponsorship at any time. + +This approach was rejected because it made more sense for the relationship to be specific to a specific transaction(s), to prevent abuse—the sponsor should decide what objects they want to support and what objects they don't want to support. This philosophy (that the sponsorship relationship should be ephemeral to prevent abuse) aligns with Stellar's design. + +The current design also supports having different sponsors for different objects, which allows users to use a broad set of services and platforms, instead of being locked into one. + + + +#### 20.3.2. Inner Object vs. Wrapper Transaction + +An alternative design considered was a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), that the sponsor would sign. It would contain a sub-transaction from the sponsee. + +It would look something like this: + +| Field Name | Required | JSON Type | Internal Type | Description | +| ----------------- | -------- | --------- | ------------- | ------------------------------------------------------------------------ | +| `TransactionType` | Yes | `string` | `UInt16` | The transaction type (`Relay`). | +| `Account` | Yes | `string` | `STAccount` | The sponsor of the transaction. | +| `Transaction` | Yes | `object` | `STTx` | The sponsee's transaction. | +| `Fee` | Yes | `string` | `STAmount` | The fee for the transaction. This should match the fee in `Transaction`. | + +This was inspired by Stellar's sandwich transaction design, but the current inner object design felt cleaner. From an implementation perspective, it's easier to have the fee payer as a part of the existing transaction rather than as a part of a wrapper transaction, since that info needs to somehow get passed down the stack. Also, while the wrapper transaction paradigm will be used in XLS-56, they should be used sparingly in designs—only when necessary—as their flow is rather complicated in the `rippled` code. + +In addition, the signing process becomes complicated (as discovered in the process of developing XLS-56). You have to somehow prevent the sponsor from submitting the as-is signed transaction to the network, without including it in the wrapper transaction. + +#### 20.3.2. Create-Accept-Cancel Flow + +Another design considered was to have a new set of transactions (e.g. `SponsorCreate`/`SponsorAccept`/`SponsorCancel`/`SponsorFinish`) where a sponsor could take on the reserve for an existing object. + +This design was never seriously considered, as it felt too complicated and introduced several new transactions. It also doesn't support adding a sponsor to the object at object creation time, which is a much smoother UX and never requires the owner/sponsee to hold enough XRP for the reserve. ## 21. n+1. Remaining TODOs/Open Questions @@ -1034,38 +1086,3 @@ The answer to this question is still being explored. One possible solution is to ### A.16: How does this proposal work in conjunction with [XLS-49](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0049-multiple-signer-lists)? What signer list(s) have the power to sponsor fees or reserves? Currently, only the global signer list is supported. Another `SignerListID` value could be added to support sponsorship. Transaction values can only go up to $2^{16}$, since the `TransactionType` field is a `UInt16`, but the `SignerListID` field goes up to $2^{32}$, so there is room in the design for additional values that do not correlate to a specific transaction type. - -## Appendix B: Alternate Designs - -### B.1: Add a `Sponsor` to the account - -This design involved updating `AccountSet` to allow users to add a `Sponsor` to their account (with a signature from the sponsor as well). The sponsor would then sponsor every object from that account while the field was active, and either the sponsor or the account could remove the sponsorship at any time. - -This was a previous version of the spec, but it made more sense for the relationship to be specific to a specific transaction(s), to prevent abuse (the sponsor should decide what objects they want to support and what objects they don't want to support). - -The current design also supports having different sponsors for different objects, which allows users to use a broad set of services and platforms, instead of being locked into one. - - - -### B.2: A Wrapper Transaction - -There would be a wrapper transaction (tentatively named `Relay`), similar to `Batch` in [XLS-56](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch), that the sponsor would sign. It would contain a sub-transaction from the sponsee. - -It would look something like this: - -| Field Name | Required | JSON Type | Internal Type | Description | -| ----------------- | -------- | --------- | ------------- | ------------------------------------------------------------------------ | -| `TransactionType` | Yes | `string` | `UInt16` | The transaction type (`Relay`). | -| `Account` | Yes | `string` | `STAccount` | The sponsor of the transaction. | -| `Transaction` | Yes | `object` | `STTx` | The sponsee's transaction. | -| `Fee` | Yes | `string` | `STAmount` | The fee for the transaction. This should match the fee in `Transaction`. | - -This was a part of a previous version of the spec (inspired by Stellar's [sandwich transaction design](https://developers.stellar.org/docs/learn/encyclopedia/sponsored-reserves#begin-and-end-sponsorships) for their implementation of sponsored reserves), but the existing design felt cleaner. From an implementation perspective, it's easier to have the fee payer as a part of the existing transaction rather than as a part of a wrapper transaction, since that info needs to somehow get passed down the stack. Also, while the wrapper transaction paradigm will be used in XLS-56, they should be used sparingly in designs - only when necessary - as their flow is rather complicated in the `rippled` code. - -In addition, the signing process becomes complicated (as discovered in the process of developing XLS-56). You have to somehow prevent the sponsor from submitting the as-is signed transaction to the network, without including it in the wrapper transaction. - -### B.3: A Create-Accept-Cancel Flow - -The rough idea of this design was to have a new set of transactions (e.g. `SponsorCreate`/`SponsorAccept`/`SponsorCancel`/`SponsorFinish`) where a sponsor could take on the reserve for an existing object. - -This design was never seriously considered, as it felt too complicated and introduced several new transactions. It also doesn't support adding a sponsor to the object at object creation time, which is a much smoother UX and never requires the owner/sponsee to hold enough XRP for the reserve. From 68af3e82e9604edc1a7ac985b9f073d8373312b9 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 23 Dec 2025 17:34:23 -0800 Subject: [PATCH 27/53] fix link --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index ef5bac4d8..63cd34450 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -8,7 +8,7 @@ proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 requires: 74 created: 2024-05-02 - updated: 2024-12-24 + updated: 2025-12-23
# Sponsored Fees and Reserves From 109d90f86eddb0b7bf70fc431ec8965ad4fd21c6 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 6 Jan 2026 13:57:00 -0500 Subject: [PATCH 28/53] Sync Public/Sponsor XLS.md from Obsidian vault --- XLS-0068-sponsored-fees-and-reserves/README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 63cd34450..bc2476cda 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -159,7 +159,7 @@ This object charges 1 reserve. ### 5.6. Deletion -This object will be deleted any time the `FeeAmount` and `ReserveCount` are both `0`. This can be done directly via `SponsorshipSet`, or can occur in the regular flow of transactions, if the sponsorship runs out. +This object will be deleted via `SponsorshipSet`, when the `tfDeleteObject` flag is enabled. This object is a [deletion blocker](https://xrpl.org/docs/concepts/accounts/deleting-accounts/#requirements). @@ -258,7 +258,9 @@ The total account reserve should now be calculated as: $$ \displaylines{ -(acct.SponsorAccount ext{ ? } 0 : acctReserve) + \nobjReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + \nacctReserve * acct.SponsoringAccountCount +(acct.SponsorAccount ext{ ? } 0 : acctReserve) + +objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + +acctReserve * acct.SponsoringAccountCount } $$ @@ -290,7 +292,7 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/led | `LowNode` | No | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this entry, in case the directory consists of multiple pages. | | `LowQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the low account, as an integer in the implied ratio `LowQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | | `LowQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the low account, as an integer in the implied ratio `LowQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `PreviousTxnID` | No | Yes | N/A | `string` | `UInt256` | The identifying hash of the transaction that most recently modified this entry. | +| `PreviousTxnID` | No | Yes | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this entry. | | `PreviousTxnLgrSeq` | No | Yes | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this entry. | @@ -486,6 +488,8 @@ This transaction creates and updates the `Sponsorship` object. - `ReserveCount` is specified - `tfSponsorshipSetRequireSignForFee` is enabled - `tfSponsorshipSetRequireSignForReserve` is enabled + - `tfSponsorshipClearRequireSignForFee` is enabled + - `tfSponsorshipClearRequireSignForReserve` is enabled ### 9.4. State Changes @@ -522,7 +526,7 @@ This transaction transfers a sponsor relationship for a particular ledger object | ----------------- | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------- | | `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | | `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | -| `ObjectID` | | `string` | `UInt256` | The ID of the object to transfer sponsorship. | +| `ObjectID` | | `string` | `Hash256` | The ID of the object to transfer sponsorship. | | `Sponsor` | | `object` | `STObject` | The new sponsor of the object. This field contains the same subfields as above. | #### 10.1.1. `ObjectID` From c3ff9fbc10a87ba5f067bc0bcbfd9a3a9d691d53 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 6 Jan 2026 14:00:52 -0500 Subject: [PATCH 29/53] fix update date --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index bc2476cda..af8de8d51 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -8,7 +8,7 @@ proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 requires: 74 created: 2024-05-02 - updated: 2025-12-23 + updated: 2025-01-06 # Sponsored Fees and Reserves From b2ef3df1c526bae5c1fc718af02e1202ff261a06 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 6 Jan 2026 15:53:02 -0500 Subject: [PATCH 30/53] flesh things out more --- .../README.md | 606 +++++++++++++++--- templates/AMENDMENT_TEMPLATE.md | 12 +- 2 files changed, 538 insertions(+), 80 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index af8de8d51..2107e69d2 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -108,9 +108,60 @@ As a reference, here are the fields that all ledger objects currently have: We propose this additional field: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ---------------- | --------- | --------- | ------------- | --------- | ------------- | -------------------------------------------------------------- | -| `SponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve for this ledger object. | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ---------------- | --------- | --------- | ------------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor paying the owner reserve for a given ledger object. When present, it indicates that the reserve burden for that object has been shifted from the owner to the sponsor. | + +### 4.2. Invariant Checks + +#### 4.2.1. Allowed Ledger Entry Types + +The `SponsorAccount` field **MAY** appear on the following ledger entry types: + +- `AccountRoot` (for account reserve sponsorship) +- `Offer` +- `Escrow` +- `Check` +- `PayChannel` +- `DepositPreauth` +- `Ticket` +- `NFTokenPage` +- `NFTokenOffer` +- `AMM` +- `Bridge` +- `XChainOwnedClaimID` +- `XChainOwnedCreateAccountClaimID` +- `DID` +- Any other ledger entry type that contributes to an account's owner reserve, subject to implementation details + +The `SponsorAccount` field **MUST NOT** appear on: + +- `RippleState` objects, which instead use the `HighSponsorAccount` and `LowSponsorAccount` fields defined in section 7.1 +- `DirectoryNode` objects (these do not have reserves) +- `Amendments` objects (global ledger objects) +- `FeeSettings` objects (global ledger objects) +- `NegativeUNL` objects (global ledger objects) + +#### 4.2.2. Presence Rules + +- The field **MUST** be omitted when there is no sponsor; there is no sentinel value such as `0` or an empty string to indicate "no sponsor". +- When present, the field **MUST** contain a valid `AccountID` that exists on the ledger. +- The field is added when sponsorship is established (either at object creation or via `SponsorshipTransfer`). +- The field is removed when sponsorship is dissolved via `SponsorshipTransfer`. + +#### 4.2.3. Constraints + +- **Self-Sponsorship**: An account **MAY** sponsor its own objects. While this does not reduce the account's reserve requirement, it may be useful for accounting or organizational purposes. +- **Relationship to Ownership**: The `SponsorAccount` value **MAY** differ from the object's owner (as indicated by the `Owner` or `Account` field). This is the typical case for sponsorship. +- **Sponsor Account Requirements**: The sponsor account **MUST** have sufficient XRP to meet its reserve requirements, including reserves for all objects and accounts it sponsors. + +#### 4.2.4. Authoritative Indication + +Implementations **MUST** treat the presence or absence of `SponsorAccount` as the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: + +- The object's reserve is counted against the sponsor's `SponsoringOwnerCount` (or `SponsoringAccountCount` for `AccountRoot`), not the owner's `OwnerCount`. +- The owner's `SponsoredOwnerCount` is incremented (for non-`AccountRoot` objects). +- The reserve calculation for both sponsor and owner accounts is adjusted accordingly. ## 5. Ledger Entry: `Sponsorship` @@ -120,25 +171,34 @@ _Note: this object does not need to be created in order to sponsor accounts. It ### 5.1. Object ID -The key of the `Sponsorship` 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: +#### 5.1.1. Key Space + +The `Sponsorship` ledger entry uses a dedicated key space constant `spaceSponsorship`, which will be assigned a unique 16-bit value during implementation (e.g., `0x0053` or another available value in the key space range). + +#### 5.1.2. ID Calculation Algorithm + +The unique identifier for a `Sponsorship` object is calculated using [`SHA512-Half`](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#hashes) of the following values concatenated in order: -- The `Sponsorship` space key (defined during implementation) -- The `AccountID` of the `Sponsor` -- The `AccountID` of the `Sponsee` +1. The `Sponsorship` space key (`'>'`) +2. The `AccountID` of the `Owner` (sponsor) +3. The `AccountID` of the `Sponsee` + +This ensures that there can be at most one `Sponsorship` object per sponsor-sponsee pair, preventing duplicate relationships. ### 5.2. Fields -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ------------------- | --------- | --------- | ------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -| `Owner` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | -| `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | -| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | -| `MaxFee` | | | N/A | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | -| `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | -| `OwnerNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. | -| `SponseeNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. | -| `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this entry. | -| `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ------------------- | --------- | --------- | ------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The value `"Sponsorship"` (JSON) or a unique numeric value (internal, assigned during implementation) indicates this is a `Sponsorship` ledger object. | +| `Owner` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | +| `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | +| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `MaxFee` | | | N/A | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | +| `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | +| `OwnerNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. | +| `SponseeNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsee's owner directory links to this object, in case the directory consists of multiple pages. | +| `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this entry. | +| `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | ### 5.3. Flags @@ -151,23 +211,47 @@ There are two flags on this object: ### 5.4. Ownership -The object is owned by `Sponsor`, who also pays the reserve. +The object is owned by `Owner`, who also pays the reserve for this object. ### 5.5. Reserve -This object charges 1 reserve. +**Reserve Requirement:** Standard + +The reserve is charged to the sponsor's account (the `Owner` field). ### 5.6. Deletion -This object will be deleted via `SponsorshipSet`, when the `tfDeleteObject` flag is enabled. +#### 5.6.1. \*Deletion Transactions + +`SponsorshipSet` with the `tfDeleteObject` flag + +#### 5.6.2. Deletion Conditions + +- The `SponsorshipSet` transaction must be submitted by the sponsor (the `Owner` of the `Sponsorship` object). +- The `tfDeleteObject` flag must be enabled. +- No other fields (`FeeAmount`, `MaxFee`, `ReserveCount`, or flag-setting fields) may be specified in the deletion transaction. +- **Note:** Non-zero `FeeAmount` and `ReserveCount` values **ARE** permitted at deletion time. Any remaining XRP in `FeeAmount` is returned to the sponsor's account upon deletion. + +#### 5.6.3. Account Deletion Blocker This object is a [deletion blocker](https://xrpl.org/docs/concepts/accounts/deleting-accounts/#requirements). +This object must be deleted before its owner account (the sponsor) can be deleted. The sponsor must either: + +1. Delete the `Sponsorship` object via `SponsorshipSet` with `tfDeleteObject`, or +2. Wait for the sponsee to delete it (if the sponsee has permission to do so) + +**Note on Existing Sponsored Objects:** Deleting a `Sponsorship` object does **not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `SponsorAccount` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. + ### 5.7. Invariant Checks -- `FeeAmount` >= 0 || `ReserveCount` >= 0 -- `SponsorAccount` != `Sponsee` -- `FeeAmount` is nonnegative and denominated in XRP +The following invariants must always hold for a `Sponsorship` object: + +- `Owner != Sponsee` (an account cannot create a `Sponsorship` object with itself as both sponsor and sponsee) +- `FeeAmount >= 0` AND `FeeAmount` is denominated in XRP (drops) +- `ReserveCount >= 0` +- At least one of `FeeAmount` and `ReserveCount` must be included +- Both `Owner` and `Sponsee` must be valid `AccountID` values that exist on the ledger ### 5.8. RPC Name @@ -242,7 +326,7 @@ This spec proposes these additional fields: #### 6.1.1. `SponsorAccount` -The `SponsorAccount` field is already added in the ledger common fields (see section [3.1.1](#311-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. +The `SponsorAccount` field is already added in the ledger common fields (see section [4.2](#42-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. This field is included if the account was created with a sponsor paying its account reserve. If this sponsored account is deleted, the destination of the `AccountDelete` transaction must equal `SponsorAccount`, so that the sponsor can recoup their fees. @@ -264,6 +348,21 @@ acctReserve * acct.SponsoringAccountCount } $$ +### 6.3. Invariants + +The following invariants must hold for `AccountRoot` objects with sponsorship fields: + +- `SponsoredOwnerCount <= OwnerCount` (cannot have more sponsored objects than total owned objects) +- If `SponsorAccount` is present, it must be a valid `AccountID` that exists on the ledger +- The reserve calculation must always result in a non-negative value + +**Global Invariant (referenced in section 18.2):** + +Across all accounts in the ledger: +$$\sum_{accounts} SponsoredOwnerCount = \sum_{accounts} SponsoringOwnerCount$$ + +This ensures that every sponsored object is properly accounted for on both the sponsee and sponsor sides. + ## 7. Ledger Entry: `RippleState` A `RippleState` ledger entry represents a [trust line](https://xrpl.org/docs/concepts/tokens/fungible-tokens) between two accounts. Each account can change its own limit and other settings, but the balance is a single shared value. A trust line that is entirely in its default state is considered the same as a trust line that does not exist and is automatically deleted. You can create or modify a trust line with a [TrustSet transaction](https://xrpl.org/docs/references/protocol/transactions/types/trustset). @@ -330,6 +429,13 @@ We propose these modifications: The `Sponsor` inner object contains all of the information for the sponsorship happening in the transaction. +**Implementation Note:** The `Sponsor` field is a new `STObject` field. + +**Transaction Types:** The `Sponsor` field **MAY** be included in any transaction type except: + +- Pseudo-transactions (`EnableAmendment`, `SetFee`, `UNLModify`) +- `Batch` transactions (inner transactions should use `Sponsor` instead) + The fields contained in this object are: | Field Name | Required? | JSON Type | Internal Type | Description | @@ -455,15 +561,15 @@ This transaction creates and updates the `Sponsorship` object. ### 9.1. Fields -| Field Name | Required? | JSON Type | Internal Type | Description | -| ----------------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipSet`). | -| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the sponsor or the sponsee. | -| `SponsorAccount` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | -| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `SponsorAccount`. | -| `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | -| `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | -| `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ----------------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipSet`). | +| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the sponsor or the sponsee. | +| `SponsorAccount` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | +| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `SponsorAccount`. | +| `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. This value will replace what is currently in the `Sponsorship.FeeAmount` field (if it exists). | +| `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | +| `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. This value will replace what is currently in the `Sponsorship.ReserveCount` field (if it exists). | ### 9.2. Flags @@ -475,31 +581,62 @@ This transaction creates and updates the `Sponsorship` object. | `tfSponsorshipClearRequireSignForReserve` | `0x00080000` | Removes the restriction every use of this sponsor for sponsoring fees requires a signature from the sponsor. | | `tfDeleteObject` | `0x00100000` | Removes the ledger object. | -### 9.3. Failure Conditions +### 9.3. Transaction Fee + +**Fee Structure:** Standard + +This transaction uses the standard transaction fee (currently 10 drops, subject to Fee Voting changes). -- `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` -- Both `SponsorAccount` and `Sponsee` are specified -- `SponsorAccount` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled -- `MaxFee` is less than the base fee or is not denominated in XRP -- `FeeAmount` is less than the base fee or is not denominated in XRP +### 9.4. Failure Conditions + +- `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` (`temMALFORMED`) +- Both `SponsorAccount` and `Sponsee` are specified (`temMALFORMED`) +- Neither `SponsorAccount` nor `Sponsee` is specified (`temMALFORMED`) +- `SponsorAccount` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled (`temMALFORMED` - only sponsor can create/update) +- `MaxFee` is less than the base fee or is not denominated in XRP (`temBAD_AMOUNT`) +- `FeeAmount` is not denominated in XRP (`temBAD_AMOUNT`) +- `SponsorAccount` or `Sponsee` does not exist on the ledger (`terNO_ACCOUNT`) +- `Owner == Sponsee` (attempting to create self-sponsorship) (`temMALFORMED`) +- Sponsor does not have sufficient XRP to cover the reserve for the `Sponsorship` object (`tecINSUFFICIENT_RESERVE`) - If `tfDeleteObject` is enabled: - - `FeeAmount` is specified - - `MaxFee` is specified - - `ReserveCount` is specified - - `tfSponsorshipSetRequireSignForFee` is enabled - - `tfSponsorshipSetRequireSignForReserve` is enabled - - `tfSponsorshipClearRequireSignForFee` is enabled - - `tfSponsorshipClearRequireSignForReserve` is enabled - -### 9.4. State Changes - -- If the object already exists: - - `Sponsorship.Amount = tx.FeeAmount` - - `Sponsorship.MaxFee` = `tx.MaxFee` - - `Sponsorship.ReserveCount = tx.ReserveCount` -- If the object doesn't exist, it will be created with the provided fields. -- If the `tfDeleteObject` flag is used, it will delete the object. All funds remaining in the object will be sent back to the `SponsorAccount`. - - _Note: this does not affect already-sponsored entries and accounts. Existing sponsored objects/accounts will need to go through the `SponsorshipTransfer` process._ + - `FeeAmount` is specified (`temMALFORMED`) + - `MaxFee` is specified (`temMALFORMED`) + - `ReserveCount` is specified (`temMALFORMED`) + - `tfSponsorshipSetRequireSignForFee` is enabled (`temINVALID_FLAG`) + - `tfSponsorshipSetRequireSignForReserve` is enabled (`temINVALID_FLAG`) + - `tfSponsorshipClearRequireSignForFee` is enabled (`temINVALID_FLAG`) + - `tfSponsorshipClearRequireSignForReserve` is enabled (`temINVALID_FLAG`) + - The `Sponsorship` object does not exist (`tecNO_ENTRY`) + +### 9.5. State Changes + +**On Success (`tesSUCCESS`):** + +**If creating a new `Sponsorship` object:** + +- Create the `Sponsorship` ledger entry with the specified fields +- Increment the sponsor's `OwnerCount` by 1 +- Add the object to both the sponsor's and sponsee's owner directories +- Deduct the object reserve from the sponsor's available XRP + +**If updating an existing `Sponsorship` object:** + +- Update `Sponsorship.FeeAmount` to `tx.FeeAmount` (or 0 if omitted) +- Update `Sponsorship.MaxFee` to `tx.MaxFee` (or remove field if omitted) +- Update `Sponsorship.ReserveCount` to `tx.ReserveCount` (or 0 if omitted) +- Update flags based on `tfSponsorshipSetRequireSignForFee`, `tfSponsorshipClearRequireSignForFee`, `tfSponsorshipSetRequireSignForReserve`, and `tfSponsorshipClearRequireSignForReserve` +- If `FeeAmount` is increased, deduct the additional XRP from the sponsor's balance +- If `FeeAmount` is decreased, return the difference to the sponsor's balance + +**If deleting the `Sponsorship` object (`tfDeleteObject` flag):** + +- Delete the `Sponsorship` ledger entry +- Return all remaining XRP in `FeeAmount` to the sponsor's balance +- Decrement the sponsor's `OwnerCount` by 1 +- Remove the object from both the sponsor's and sponsee's owner directories +- Return the object reserve to the sponsor's available XRP + +**Important Note:** Deleting a `Sponsorship` object does **not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `SponsorAccount` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. ### 9.5. Example JSON @@ -559,16 +696,26 @@ The sponsor will likely only rarely want to do this (such as if they are transfe ### 10.4. Transaction Fee -The transaction fee for this transaction will be the base fee (currently 10 drops). +**Fee Structure:** Standard + +This transaction uses the standard transaction fee (currently 10 drops, subject to Fee Voting changes). ### 10.5. Failure Conditions -All failure conditions mentioned in section [6.3](#63-failure-conditions) still apply here. +All failure conditions mentioned in [section 8.3](#83-failure-conditions) still apply here. -- If transferring the sponsorship, the new sponsor does not have enough reserve for this object/account. -- If dissolving the sponsorship, the owner does not have enough reserve for this object/account. -- The new sponsor does not exist. -- The `tx.Account` neither the sponsor nor the owner (sponsee) of `ObjectID`. +**Additional failure conditions specific to `SponsorshipTransfer`:** + +- `ObjectID` is specified but does not exist on the ledger (`tecNO_TARGET`) +- `ObjectID` is specified but does not have a `SponsorAccount` field (object is not sponsored) (`tecNO_PERMISSION`) +- `ObjectID` is not specified and the `tx.Account` does not have a `SponsorAccount` field (account is not sponsored) (`tecNO_PERMISSION`) +- `tx.Account` is neither the current sponsor nor the owner (sponsee) of the object/account specified by `ObjectID` (`tecNO_PERMISSION`) +- If transferring the sponsorship to a new sponsor: + - The new sponsor account does not exist (`terNO_ACCOUNT`) + - The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) + - The `Sponsor` field is malformed or missing required subfields (`temMALFORMED`) +- If dissolving the sponsorship (no `Sponsor` field or `tfSponsorReserve` flag not set): + - The owner does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) ### 10.6. State Changes @@ -609,13 +756,15 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/ty ### 12.1. Failure Conditions -Existing failure conditions still apply. +Existing failure conditions still apply (see [AccountDelete documentation](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete)). -If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` field: +Additional failure conditions for sponsored accounts: -- The `Destination` is not equal to `AccountRoot.SponsorAccount`. +- If the `AccountRoot` associated with `tx.Account` has a `SponsorAccount` field: + - The `Destination` is not equal to `AccountRoot.SponsorAccount` (`tecNO_PERMISSION` - sponsored account funds must go to sponsor) -If the `AccountRoot` associated with the `tx.Account` has a `SponsoringOwnerCount` or `SponsoringAccountCount` field, the transaction will fail with `tecHAS_OBLIGATIONS`. +- If the `AccountRoot` associated with `tx.Account` has a non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` field: + - The transaction fails with `tecHAS_OBLIGATIONS` (account is currently sponsoring other accounts or objects and cannot be deleted until those sponsorships are transferred or dissolved) ### 12.2. State Changes @@ -639,11 +788,41 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount ## 13. Granular Permission: `SponsorFee` -This delegatable granular permission allows an account to sponsor fees on behalf of another account. +### 13.1. Description + +The `SponsorFee` permission allows an account to delegate its ability to sponsor other accounts' transaction fees. + +### 13.2. Transaction Types Affected + +This permission affects **all transaction types** that support the `Sponsor` field (see section [8.1.1](#811-sponsor)). + +When a transaction includes a `Sponsor` field with the `tfSponsorFee` flag enabled, the sponsor must have granted the `SponsorFee` permission to the transaction submitter (the `tx.Account`), unless: + +- The sponsor is signing the transaction directly (via `Sponsor.SigningPubKey` and `Sponsor.TxnSignature` or `Sponsor.Signers`), OR +- A `Sponsorship` object exists between the sponsor and sponsee with sufficient `FeeAmount` + +### 13.3. Permission Value + +65549 ## 14. Granular Permission: `SponsorReserve` -This delegatable granular permission allows an account to sponsor reserves on behalf of another account. +### 14.1. Description + +The `SponsorFee` permission allows an account to delegate its ability to sponsor other accounts' transaction fees. + +### 14.2. Transaction Types Affected + +This permission affects **all transaction types** that support the `Sponsor` field (see section 8.1.1) and create new ledger objects or accounts. + +When a transaction includes a `Sponsor` field with the `tfSponsorReserve` flag enabled, the sponsor must have granted the `SponsorReserve` permission to the transaction submitter (the `tx.Account`), unless: + +- The sponsor is signing the transaction directly (via `Sponsor.SigningPubKey` and `Sponsor.TxnSignature` or `Sponsor.Signers`), OR +- A `Sponsorship` object exists between the sponsor and sponsee with sufficient `ReserveCount` + +### 14.3. Permission Value + +65550 ## 15. RPC: `account_objects` @@ -763,7 +942,81 @@ The response fields are nearly identical to `account_objects`. - `actNotFound` - The [address](https://xrpl.org/docs/references/protocol/data-types/basic-data-types#addresses) specified in the `account` field of the request does not correspond to an account in the ledger. - `lgrNotFound` - The ledger specified by the `ledger_hash` or `ledger_index` does not exist, or it does exist but the server does not have it. -## 17. Security +### 16.4. Example Request + +```json +{ + "command": "account_sponsoring", + "account": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "ledger_index": "validated", + "limit": 10 +} +``` + +### 16.5. Example Response + +```json +{ + "account": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "sponsored_objects": [ + { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "100" + }, + "Flags": 65536, + "HighLimit": { + "currency": "USD", + "issuer": "rSponsee1ABC123XYZ456DEF789GHI", + "value": "1000" + }, + "HighNode": "0000000000000000", + "HighSponsorAccount": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "issuer": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "value": "0" + }, + "LowNode": "0000000000000000", + "PreviousTxnID": "E3FE6EA3D48F0C2B639448020EA4F03D4F4F8FFDB243A852A0F59177921B4879", + "PreviousTxnLgrSeq": 14090896, + "index": "9ED4406351B7A511A012A9B5E7FE4059FA2F7650621379C0013492C315E25B97" + }, + { + "Account": "rSponsee2XYZ789ABC123DEF456GHI", + "Balance": "1000000", + "Flags": 0, + "LedgerEntryType": "AccountRoot", + "OwnerCount": 3, + "PreviousTxnID": "0D5FB50FA65C9FE1538FD7E398FFFE9D1908DFA4576D8D7A020040686F93C77D", + "PreviousTxnLgrSeq": 14091574, + "Sequence": 1, + "SponsorAccount": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "index": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8" + }, + { + "LedgerEntryType": "Sponsorship", + "Owner": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "Sponsee": "rSponsee3DEF456GHI789ABC123XYZ", + "FeeAmount": "5000000", + "MaxFee": "10000", + "ReserveCount": 5, + "OwnerNode": "0000000000000000", + "SponseeNode": "0000000000000000", + "PreviousTxnID": "B044D3861WL1HZ4WM4GURZVKTTdJPgM1v5T8QBKWJZQM", + "PreviousTxnLgrSeq": 14091600, + "index": "2B42F8F4E3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3" + } + ], + "ledger_hash": "4C99E5F63C0D0B1C2283D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3", + "ledger_index": 14091625, + "validated": true +} +``` + +## 17. Security Considerations ### 17.1. Security Axioms @@ -1009,12 +1262,217 @@ Another design considered was to have a new set of transactions (e.g. `SponsorCr This design was never seriously considered, as it felt too complicated and introduced several new transactions. It also doesn't support adding a sponsor to the object at object creation time, which is a much smoother UX and never requires the owner/sponsee to hold enough XRP for the reserve. -## 21. n+1. Remaining TODOs/Open Questions +## 21. Backwards Compatibility + +This amendment introduces new functionality while maintaining compatibility with existing ledger states and transactions. + +### 21.1. Pre-Amendment Ledgers and Transactions + +All ledgers and transactions created before the `Sponsor` amendment are activated remain valid. The amendment does not modify or invalidate any existing ledger entries or historical transactions. + +### 21.2. Changes to `AccountDelete` Behavior + +The `AccountDelete` transaction has two new behavioral changes: + +1. **Destination Constraint for Sponsored Accounts**: If an account being deleted has a `SponsorAccount` field (indicating the account reserve is sponsored), the `Destination` field of the `AccountDelete` transaction must equal the `SponsorAccount`. This ensures sponsors can recoup their reserve. Accounts without sponsorship can still use `AccountDelete` with any valid destination as before. + +2. **New Failure Condition**: `AccountDelete` will fail with `tecHAS_OBLIGATIONS` if the account has non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` fields, indicating the account is currently sponsoring other accounts or objects. This prevents sponsors from deleting their accounts while they have active sponsorship obligations. + +### 21.3. Reserve Accounting Changes + +The amendment introduces new fields that affect reserve calculations: + +- **New AccountRoot Fields**: `SponsorAccount`, `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` modify how an account's required XRP reserve is calculated. +- **New Ledger Entry Fields**: The `SponsorAccount` field (and `HighSponsorAccount`/`LowSponsorAccount` for `RippleState`) indicates when an object's reserve is sponsored. +- **Reserve Calculation**: The reserve formula changes from `acctReserve + objReserve * OwnerCount` to account for sponsored objects and sponsorship obligations (see section 6.2). + +Accounts without these new fields continue to use the existing reserve calculation, ensuring backward compatibility. + +### 21.4. Impact on Tooling and Applications + +Legacy tooling that does not understand the new sponsorship fields may experience the following: + +- **Display Issues**: Tools may not correctly display or interpret `SponsorAccount`, `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` fields. +- **Reserve Calculations**: Tools that calculate required reserves may produce incorrect results if they do not account for the new reserve formula. +- **RPC Methods**: The new `account_sponsoring` RPC method and the `sponsored` filter on `account_objects` will not be available in tools that have not been updated. +- **Transaction Construction**: Tools will need updates to support constructing transactions with the `Sponsor` field and handling the dual-signature flow. + +Applications should be updated to recognize and handle these new fields to provide accurate information to users. + +### 21.5. Amendment Activation + +The `Sponsor` amendment must be enabled via the standard amendment process. Once activated: + +- New transactions can include the `Sponsor` field to enable fee and reserve sponsorship. +- New ledger entries can include sponsorship-related fields. +- The new `Sponsorship` ledger entry type becomes available. +- The new `SponsorshipSet` and `SponsorshipTransfer` transaction types become available. +- The `Payment` transaction accepts the new `tfSponsorCreatedAccount` flag. + +Before activation, transactions attempting to use these features will be rejected. + +## 22. Test Plan + +This section outlines the testing strategy for the sponsored fees and reserves feature. A comprehensive test plan is essential to ensure the correctness and security of this amendment. + +### 22.1. Unit Tests for Ledger Entries + +**`Sponsorship` Object Tests:** + +- Creation of `Sponsorship` objects via `SponsorshipSet` with various field combinations +- Updates to existing `Sponsorship` objects (modifying `FeeAmount`, `MaxFee`, `ReserveCount`) +- Deletion of `Sponsorship` objects via `SponsorshipSet` with `tfDeleteObject` flag +- Validation of invariants: `Owner != Sponsee`, `FeeAmount >= 0` and denominated in XRP, `ReserveCount >= 0` +- Proper handling of `lsfSponsorshipRequireSignForFee` and `lsfSponsorshipRequireSignForReserve` flags +- Verification that `Sponsorship` objects are deletion blockers + +**`AccountRoot` Field Tests:** + +- Addition and removal of `SponsorAccount` field on accounts +- Correct tracking of `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` +- Reserve calculation with new fields (see section 6.2) +- Interaction between sponsored and unsponsored objects on the same account + +**`RippleState` Field Tests:** + +- Addition and removal of `HighSponsorAccount` and `LowSponsorAccount` fields +- Verification that `SponsorAccount` common field is not used on `RippleState` objects +- Proper handling of bidirectional trust line sponsorship + +### 22.2. Unit Tests for Transactions + +**`SponsorshipSet` Transaction Tests:** + +- Creating new `Sponsorship` objects with valid and invalid parameters +- Updating existing `Sponsorship` objects +- Deleting `Sponsorship` objects and verifying fund return to sponsor +- Failure conditions: invalid account combinations, invalid fee/reserve values, conflicting flags +- Proper increment/decrement of owner counts and sponsorship counters + +**`SponsorshipTransfer` Transaction Tests:** + +- Transferring sponsorship of objects to new sponsors +- Dissolving sponsorship (returning reserve burden to owner) +- Transferring sponsorship of accounts +- Failure conditions: insufficient reserves, invalid accounts, unauthorized submitters +- Proper updates to `SponsoringOwnerCount`, `SponsoringAccountCount`, and `SponsoredOwnerCount` + +**`Payment` Transaction Tests:** + +- Creating sponsored accounts using `tfSponsorCreatedAccount` flag +- Fee sponsorship via `Sponsor` field with `tfSponsorFee` flag +- Reserve sponsorship for new accounts via `Sponsor` field with `tfSponsorReserve` flag +- Combined fee and reserve sponsorship +- Failure conditions: invalid sponsor signatures, insufficient sponsor funds + +**`AccountDelete` Transaction Tests:** + +- Deleting sponsored accounts with correct destination (must be sponsor) +- Failure when destination is not the sponsor for sponsored accounts +- Failure with `tecHAS_OBLIGATIONS` when account has non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` +- Proper decrement of sponsor's counters upon successful deletion + +**Common Transaction Field Tests:** + +- Valid and invalid `Sponsor` field structures +- Single-signature sponsorship (`SigningPubKey` + `TxnSignature`) +- Multi-signature sponsorship (`Signers` array) +- Validation of `tfSponsorFee` and `tfSponsorReserve` flags +- Fee calculation with sponsor signatures (multi-sig overhead) +- Pre-funded sponsorship (using `Sponsorship` object) vs. co-signed sponsorship +- Signature validation and replay attack prevention + +### 22.3. Invariant Tests + +**Owner Count Balancing:** + +- Verify that object creation increments either `OwnerCount` or both `SponsoringOwnerCount` and `SponsoredOwnerCount` +- Verify that object deletion decrements the appropriate counters +- Test the global invariant: `Σ SponsoredOwnerCount = Σ SponsoringOwnerCount` + +**Account Count Balancing:** + +- Verify that sponsored account creation increments `SponsoringAccountCount` on sponsor +- Verify that sponsored account deletion decrements `SponsoringAccountCount` on sponsor + +**Reserve Consistency:** + +- Verify that accounts always have sufficient XRP to meet their reserve requirements +- Test edge cases where sponsorship changes affect reserve calculations + +### 22.4. Integration Tests + +**End-to-End Co-Signed Sponsorship Flow:** + +- Sponsee constructs and autofills transaction +- Sponsor signs transaction +- Sponsee adds sponsor signature and signs transaction +- Transaction is submitted and validated +- Verify correct state changes and fee/reserve handling + +**End-to-End Pre-Funded Sponsorship Flow:** + +- Sponsor creates `Sponsorship` object with `SponsorshipSet` +- Sponsee constructs transaction with `Sponsor` field +- Transaction is submitted without sponsor signature (using pre-funded amounts) +- Verify `FeeAmount` and `ReserveCount` are decremented correctly +- Test exhaustion of pre-funded amounts + +**Error Case Testing:** + +- Insufficient sponsor funds for fees +- Insufficient sponsor reserves for objects +- Exceeding `MaxFee` limit in `Sponsorship` object +- Exceeding `ReserveCount` limit in `Sponsorship` object +- Invalid or missing sponsor signatures +- Signature replay attempts + +**Sponsorship Transfer Flows:** + +- Sponsor transfers object sponsorship to new sponsor +- Sponsor dissolves sponsorship, returning burden to owner +- Owner transfers sponsorship to new sponsor +- Test with insufficient reserves on receiving party + +### 22.5. RPC Tests + +**`account_objects` with `sponsored` Filter:** + +- Retrieve only sponsored objects (`sponsored: true`) +- Retrieve only unsponsored objects (`sponsored: false`) +- Retrieve all objects (omit `sponsored` parameter) +- Test pagination with `sponsored` filter +- Test interaction with `type` filter + +**`account_sponsoring` RPC Method:** + +- Retrieve all objects sponsored by an account +- Test pagination with `limit` and `marker` +- Test `deletion_blockers_only` filter +- Test with various `type` filters +- Verify correct response format and field presence +- Test error conditions: invalid account, missing ledger + +### 22.6. Performance and Stress Tests + +- Create maximum number of sponsored objects per account +- Test with large `Sponsorship` object counts +- Verify performance of reserve calculations with many sponsored objects +- Test transaction throughput with sponsored transactions + +### 22.7. Amendment Activation Tests + +- Verify that sponsorship features are unavailable before amendment activation +- Verify that sponsorship features become available after amendment activation +- Test that pre-amendment ledgers and transactions remain valid post-activation + +## 23. Reference Implementation + +https://github.com/XRPLF/rippled/pull/5887 + +## 24. n+1. Remaining TODOs/Open Questions -- How will this work for objects like trustlines, where multiple accounts might be holding reserves for it? - - Maybe a second `Sponsor` field or something? - Should fee sponsorship allow for the existing fee paradigm that allows users to dip below the reserve? -- Should `account_sponsoring` be Clio-only? - Should a sponsored account be prevented from sponsoring other accounts? By default the answer is no, so unless there's a reason to do so, we should leave it as is. # Appendix diff --git a/templates/AMENDMENT_TEMPLATE.md b/templates/AMENDMENT_TEMPLATE.md index acdfe9783..87592fa02 100644 --- a/templates/AMENDMENT_TEMPLATE.md +++ b/templates/AMENDMENT_TEMPLATE.md @@ -265,17 +265,17 @@ _[Only include this section if the transaction adds or modifies metadata fields. _[If your specification introduces new permissions, document each permission in its own numbered section following this part of the template. Otherwise, do not include any sections with this title.]_ -### 4.1. Transaction Types Affected +### 4.1. Permission Description -_[List transaction types that this permission applies to]_ +_[Describe how this permission interacts with existing permissions and what it allows/restricts. Describe what the granular permission controls.]_ -### 4.2. Permission Scope +### 4.2. Transaction Types Affected -_[Describe what the granular permission controls]_ +_[List transaction types that this permission applies to]_ -### 4.3. Permission Description +### 4.3. Permission Value -_[Describe how this permission interacts with existing permissions and what it allows/restricts]_ +_[Specify the numeric value for the permission]_ ## 5. RPC: `[rpc_method_name]` From d42179291fdf3a7b17c4aa349dd09dbd91ff5394 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 7 Jan 2026 12:14:51 -0500 Subject: [PATCH 31/53] some cleanup --- .../README.md | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 2107e69d2..a08d68952 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -429,13 +429,6 @@ We propose these modifications: The `Sponsor` inner object contains all of the information for the sponsorship happening in the transaction. -**Implementation Note:** The `Sponsor` field is a new `STObject` field. - -**Transaction Types:** The `Sponsor` field **MAY** be included in any transaction type except: - -- Pseudo-transactions (`EnableAmendment`, `SetFee`, `UNLModify`) -- `Batch` transactions (inner transactions should use `Sponsor` instead) - The fields contained in this object are: | Field Name | Required? | JSON Type | Internal Type | Description | @@ -475,6 +468,13 @@ There will be no additional transaction fee required for the use of the `TxnSign Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. +#### 8.1.1.4. Transaction Types + +**Transaction Types:** The `Sponsor` field may be included in any transaction type except: + +- Pseudo-transactions (`EnableAmendment`, `SetFee`, `UNLModify`) +- `Batch` transactions (inner transactions should use `Sponsor` instead) + ### 8.2. Transaction Fee If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base extunderscore fee$. @@ -490,6 +490,7 @@ The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Spo - The `SponsorAccount` doesn't exist on the ledger. - An invalid sponsorship flag is used. - `Sponsor.SigningPubKey`, `Sponsor.TxnSignature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). +- `Sponsor` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)) #### 8.3.2. Fee Sponsorship Failures @@ -511,7 +512,7 @@ Note: if a transaction doesn't charge a fee (such as an account's first `SetRegu #### 8.3.3. Reserve Sponsorship Failures - The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) -- The transaction does not support reserve sponsorship (see section 6.3.4) +- The transaction does not support reserve sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)) If a `Sponsorship` object exists: @@ -610,16 +611,14 @@ This transaction uses the standard transaction fee (currently 10 drops, subject ### 9.5. State Changes -**On Success (`tesSUCCESS`):** - -**If creating a new `Sponsorship` object:** +If creating a new `Sponsorship` object: - Create the `Sponsorship` ledger entry with the specified fields - Increment the sponsor's `OwnerCount` by 1 - Add the object to both the sponsor's and sponsee's owner directories - Deduct the object reserve from the sponsor's available XRP -**If updating an existing `Sponsorship` object:** +If updating an existing `Sponsorship` object: - Update `Sponsorship.FeeAmount` to `tx.FeeAmount` (or 0 if omitted) - Update `Sponsorship.MaxFee` to `tx.MaxFee` (or remove field if omitted) @@ -628,7 +627,7 @@ This transaction uses the standard transaction fee (currently 10 drops, subject - If `FeeAmount` is increased, deduct the additional XRP from the sponsor's balance - If `FeeAmount` is decreased, return the difference to the sponsor's balance -**If deleting the `Sponsorship` object (`tfDeleteObject` flag):** +If deleting the `Sponsorship` object (`tfDeleteObject` flag): - Delete the `Sponsorship` ledger entry - Return all remaining XRP in `FeeAmount` to the sponsor's balance @@ -636,7 +635,7 @@ This transaction uses the standard transaction fee (currently 10 drops, subject - Remove the object from both the sponsor's and sponsee's owner directories - Return the object reserve to the sponsor's available XRP -**Important Note:** Deleting a `Sponsorship` object does **not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `SponsorAccount` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. +_Note: Deleting a `Sponsorship` object does not affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `SponsorAccount` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used._ ### 9.5. Example JSON @@ -704,7 +703,7 @@ This transaction uses the standard transaction fee (currently 10 drops, subject All failure conditions mentioned in [section 8.3](#83-failure-conditions) still apply here. -**Additional failure conditions specific to `SponsorshipTransfer`:** +Additional failure conditions specific to `SponsorshipTransfer`: - `ObjectID` is specified but does not exist on the ledger (`tecNO_TARGET`) - `ObjectID` is specified but does not have a `SponsorAccount` field (object is not sponsored) (`tecNO_PERMISSION`) @@ -794,7 +793,7 @@ The `SponsorFee` permission allows an account to delegate its ability to sponsor ### 13.2. Transaction Types Affected -This permission affects **all transaction types** that support the `Sponsor` field (see section [8.1.1](#811-sponsor)). +This permission affects all transaction types that support the `Sponsor` field (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). When a transaction includes a `Sponsor` field with the `tfSponsorFee` flag enabled, the sponsor must have granted the `SponsorFee` permission to the transaction submitter (the `tx.Account`), unless: @@ -813,7 +812,7 @@ The `SponsorFee` permission allows an account to delegate its ability to sponsor ### 14.2. Transaction Types Affected -This permission affects **all transaction types** that support the `Sponsor` field (see section 8.1.1) and create new ledger objects or accounts. +This permission affects all transaction types that support the `Sponsor` field (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)) and create new ledger objects or accounts. When a transaction includes a `Sponsor` field with the `tfSponsorReserve` flag enabled, the sponsor must have granted the `SponsorReserve` permission to the transaction submitter (the `tx.Account`), unless: @@ -1058,10 +1057,6 @@ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to ## 19. Example Flows -Each example will show what the transaction will look like before **and** after both the sponsor and sponsee sign the transaction. - -The unsigned transaction must be autofilled before it is passed to the sponsor to sign. Tooling can be updated to handle combining the sponsor and sponsee signatures, similar to helper functions that already exist for multisigning. - ### 19.1. Fee Sponsorship #### 19.1.1. The Unsigned Transaction From 0bbd81bc3d7bce0436722407faf6cddfb513415f Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 7 Jan 2026 12:29:27 -0500 Subject: [PATCH 32/53] Sync Public/Sponsor XLS.md from Obsidian vault --- .../README.md | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index a08d68952..f79eb99bd 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -421,9 +421,10 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/co We propose these modifications: -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ------------------ | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | +| `SponsorSignature` | | `object` | `STObject` | This field contains all the signing information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | #### 8.1.1. `Sponsor` @@ -431,13 +432,10 @@ The `Sponsor` inner object contains all of the information for the sponsorship h The fields contained in this object are: -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------------- | --------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsoring account. | -| `Flags` | ✔️ | `number` | `UInt16` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | -| `SigningPubKey` | | `string` | `STBlob` | The `SigningPubKey` for `SponsorAccount`, if single-signing. | -| `TxnSignature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | -| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ---------------- | --------- | --------- | ------------- | ---------------------------------------------------------------------------------------- | +| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsoring account. | +| `Flags` | ✔️ | `number` | `UInt16` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | ##### 8.1.1.1. `Account` @@ -458,7 +456,15 @@ There are two flag values that are supported: This field **will** be a signing field (it will be included in transaction signatures). -##### 8.1.1.3. `SigningPubKey`, `TxnSignature` and `Signers` +#### 8.1.2 `SponsorSignature` + +| Field Name | Required? | JSON Type | Internal Type | Description | +| --------------- | --------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SigningPubKey` | | `string` | `STBlob` | The `SigningPubKey` for `SponsorAccount`, if single-signing. | +| `TxnSignature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | +| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | + +##### 8.1.2.1. `SigningPubKey`, `TxnSignature` and `Signers` Either `TxnSignature` or `Signers` must be included in the final transaction. @@ -468,13 +474,6 @@ There will be no additional transaction fee required for the use of the `TxnSign Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the transaction. There is one exception to this: if `lsfRequireSignatureForFee`/`lsfRequireSignatureForReserve` are not enabled for the type(s) of sponsorship in the transaction. -#### 8.1.1.4. Transaction Types - -**Transaction Types:** The `Sponsor` field may be included in any transaction type except: - -- Pseudo-transactions (`EnableAmendment`, `SetFee`, `UNLModify`) -- `Batch` transactions (inner transactions should use `Sponsor` instead) - ### 8.2. Transaction Fee If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base extunderscore fee$. @@ -490,7 +489,8 @@ The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Spo - The `SponsorAccount` doesn't exist on the ledger. - An invalid sponsorship flag is used. - `Sponsor.SigningPubKey`, `Sponsor.TxnSignature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). -- `Sponsor` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)) +- `Sponsor` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). +- Only one of `Sponsor` and `SponsorSignature` is included (they must either both be included, if the transaction is sponsored, or neither, if it is not). #### 8.3.2. Fee Sponsorship Failures From 22480495146bcd38a3b274b38c355bf5d6b403e2 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 7 Jan 2026 12:34:56 -0500 Subject: [PATCH 33/53] Sync Public/Sponsor XLS.md from Obsidian vault --- XLS-0068-sponsored-fees-and-reserves/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index f79eb99bd..de133b687 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -476,7 +476,7 @@ Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the trans ### 8.2. Transaction Fee -If the `Sponsor.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base extunderscore fee$. +If the `SponsorSignature.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base extunderscore fee$. The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Sponsor.Signers|) * base\_fee$ (plus transaction-specific fees). @@ -488,8 +488,8 @@ The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Spo - `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). - The `SponsorAccount` doesn't exist on the ledger. - An invalid sponsorship flag is used. -- `Sponsor.SigningPubKey`, `Sponsor.TxnSignature`, and `Sponsor.Signers` are all included (or other incorrect combinations of signing fields). -- `Sponsor` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). +- `SponsorSignature.SigningPubKey`, `SponsorSignature.TxnSignature`, and `SponsorSignature.Signers` are all included (or other incorrect combinations of signing fields). +- `Sponsor` or `SponsorSignature` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). - Only one of `Sponsor` and `SponsorSignature` is included (they must either both be included, if the transaction is sponsored, or neither, if it is not). #### 8.3.2. Fee Sponsorship Failures From b108456311e4b61898c0c3df94e6cff4252779ee Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 13 Jan 2026 16:07:51 -0500 Subject: [PATCH 34/53] add payment failure conditions --- .../README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index de133b687..07a24737e 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -747,6 +747,25 @@ This spec proposes the following additions: | ------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `tfSponsorCreatedAccount` | `0x00080000` | This flag is only valid if the `Payment` is used to create an account. If it is enabled, the created account will be sponsored by the `tx.Account`. | +### 11.2. Failure Conditions + +Existing failure conditions still apply (see [Payment documentation](https://xrpl.org/docs/references/protocol/transactions/types/payment)), with one exception: + +- If `tfSponsorCreatedAccount` is enabled, the XRP amount does not need to be greater than or equal to the account reserve requirement. + +Additional failure conditions when `tfSponsorCreatedAccount` is enabled: + +- `tfNoRippleDirect`, `tfPartialPayment`, or `tfLimitQuality` are enabled (`temINVALID_FLAG`) +- `Amount` specifies a non-XRP currency (`temBAD_AMOUNT`) +- `Destination` already exists (`tecNO_DST`) +- `Account` does not have enough XRP to cover the account reserve requirement (`tecINSUFFICIENT_RESERVE`) + +### 11.3. State Changes + +Existing state changes still apply (see [Payment documentation](https://xrpl.org/docs/references/protocol/transactions/types/payment)). + +If `tfSponsorCreatedAccount` is enabled, the created account's `AccountRoot` will have a `SponsorAccount` field pointing to the `tx.Account`. + ## 12. Transaction: `AccountDelete` This transaction deletes an account. From 8ba38f83064c454db0c446ac3491bcefdcce27a9 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 13 Jan 2026 22:44:17 -0500 Subject: [PATCH 35/53] Update XLS-0068-sponsored-fees-and-reserves/README.md Co-authored-by: tequ --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 07a24737e..e19116aa4 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -758,7 +758,7 @@ Additional failure conditions when `tfSponsorCreatedAccount` is enabled: - `tfNoRippleDirect`, `tfPartialPayment`, or `tfLimitQuality` are enabled (`temINVALID_FLAG`) - `Amount` specifies a non-XRP currency (`temBAD_AMOUNT`) - `Destination` already exists (`tecNO_DST`) -- `Account` does not have enough XRP to cover the account reserve requirement (`tecINSUFFICIENT_RESERVE`) +- `Account` does not have enough XRP to cover the account reserve requirement (`tecNO_DST_INSUF_XRP`) ### 11.3. State Changes From 6aa1204e33438188175e28e00403e546a31a6495 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 14 Jan 2026 11:59:06 -0500 Subject: [PATCH 36/53] respond to comments --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index e19116aa4..6fef4eaa0 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -757,7 +757,7 @@ Additional failure conditions when `tfSponsorCreatedAccount` is enabled: - `tfNoRippleDirect`, `tfPartialPayment`, or `tfLimitQuality` are enabled (`temINVALID_FLAG`) - `Amount` specifies a non-XRP currency (`temBAD_AMOUNT`) -- `Destination` already exists (`tecNO_DST`) +- `Destination` already exists (`tecNO_SPONSOR_PERMISSION`) - `Account` does not have enough XRP to cover the account reserve requirement (`tecNO_DST_INSUF_XRP`) ### 11.3. State Changes From ff0988c7ed0b5438c7021fc151d99b5291e8a2cd Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 26 Jan 2026 13:03:30 -0500 Subject: [PATCH 37/53] fix jsons, other minor nits --- .../README.md | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 6fef4eaa0..850d1fb46 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -221,7 +221,7 @@ The reserve is charged to the sponsor's account (the `Owner` field). ### 5.6. Deletion -#### 5.6.1. \*Deletion Transactions +#### 5.6.1. Deletion Transactions `SponsorshipSet` with the `tfDeleteObject` flag @@ -264,8 +264,8 @@ The `snake_case` form of the ledger object name is `sponsorship`. "LedgerEntryType": "Sponsorship", "Owner": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", "Sponsee": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", - "FeeAmount": "1000000", - "MaxFee": "1000", + "FeeAmount": "1000000", // 1 XRP, 1 million drops + "MaxFee": "1000", // 1000 drops "ReserveCount": 5, "Flags": 0, "OwnerNode": "0000000000000000", @@ -476,7 +476,7 @@ Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the trans ### 8.2. Transaction Fee -If the `SponsorSignature.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base extunderscore fee$. +If the `SponsorSignature.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base\_fee$. The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Sponsor.Signers|) * base\_fee$ (plus transaction-specific fees). @@ -1113,7 +1113,9 @@ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to Fee: "10", Sponsor: { Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 1, + Flags: 1 + }, + SponsorSignature: { SigningPubKey: "03072BBE5F93D4906FC31A690A2C269F2B9A56D60DA9C2C6C0D88FB51B644C6F94", // rSponsor's public key Signature: "3045022100C15AFB7C0C4F5EDFEC4667B292DAB165B96DAF3FFA6C7BBB3361E9EE19E04BC70220106C04B90185B67DB2C67864EB0A11AE6FB62280588954C6E4D9C1EF3710904D" }, @@ -1163,7 +1165,9 @@ The only way an account can be created is via a `Payment` transaction. So the sp Fee: "10", Sponsor: { Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 2, + Flags: 2 + }, + SponsorSignature: { SigningPubKey: "03072BBE5F93D4906FC31A690A2C269F2B9A56D60DA9C2C6C0D88FB51B644C6F94", // rSponsor's public key Signature: "30440220702ABC11419AD4940969CC32EB4D1BFDBFCA651F064F30D6E1646D74FBFC493902204E5B451B447B0F69904127F04FE71634BD825A8970B9467871DA89EEC4B021F8" }, @@ -1209,7 +1213,9 @@ The only way an account can be created is via a `Payment` transaction. So the sp Fee: "10", Sponsor: { Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 2, + Flags: 2 + }, + SponsorSignature: { SigningPubKey: "03072BBE5F93D4906FC31A690A2C269F2B9A56D60DA9C2C6C0D88FB51B644C6F94", // rSponsor's public key Signature: "30450221009878F3A321250341886FE344E0B50700C8020ABAA25301925BD84DDB5421D432022002A3C72C54BACB5E7DAEC48E2A1D75DCBB8BA3B2212C7FC22F070CCABAF76EC1" }, From 8d8d8d657666953d9ee656a8393543dc8ccaf64e Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 28 Jan 2026 17:57:59 -0500 Subject: [PATCH 38/53] get parser passing --- .../README.md | 111 ++++++++++++++++-- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 850d1fb46..6627d6154 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -169,7 +169,7 @@ Implementations **MUST** treat the presence or absence of `SponsorAccount` as th _Note: this object does not need to be created in order to sponsor accounts. It is an offered convenience, so that sponsors do not have to co-sign every sponsored transaction if they don't want to, especially for transaction fees. It also allows them to set a maximum balance even if they still want to co-sign transactions._ -### 5.1. Object ID +### 5.1. Object Identifier #### 5.1.1. Key Space @@ -213,7 +213,7 @@ There are two flags on this object: The object is owned by `Owner`, who also pays the reserve for this object. -### 5.5. Reserve +### 5.5. Reserves **Reserve Requirement:** Standard @@ -243,7 +243,7 @@ This object must be deleted before its owner account (the sponsor) can be delete **Note on Existing Sponsored Objects:** Deleting a `Sponsorship` object does **not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `SponsorAccount` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. -### 5.7. Invariant Checks +### 5.7. Invariants The following invariants must always hold for a `Sponsorship` object: @@ -277,7 +277,7 @@ The `snake_case` form of the ledger object name is `sponsorship`. ## 6. Ledger Entry: `AccountRoot` -An `AccountRoot` ledger entry type describes a single [account](https://xrpl.org/docs/concepts/accounts), its settings, and XRP balance. +An [`AccountRoot` ledger entry](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/accountroot) type describes a single [account](https://xrpl.org/docs/concepts/accounts), its settings, and XRP balance. ### 6.1. Fields @@ -363,6 +363,23 @@ $$\sum_{accounts} SponsoredOwnerCount = \sum_{accounts} SponsoringOwnerCount$$ This ensures that every sponsored object is properly accounted for on both the sponsee and sponsor sides. +### 6.4. Example JSON + +```json +{ + "LedgerEntryType": "AccountRoot", + "Account": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", + "Balance": "100000000", // 100 XRP, in drops + "OwnerCount": 5, + "SponsorAccount": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "SponsoredOwnerCount": 2, + "SponsoringOwnerCount": 1, + "SponsoringAccountCount": 1, + "PreviousTxnID": "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", + "PreviousTxnLgrSeq": 12345679 +} +``` + ## 7. Ledger Entry: `RippleState` A `RippleState` ledger entry represents a [trust line](https://xrpl.org/docs/concepts/tokens/fungible-tokens) between two accounts. Each account can change its own limit and other settings, but the balance is a single shared value. A trust line that is entirely in its default state is considered the same as a trust line that does not exist and is automatically deleted. You can create or modify a trust line with a [TrustSet transaction](https://xrpl.org/docs/references/protocol/transactions/types/trustset). @@ -411,6 +428,36 @@ Existing invariants remain. The common field `SponsorAccount` must not be on any `RippleState` objects (they must use `HighSponsorAccount` and `LowSponsorAccount` instead). +### 7.3. Example JSON + +```json +{ + "LedgerEntryType": "RippleState", + "Balance": { + "currency": "USD", + "issuer": "rLowAccountAddressXXXXXXXXXXXXXXX", + "value": "-10" + }, + "HighLimit": { + "currency": "USD", + "issuer": "rHighAccountAddressXXXXXXXXXXXXXX", + "value": "100" + }, + "LowLimit": { + "currency": "USD", + "issuer": "rLowAccountAddressXXXXXXXXXXXXXXX", + "value": "0" + }, + "HighSponsorAccount": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "LowSponsorAccount": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "Flags": 262144, + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnID": "ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789", + "PreviousTxnLgrSeq": 12345680 +} +``` + ## 8. Transactions: Common Fields ### 8.1. Fields @@ -723,15 +770,35 @@ Additional failure conditions specific to `SponsorshipTransfer`: - The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. - If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. +### 10.7. Example JSON + +```json +{ + "TransactionType": "SponsorshipTransfer", + "Account": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", + "ObjectID": "13F1A9B5C2D3E4F613F1A9B5C2D3E4F613F1A9B5C2D3E4F613F1A9B5C2D3E4F6", + "Sponsor": { + "Account": "rNEWSponsor3LNcTz8JF2oJC6qaww6RZ7Lw", + "Flags": 2 + }, + "Fee": "12", + "Sequence": 43 +} +``` + ## 11. Transaction: `Payment` A Payment transaction represents a transfer of value from one account to another. (Depending on the path taken, this can involve additional exchanges of value, which occur atomically.) This transaction type can be used for several [types of payments](https://xrpl.org/docs/references/protocol/transactions/types/payment#types-of-payments). Payments are also the only way to [create accounts](https://xrpl.org/docs/references/protocol/transactions/types/payment#creating-accounts). -As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment) are the fields that `Payment` currently has. This amendment proposes no changes to the fields, only to the flags and behavior. +As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment) are the fields that `Payment` currently has. + +### 11.1. Fields -### 11.1. Flags +This amendment proposes no changes to the fields, only to the flags and behavior. + +### 11.2. Flags As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/payment#payment-flags) are the flags that `Payment` currently has: @@ -747,7 +814,7 @@ This spec proposes the following additions: | ------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `tfSponsorCreatedAccount` | `0x00080000` | This flag is only valid if the `Payment` is used to create an account. If it is enabled, the created account will be sponsored by the `tx.Account`. | -### 11.2. Failure Conditions +### 11.3. Failure Conditions Existing failure conditions still apply (see [Payment documentation](https://xrpl.org/docs/references/protocol/transactions/types/payment)), with one exception: @@ -760,19 +827,37 @@ Additional failure conditions when `tfSponsorCreatedAccount` is enabled: - `Destination` already exists (`tecNO_SPONSOR_PERMISSION`) - `Account` does not have enough XRP to cover the account reserve requirement (`tecNO_DST_INSUF_XRP`) -### 11.3. State Changes +### 11.4. State Changes Existing state changes still apply (see [Payment documentation](https://xrpl.org/docs/references/protocol/transactions/types/payment)). If `tfSponsorCreatedAccount` is enabled, the created account's `AccountRoot` will have a `SponsorAccount` field pointing to the `tx.Account`. +### 11.5. Example JSON + +```json +{ + "TransactionType": "Payment", + "Account": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "Destination": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", // the new sponsored account + "Amount": "1", // 1 drop, the minimum + "Flags": 524288, // tfSponsorCreatedAccount + "Fee": "10", + "Sequence": 3 +} +``` + ## 12. Transaction: `AccountDelete` This transaction deletes an account. -As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete) are the fields that `AccountDelete` currently has. This amendment proposes no changes to the fields, only to the behavior. +As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete) are the fields that `AccountDelete` currently has. + +### 12.1. Fields + +This amendment proposes no changes to the fields, only to the behavior. -### 12.1. Failure Conditions +### 12.2. Failure Conditions Existing failure conditions still apply (see [AccountDelete documentation](https://xrpl.org/docs/references/protocol/transactions/types/accountdelete)). @@ -784,7 +869,7 @@ Additional failure conditions for sponsored accounts: - If the `AccountRoot` associated with `tx.Account` has a non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` field: - The transaction fails with `tecHAS_OBLIGATIONS` (account is currently sponsoring other accounts or objects and cannot be deleted until those sponsorships are transferred or dissolved) -### 12.2. State Changes +### 12.3. State Changes Existing state changes still apply, including rules around deletion blockers. @@ -792,13 +877,13 @@ If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` fie If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. -### 12.3. Example JSON +### 12.4. Example JSON ```json { "TransactionType": "AccountDelete", "Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm", - "Destination": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "Destination": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", // the sponsor "Fee": "5000000", "Sequence": 2470665 } From f0b1dddd661d5b92de8eb8e09580149985b29bc7 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 29 Jan 2026 11:52:16 -0500 Subject: [PATCH 39/53] update spec based on discussion --- .../README.md | 196 ++++++++---------- 1 file changed, 83 insertions(+), 113 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 6627d6154..bb59857e5 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -108,15 +108,15 @@ As a reference, here are the fields that all ledger objects currently have: We propose this additional field: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ---------------- | --------- | --------- | ------------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `SponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor paying the owner reserve for a given ledger object. When present, it indicates that the reserve burden for that object has been shifted from the owner to the sponsor. | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ---------- | --------- | --------- | ------------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Sponsor` | | | N/A | `string` | `AccountID` | The sponsor paying the owner reserve for a given ledger object. When present, it indicates that the reserve burden for that object has been shifted from the owner to the sponsor. | ### 4.2. Invariant Checks #### 4.2.1. Allowed Ledger Entry Types -The `SponsorAccount` field **MAY** appear on the following ledger entry types: +The `Sponsor` field **MAY** appear on the following ledger entry types: - `AccountRoot` (for account reserve sponsorship) - `Offer` @@ -134,9 +134,9 @@ The `SponsorAccount` field **MAY** appear on the following ledger entry types: - `DID` - Any other ledger entry type that contributes to an account's owner reserve, subject to implementation details -The `SponsorAccount` field **MUST NOT** appear on: +The `Sponsor` field **MUST NOT** appear on: -- `RippleState` objects, which instead use the `HighSponsorAccount` and `LowSponsorAccount` fields defined in section 7.1 +- `RippleState` objects, which instead use the `HighSponsor` and `LowSponsor` fields defined in section 7.1 - `DirectoryNode` objects (these do not have reserves) - `Amendments` objects (global ledger objects) - `FeeSettings` objects (global ledger objects) @@ -152,12 +152,12 @@ The `SponsorAccount` field **MUST NOT** appear on: #### 4.2.3. Constraints - **Self-Sponsorship**: An account **MAY** sponsor its own objects. While this does not reduce the account's reserve requirement, it may be useful for accounting or organizational purposes. -- **Relationship to Ownership**: The `SponsorAccount` value **MAY** differ from the object's owner (as indicated by the `Owner` or `Account` field). This is the typical case for sponsorship. +- **Relationship to Ownership**: The `Sponsor` value **MAY** differ from the object's owner (as indicated by the `Owner` or `Account` field). This is the typical case for sponsorship. - **Sponsor Account Requirements**: The sponsor account **MUST** have sufficient XRP to meet its reserve requirements, including reserves for all objects and accounts it sponsors. #### 4.2.4. Authoritative Indication -Implementations **MUST** treat the presence or absence of `SponsorAccount` as the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: +Implementations **MUST** treat the presence or absence of `Sponsor` as the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: - The object's reserve is counted against the sponsor's `SponsoringOwnerCount` (or `SponsoringAccountCount` for `AccountRoot`), not the owner's `OwnerCount`. - The owner's `SponsoredOwnerCount` is incremented (for non-`AccountRoot` objects). @@ -165,7 +165,7 @@ Implementations **MUST** treat the presence or absence of `SponsorAccount` as th ## 5. Ledger Entry: `Sponsorship` -`Sponsorship` is an object that reflects a sponsoring relationship between two accounts, `SponsorAccount` and `Sponsee`. This allows sponsors to "pre-fund" sponsees, if they so desire. +`Sponsorship` is an object that reflects a sponsoring relationship between two accounts, `Sponsor` and `Sponsee`. This allows sponsors to "pre-fund" sponsees, if they so desire. _Note: this object does not need to be created in order to sponsor accounts. It is an offered convenience, so that sponsors do not have to co-sign every sponsored transaction if they don't want to, especially for transaction fees. It also allows them to set a maximum balance even if they still want to co-sign transactions._ @@ -241,7 +241,7 @@ This object must be deleted before its owner account (the sponsor) can be delete 1. Delete the `Sponsorship` object via `SponsorshipSet` with `tfDeleteObject`, or 2. Wait for the sponsee to delete it (if the sponsee has permission to do so) -**Note on Existing Sponsored Objects:** Deleting a `Sponsorship` object does **not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `SponsorAccount` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. +**Note on Existing Sponsored Objects:** Deleting a `Sponsorship` object does **not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `Sponsor` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. ### 5.7. Invariants @@ -319,16 +319,16 @@ This spec proposes these additional fields: | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | | ------------------------ | --------- | --------- | ------------- | --------- | ------------- | ----------------------------------------------------------------------------- | -| `SponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the account reserve for this account. | +| `Sponsor` | | | N/A | `string` | `AccountID` | The sponsor that is paying the account reserve for this account. | | `SponsoredOwnerCount` | | | `0` | `number` | `UInt32` | The number of objects the account owns that are being sponsored by a sponsor. | | `SponsoringOwnerCount` | | | `0` | `number` | `UInt32` | The number of objects the account is sponsoring the reserve for. | | `SponsoringAccountCount` | | | `0` | `number` | `UInt32` | The number of accounts that the account is sponsoring the reserve for. | -#### 6.1.1. `SponsorAccount` +#### 6.1.1. `Sponsor` -The `SponsorAccount` field is already added in the ledger common fields (see section [4.2](#42-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. +The `Sponsor` field is already added in the ledger common fields (see section [4.2](#42-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. -This field is included if the account was created with a sponsor paying its account reserve. If this sponsored account is deleted, the destination of the `AccountDelete` transaction must equal `SponsorAccount`, so that the sponsor can recoup their fees. +This field is included if the account was created with a sponsor paying its account reserve. If this sponsored account is deleted, the destination of the `AccountDelete` transaction must equal `Sponsor`, so that the sponsor can recoup their fees. _Note: The `Destination` field of `AccountDelete` will still work as-is if the account is not sponsored, where it can be set to any account._ @@ -342,7 +342,7 @@ The total account reserve should now be calculated as: $$ \displaylines{ -(acct.SponsorAccount ext{ ? } 0 : acctReserve) + +(acct.Sponsor ext{ ? } 0 : acctReserve) + objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + acctReserve * acct.SponsoringAccountCount } @@ -353,7 +353,7 @@ $$ The following invariants must hold for `AccountRoot` objects with sponsorship fields: - `SponsoredOwnerCount <= OwnerCount` (cannot have more sponsored objects than total owned objects) -- If `SponsorAccount` is present, it must be a valid `AccountID` that exists on the ledger +- If `Sponsor` is present, it must be a valid `AccountID` that exists on the ledger - The reserve calculation must always result in a non-negative value **Global Invariant (referenced in section 18.2):** @@ -371,7 +371,7 @@ This ensures that every sponsored object is properly accounted for on both the s "Account": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", "Balance": "100000000", // 100 XRP, in drops "OwnerCount": 5, - "SponsorAccount": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "Sponsor": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", "SponsoredOwnerCount": 2, "SponsoringOwnerCount": 1, "SponsoringAccountCount": 1, @@ -415,10 +415,10 @@ As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/led This spec proposes these additional fields: -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| -------------------- | --------- | --------- | ------------- | --------- | ------------- | ---------------------------------------------------------------------------------------- | -| `HighSponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve on behalf of the "high" account on the trustline. | -| `LowSponsorAccount` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve on behalf of the "low" account on the trustline. | +| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | +| ------------- | --------- | --------- | ------------- | --------- | ------------- | ---------------------------------------------------------------------------------------- | +| `HighSponsor` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve on behalf of the "high" account on the trustline. | +| `LowSponsor` | | | N/A | `string` | `AccountID` | The sponsor that is paying the reserve on behalf of the "low" account on the trustline. | These additional fields are necessary for a trustline since the reserve for this object may be held by two accounts (in the case of a bidirectional trustline). @@ -426,7 +426,7 @@ These additional fields are necessary for a trustline since the reserve for this Existing invariants remain. -The common field `SponsorAccount` must not be on any `RippleState` objects (they must use `HighSponsorAccount` and `LowSponsorAccount` instead). +The common field `Sponsor` must not be on any `RippleState` objects (they must use `HighSponsor` and `LowSponsor` instead). ### 7.3. Example JSON @@ -448,8 +448,8 @@ The common field `SponsorAccount` must not be on any `RippleState` objects (they "issuer": "rLowAccountAddressXXXXXXXXXXXXXXX", "value": "0" }, - "HighSponsorAccount": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", - "LowSponsorAccount": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "HighSponsor": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", + "LowSponsor": "rN7n7otQDd6FczFgLdlqtyMVrn3HMfXpf", "Flags": 262144, "HighNode": "0000000000000000", "LowNode": "0000000000000000", @@ -470,29 +470,13 @@ We propose these modifications: | Field Name | Required? | JSON Type | Internal Type | Description | | ------------------ | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Sponsor` | | `object` | `STObject` | This field contains all the information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | +| `Sponsor` | | `string` | `STAccount` | The sponsoring account. | +| `SponsorFlags` | | `number` | `UInt32` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | | `SponsorSignature` | | `object` | `STObject` | This field contains all the signing information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | -#### 8.1.1. `Sponsor` +##### 8.1.1. `SponsorFlags` -The `Sponsor` inner object contains all of the information for the sponsorship happening in the transaction. - -The fields contained in this object are: - -| Field Name | Required? | JSON Type | Internal Type | Description | -| ---------------- | --------- | --------- | ------------- | ---------------------------------------------------------------------------------------- | -| `SponsorAccount` | ✔️ | `string` | `AccountID` | The sponsoring account. | -| `Flags` | ✔️ | `number` | `UInt16` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | - -##### 8.1.1.1. `Account` - -The `Sponsor.Account` field represents the sponsor. - -This field **will** be a signing field (it will be included in transaction signatures). - -##### 8.1.1.2. `Flags` - -The `Flags` field allows the user to specify which sponsorship type(s) they wish to participate in. At least one flag **must** be specified if the `Sponsor` field is included in a transaction. +The `SponsorFlags` field allows the user to specify which sponsorship type(s) they wish to participate in. This field **must** be included if the `Sponsor` field is included in a transaction, and at least one flag **must** be specified if the `Sponsor` field is included in a transaction. There are two flag values that are supported: @@ -501,15 +485,13 @@ There are two flag values that are supported: | `tfSponsorFee` | `0x00000001` | Sponsoring (paying for) the fee of the transaction. | | `tfSponsorReserve` | `0x00000002` | Sponsoring the reserve for any objects created in the transaction. | -This field **will** be a signing field (it will be included in transaction signatures). - #### 8.1.2 `SponsorSignature` -| Field Name | Required? | JSON Type | Internal Type | Description | -| --------------- | --------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `SigningPubKey` | | `string` | `STBlob` | The `SigningPubKey` for `SponsorAccount`, if single-signing. | -| `TxnSignature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. All signing fields must be included in the signature, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | -| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. All signing fields must be included, including `Sponsor.SponsorAccount` and `Sponsor.Flags`. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| --------------- | --------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SigningPubKey` | | `string` | `STBlob` | The `SigningPubKey` for `Sponsor`, if single-signing. | +| `TxnSignature` | | `string` | `STBlob` | A signature of the transaction from the sponsor, to indicate their approval of this transaction, if single-signing. | +| `Signers` | | `array` | `STArray` | An array of signatures of the transaction from the sponsor's signers to indicate their approval of this transaction, if the sponsor is multi-signing. | ##### 8.1.2.1. `SigningPubKey`, `TxnSignature` and `Signers` @@ -533,7 +515,7 @@ The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Spo - `Sponsor.TxnSignature` is invalid. - `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). -- The `SponsorAccount` doesn't exist on the ledger. +- The `Sponsor` doesn't exist on the ledger. - An invalid sponsorship flag is used. - `SponsorSignature.SigningPubKey`, `SponsorSignature.TxnSignature`, and `SponsorSignature.Signers` are all included (or other incorrect combinations of signing fields). - `Sponsor` or `SponsorSignature` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). @@ -613,8 +595,8 @@ This transaction creates and updates the `Sponsorship` object. | ----------------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipSet`). | | `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the sponsor or the sponsee. | -| `SponsorAccount` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | -| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `SponsorAccount`. | +| `Sponsor` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | +| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `Sponsor`. | | `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. This value will replace what is currently in the `Sponsorship.FeeAmount` field (if it exists). | | `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | | `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. This value will replace what is currently in the `Sponsorship.ReserveCount` field (if it exists). | @@ -637,13 +619,13 @@ This transaction uses the standard transaction fee (currently 10 drops, subject ### 9.4. Failure Conditions -- `tx.Account` is not equal to either `tx.SponsorAccount` or `tx.Sponsee` (`temMALFORMED`) -- Both `SponsorAccount` and `Sponsee` are specified (`temMALFORMED`) -- Neither `SponsorAccount` nor `Sponsee` is specified (`temMALFORMED`) -- `SponsorAccount` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled (`temMALFORMED` - only sponsor can create/update) +- `tx.Account` is not equal to either `tx.Sponsor` or `tx.Sponsee` (`temMALFORMED`) +- Both `Sponsor` and `Sponsee` are specified (`temMALFORMED`) +- Neither `Sponsor` nor `Sponsee` is specified (`temMALFORMED`) +- `Sponsor` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled (`temMALFORMED` - only sponsor can create/update) - `MaxFee` is less than the base fee or is not denominated in XRP (`temBAD_AMOUNT`) - `FeeAmount` is not denominated in XRP (`temBAD_AMOUNT`) -- `SponsorAccount` or `Sponsee` does not exist on the ledger (`terNO_ACCOUNT`) +- `Sponsor` or `Sponsee` does not exist on the ledger (`terNO_ACCOUNT`) - `Owner == Sponsee` (attempting to create self-sponsorship) (`temMALFORMED`) - Sponsor does not have sufficient XRP to cover the reserve for the `Sponsorship` object (`tecINSUFFICIENT_RESERVE`) - If `tfDeleteObject` is enabled: @@ -682,7 +664,7 @@ If deleting the `Sponsorship` object (`tfDeleteObject` flag): - Remove the object from both the sponsor's and sponsee's owner directories - Return the object reserve to the sponsor's available XRP -_Note: Deleting a `Sponsorship` object does not affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `SponsorAccount` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used._ +_Note: Deleting a `Sponsorship` object does not affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `Sponsor` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used._ ### 9.5. Example JSON @@ -705,12 +687,14 @@ This transaction transfers a sponsor relationship for a particular ledger object ### 10.1. Fields -| Field Name | Required? | JSON Type | Internal Type | Description | -| ----------------- | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------- | -| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | -| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | -| `ObjectID` | | `string` | `Hash256` | The ID of the object to transfer sponsorship. | -| `Sponsor` | | `object` | `STObject` | The new sponsor of the object. This field contains the same subfields as above. | +| Field Name | Required? | JSON Type | Internal Type | Description | +| ------------------ | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | +| `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | +| `ObjectID` | | `string` | `Hash256` | The ID of the object to transfer sponsorship. | +| `Sponsor` | | `string` | `STAccount` | The new sponsor of the object. | +| `SponsorFlags` | | `number` | `UInt32` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). The `tfSponsorReserve` flag **must** be included. | +| `SponsorSignature` | | `object` | `STObject` | This field contains all the signing information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | #### 10.1.1. `ObjectID` @@ -753,8 +737,8 @@ All failure conditions mentioned in [section 8.3](#83-failure-conditions) still Additional failure conditions specific to `SponsorshipTransfer`: - `ObjectID` is specified but does not exist on the ledger (`tecNO_TARGET`) -- `ObjectID` is specified but does not have a `SponsorAccount` field (object is not sponsored) (`tecNO_PERMISSION`) -- `ObjectID` is not specified and the `tx.Account` does not have a `SponsorAccount` field (account is not sponsored) (`tecNO_PERMISSION`) +- `ObjectID` is specified but does not have a `Sponsor` field (object is not sponsored) (`tecNO_PERMISSION`) +- `ObjectID` is not specified and the `tx.Account` does not have a `Sponsor` field (account is not sponsored) (`tecNO_PERMISSION`) - `tx.Account` is neither the current sponsor nor the owner (sponsee) of the object/account specified by `ObjectID` (`tecNO_PERMISSION`) - If transferring the sponsorship to a new sponsor: - The new sponsor account does not exist (`terNO_ACCOUNT`) @@ -777,10 +761,8 @@ Additional failure conditions specific to `SponsorshipTransfer`: "TransactionType": "SponsorshipTransfer", "Account": "rfkDkFai4jUfCvAJiZ5Vm7XvvWjYvDqeYo", "ObjectID": "13F1A9B5C2D3E4F613F1A9B5C2D3E4F613F1A9B5C2D3E4F613F1A9B5C2D3E4F6", - "Sponsor": { - "Account": "rNEWSponsor3LNcTz8JF2oJC6qaww6RZ7Lw", - "Flags": 2 - }, + "Sponsor": "rNEWSponsor3LNcTz8JF2oJC6qaww6RZ7Lw", + "SponsorFlags": 2, "Fee": "12", "Sequence": 43 } @@ -831,7 +813,7 @@ Additional failure conditions when `tfSponsorCreatedAccount` is enabled: Existing state changes still apply (see [Payment documentation](https://xrpl.org/docs/references/protocol/transactions/types/payment)). -If `tfSponsorCreatedAccount` is enabled, the created account's `AccountRoot` will have a `SponsorAccount` field pointing to the `tx.Account`. +If `tfSponsorCreatedAccount` is enabled, the created account's `AccountRoot` will have a `Sponsor` field pointing to the `tx.Account`. ### 11.5. Example JSON @@ -863,8 +845,8 @@ Existing failure conditions still apply (see [AccountDelete documentation](https Additional failure conditions for sponsored accounts: -- If the `AccountRoot` associated with `tx.Account` has a `SponsorAccount` field: - - The `Destination` is not equal to `AccountRoot.SponsorAccount` (`tecNO_PERMISSION` - sponsored account funds must go to sponsor) +- If the `AccountRoot` associated with `tx.Account` has a `Sponsor` field: + - The `Destination` is not equal to `AccountRoot.Sponsor` (`tecNO_PERMISSION` - sponsored account funds must go to sponsor) - If the `AccountRoot` associated with `tx.Account` has a non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` field: - The transaction fails with `tecHAS_OBLIGATIONS` (account is currently sponsoring other accounts or objects and cannot be deleted until those sponsorships are transferred or dissolved) @@ -873,9 +855,9 @@ Additional failure conditions for sponsored accounts: Existing state changes still apply, including rules around deletion blockers. -If the `AccountRoot` associated with the `tx.Account` has a `SponsorAccount` field, the `SponsorAccount`'s `AccountRoot.SponsoringAccountCount` is decremented by 1. +If the `AccountRoot` associated with the `tx.Account` has a `Sponsor` field, the `Sponsor`'s `AccountRoot.SponsoringAccountCount` is decremented by 1. -If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `SponsorAccount`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. +If the `AccountRoot` associated with the `tx.Account` has a `SponsoredOwnerCount` field, the `Sponsor`'s `SponsoringOwnerCount` is decremented by the `tx.Account`'s `SponsoredOwnerCount`. ### 12.4. Example JSON @@ -988,7 +970,7 @@ There are no additional failure conditions. "value": "1000" }, "HighNode": "0000000000000000", - "HighSponsorAccount": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "HighSponsor": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", "LedgerEntryType": "RippleState", "LowLimit": { "currency": "USD", @@ -1009,7 +991,7 @@ There are no additional failure conditions. ## 16. RPC: `account_sponsoring` -The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `SponsorAccount` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). +The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `Sponsor` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). ### 16.1. Request Fields @@ -1075,7 +1057,7 @@ The response fields are nearly identical to `account_objects`. "value": "1000" }, "HighNode": "0000000000000000", - "HighSponsorAccount": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "HighSponsor": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", "LedgerEntryType": "RippleState", "LowLimit": { "currency": "USD", @@ -1096,7 +1078,7 @@ The response fields are nearly identical to `account_objects`. "PreviousTxnID": "0D5FB50FA65C9FE1538FD7E398FFFE9D1908DFA4576D8D7A020040686F93C77D", "PreviousTxnLgrSeq": 14091574, "Sequence": 1, - "SponsorAccount": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + "Sponsor": "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", "index": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8" }, { @@ -1139,7 +1121,7 @@ An axiom that is out of scope: the sponsee may not have any control over a spons ### 17.2. Signatures -Since a fee sponsorship must approve of the `Fee` field, and a reserve sponsorship must approve of a broad set of transaction fields, the sponsor must always sign the whole transaction. This also avoids needing to have different sponsorship processes for different sponsorship types. This includes the non-signature parts of the `Sponsor` object (`Sponsor.Account` and `Sponsor.Flags`). The same is true for the sponsee's transaction signature; the sponsee must approve of the sponsor and sponsorship type. +Since a fee sponsorship must approve of the `Fee` field, and a reserve sponsorship must approve of a broad set of transaction fields, the sponsor must always sign the whole transaction. This also avoids needing to have different sponsorship processes for different sponsorship types. The same is true for the sponsee's transaction signature; the sponsee must approve of the sponsor and sponsorship type. A sponsor's `Signature` cannot be replayed or attached to a different transaction, since the whole transaction (including the `Account` and `Sequence` values) must be signed. @@ -1175,10 +1157,8 @@ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to Amount: "20000000", Sequence: 3, Fee: "10", - Sponsor: { - Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 1 - } + Sponsor: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + SponsorFlags: 1 } ``` @@ -1196,10 +1176,8 @@ In other words, the sum of all accounts' `SponsoredOwnerCount`s must be equal to Amount: "20000000", Sequence: 3, Fee: "10", - Sponsor: { - Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 1 - }, + Sponsor: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + SponsorFlags: 1, SponsorSignature: { SigningPubKey: "03072BBE5F93D4906FC31A690A2C269F2B9A56D60DA9C2C6C0D88FB51B644C6F94", // rSponsor's public key Signature: "3045022100C15AFB7C0C4F5EDFEC4667B292DAB165B96DAF3FFA6C7BBB3361E9EE19E04BC70220106C04B90185B67DB2C67864EB0A11AE6FB62280588954C6E4D9C1EF3710904D" @@ -1227,10 +1205,8 @@ The only way an account can be created is via a `Payment` transaction. So the sp Amount: "20000000", Sequence: 3, Fee: "10", - Sponsor: { - Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 2 - } + Sponsor: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + SponsorFlags: 2, } ``` @@ -1248,10 +1224,8 @@ The only way an account can be created is via a `Payment` transaction. So the sp Amount: "20000000", Sequence: 3, Fee: "10", - Sponsor: { - Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 2 - }, + Sponsor: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + SponsorFlags: 2, SponsorSignature: { SigningPubKey: "03072BBE5F93D4906FC31A690A2C269F2B9A56D60DA9C2C6C0D88FB51B644C6F94", // rSponsor's public key Signature: "30440220702ABC11419AD4940969CC32EB4D1BFDBFCA651F064F30D6E1646D74FBFC493902204E5B451B447B0F69904127F04FE71634BD825A8970B9467871DA89EEC4B021F8" @@ -1276,10 +1250,8 @@ The only way an account can be created is via a `Payment` transaction. So the sp TicketCount: 100, Sequence: 3, Fee: "10", - Sponsor: { - Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 2 - } + Sponsor: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + SponsorFlags: 2, } ``` @@ -1296,10 +1268,8 @@ The only way an account can be created is via a `Payment` transaction. So the sp TicketCount: 100, Sequence: 3, Fee: "10", - Sponsor: { - Account: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", - Flags: 2 - }, + Sponsor: "rSponsor1VktvzBz8JF2oJC6qaww6RZ7Lw", + SponsorFlags: 2, SponsorSignature: { SigningPubKey: "03072BBE5F93D4906FC31A690A2C269F2B9A56D60DA9C2C6C0D88FB51B644C6F94", // rSponsor's public key Signature: "30450221009878F3A321250341886FE344E0B50700C8020ABAA25301925BD84DDB5421D432022002A3C72C54BACB5E7DAEC48E2A1D75DCBB8BA3B2212C7FC22F070CCABAF76EC1" @@ -1379,7 +1349,7 @@ All ledgers and transactions created before the `Sponsor` amendment are activate The `AccountDelete` transaction has two new behavioral changes: -1. **Destination Constraint for Sponsored Accounts**: If an account being deleted has a `SponsorAccount` field (indicating the account reserve is sponsored), the `Destination` field of the `AccountDelete` transaction must equal the `SponsorAccount`. This ensures sponsors can recoup their reserve. Accounts without sponsorship can still use `AccountDelete` with any valid destination as before. +1. **Destination Constraint for Sponsored Accounts**: If an account being deleted has a `Sponsor` field (indicating the account reserve is sponsored), the `Destination` field of the `AccountDelete` transaction must equal the `Sponsor`. This ensures sponsors can recoup their reserve. Accounts without sponsorship can still use `AccountDelete` with any valid destination as before. 2. **New Failure Condition**: `AccountDelete` will fail with `tecHAS_OBLIGATIONS` if the account has non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` fields, indicating the account is currently sponsoring other accounts or objects. This prevents sponsors from deleting their accounts while they have active sponsorship obligations. @@ -1387,8 +1357,8 @@ The `AccountDelete` transaction has two new behavioral changes: The amendment introduces new fields that affect reserve calculations: -- **New AccountRoot Fields**: `SponsorAccount`, `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` modify how an account's required XRP reserve is calculated. -- **New Ledger Entry Fields**: The `SponsorAccount` field (and `HighSponsorAccount`/`LowSponsorAccount` for `RippleState`) indicates when an object's reserve is sponsored. +- **New AccountRoot Fields**: `Sponsor`, `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` modify how an account's required XRP reserve is calculated. +- **New Ledger Entry Fields**: The `Sponsor` field (and `HighSponsor`/`LowSponsor` for `RippleState`) indicates when an object's reserve is sponsored. - **Reserve Calculation**: The reserve formula changes from `acctReserve + objReserve * OwnerCount` to account for sponsored objects and sponsorship obligations (see section 6.2). Accounts without these new fields continue to use the existing reserve calculation, ensuring backward compatibility. @@ -1397,7 +1367,7 @@ Accounts without these new fields continue to use the existing reserve calculati Legacy tooling that does not understand the new sponsorship fields may experience the following: -- **Display Issues**: Tools may not correctly display or interpret `SponsorAccount`, `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` fields. +- **Display Issues**: Tools may not correctly display or interpret `Sponsor`, `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` fields. - **Reserve Calculations**: Tools that calculate required reserves may produce incorrect results if they do not account for the new reserve formula. - **RPC Methods**: The new `account_sponsoring` RPC method and the `sponsored` filter on `account_objects` will not be available in tools that have not been updated. - **Transaction Construction**: Tools will need updates to support constructing transactions with the `Sponsor` field and handling the dual-signature flow. @@ -1433,15 +1403,15 @@ This section outlines the testing strategy for the sponsored fees and reserves f **`AccountRoot` Field Tests:** -- Addition and removal of `SponsorAccount` field on accounts +- Addition and removal of `Sponsor` field on accounts - Correct tracking of `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` - Reserve calculation with new fields (see section 6.2) - Interaction between sponsored and unsponsored objects on the same account **`RippleState` Field Tests:** -- Addition and removal of `HighSponsorAccount` and `LowSponsorAccount` fields -- Verification that `SponsorAccount` common field is not used on `RippleState` objects +- Addition and removal of `HighSponsor` and `LowSponsor` fields +- Verification that `Sponsor` common field is not used on `RippleState` objects - Proper handling of bidirectional trust line sponsorship ### 22.2. Unit Tests for Transactions From 7dd03ee362899c40cf2fbdaaba349c4e40ce9592 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 29 Jan 2026 12:06:05 -0500 Subject: [PATCH 40/53] update sponsor transfer rules --- .../README.md | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index bb59857e5..0185d52fc 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -685,6 +685,15 @@ _Note: Deleting a `Sponsorship` object does not affect already-sponsored ledger This transaction transfers a sponsor relationship for a particular ledger object's object reserve. The sponsor relationship can either be passed on to a new sponsor, or dissolved entirely (with the sponsee taking on the reserve). Either the sponsor or sponsee may submit this transaction at any point in time. +There are three valid transfer scenarios: + +- Transferring from sponsor to sponsee (sponsored to unsponsored) + - Either the sponsor or sponsee may submit this transaction. Both have the right to end the relationship on that object at any time. +- Transferring from sponsee to sponsor (unsponsored to sponsored) + - Only the sponsee may submit this transaction. It follows a standard sponsoring flow in terms of signing. +- Transferring from sponsor to new sponsor + - Only the sponsee may submit this transaction. The old sponsor is not directly involved, and the new sponsor provides their signature via the standard signing flow. + ### 10.1. Fields | Field Name | Required? | JSON Type | Internal Type | Description | @@ -740,12 +749,20 @@ Additional failure conditions specific to `SponsorshipTransfer`: - `ObjectID` is specified but does not have a `Sponsor` field (object is not sponsored) (`tecNO_PERMISSION`) - `ObjectID` is not specified and the `tx.Account` does not have a `Sponsor` field (account is not sponsored) (`tecNO_PERMISSION`) - `tx.Account` is neither the current sponsor nor the owner (sponsee) of the object/account specified by `ObjectID` (`tecNO_PERMISSION`) +- If dissolving the sponsorship (no `Sponsor` field or `tfSponsorReserve` flag not set): + - The owner does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) +- If creating a new sponsorship (unsponsored to sponsored): + - The transaction is not submitted by the sponsee (`tecNO_PERMISSION`) + - The `Sponsor` field or the `SponsorFlags` field is missing (`temMALFORMED`) + - The `SponsorFlags` field does not include the `tfSponsorReserve` flag (`temINVALID_FLAG`) + - The new sponsor account does not exist (`terNO_ACCOUNT`) + - The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) - If transferring the sponsorship to a new sponsor: + - The transaction is not submitted by the sponsee (`tecNO_PERMISSION`) + - The `Sponsor` field or the `SponsorFlags` field is missing (`temMALFORMED`) + - The `SponsorFlags` field does not include the `tfSponsorReserve` flag (`temINVALID_FLAG`) - The new sponsor account does not exist (`terNO_ACCOUNT`) - The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) - - The `Sponsor` field is malformed or missing required subfields (`temMALFORMED`) -- If dissolving the sponsorship (no `Sponsor` field or `tfSponsorReserve` flag not set): - - The owner does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) ### 10.6. State Changes From c6bf4f2d1dbd28d82349274c0682cf13d73a7ca2 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 29 Jan 2026 12:16:31 -0500 Subject: [PATCH 41/53] clean up --- .../README.md | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 0185d52fc..698fbcf98 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -116,7 +116,7 @@ We propose this additional field: #### 4.2.1. Allowed Ledger Entry Types -The `Sponsor` field **MAY** appear on the following ledger entry types: +The `Sponsor` field **may** appear on the following ledger entry types: - `AccountRoot` (for account reserve sponsorship) - `Offer` @@ -134,9 +134,9 @@ The `Sponsor` field **MAY** appear on the following ledger entry types: - `DID` - Any other ledger entry type that contributes to an account's owner reserve, subject to implementation details -The `Sponsor` field **MUST NOT** appear on: +The `Sponsor` field **must not** appear on: -- `RippleState` objects, which instead use the `HighSponsor` and `LowSponsor` fields defined in section 7.1 +- `RippleState` objects (they instead use the `HighSponsor` and `LowSponsor` fields defined in section 7.1) - `DirectoryNode` objects (these do not have reserves) - `Amendments` objects (global ledger objects) - `FeeSettings` objects (global ledger objects) @@ -144,20 +144,20 @@ The `Sponsor` field **MUST NOT** appear on: #### 4.2.2. Presence Rules -- The field **MUST** be omitted when there is no sponsor; there is no sentinel value such as `0` or an empty string to indicate "no sponsor". -- When present, the field **MUST** contain a valid `AccountID` that exists on the ledger. +- The field **must** be omitted when there is no sponsor; there is no sentinel value such as `0` or an empty string to indicate "no sponsor". +- When present, the field **must** contain a valid `AccountID` that exists on the ledger. - The field is added when sponsorship is established (either at object creation or via `SponsorshipTransfer`). - The field is removed when sponsorship is dissolved via `SponsorshipTransfer`. #### 4.2.3. Constraints -- **Self-Sponsorship**: An account **MAY** sponsor its own objects. While this does not reduce the account's reserve requirement, it may be useful for accounting or organizational purposes. -- **Relationship to Ownership**: The `Sponsor` value **MAY** differ from the object's owner (as indicated by the `Owner` or `Account` field). This is the typical case for sponsorship. -- **Sponsor Account Requirements**: The sponsor account **MUST** have sufficient XRP to meet its reserve requirements, including reserves for all objects and accounts it sponsors. +- **Self-Sponsorship**: An account **may not** sponsor its own objects. +- **Relationship to Ownership**: The `Sponsor` value **must** differ from the object's owner (as indicated by the `Owner` or `Account` field). +- **Sponsor Account Requirements**: The sponsor account **must** have sufficient XRP to meet its reserve requirements, including reserves for all objects and accounts it sponsors. #### 4.2.4. Authoritative Indication -Implementations **MUST** treat the presence or absence of `Sponsor` as the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: +Implementations **must** treat the presence or absence of `Sponsor` as the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: - The object's reserve is counted against the sponsor's `SponsoringOwnerCount` (or `SponsoringAccountCount` for `AccountRoot`), not the owner's `OwnerCount`. - The owner's `SponsoredOwnerCount` is incremented (for non-`AccountRoot` objects). @@ -230,7 +230,7 @@ The reserve is charged to the sponsor's account (the `Owner` field). - The `SponsorshipSet` transaction must be submitted by the sponsor (the `Owner` of the `Sponsorship` object). - The `tfDeleteObject` flag must be enabled. - No other fields (`FeeAmount`, `MaxFee`, `ReserveCount`, or flag-setting fields) may be specified in the deletion transaction. -- **Note:** Non-zero `FeeAmount` and `ReserveCount` values **ARE** permitted at deletion time. Any remaining XRP in `FeeAmount` is returned to the sponsor's account upon deletion. +- **Note:** Non-zero `FeeAmount` and `ReserveCount` values **are** permitted at deletion time. Any remaining XRP in `FeeAmount` is returned to the sponsor's account upon deletion. #### 5.6.3. Account Deletion Blocker @@ -241,7 +241,7 @@ This object must be deleted before its owner account (the sponsor) can be delete 1. Delete the `Sponsorship` object via `SponsorshipSet` with `tfDeleteObject`, or 2. Wait for the sponsee to delete it (if the sponsee has permission to do so) -**Note on Existing Sponsored Objects:** Deleting a `Sponsorship` object does **not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `Sponsor` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. +**Note on Existing Sponsored Objects:** Deleting a `Sponsorship` object **does not** affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `Sponsor` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used. ### 5.7. Invariants @@ -426,7 +426,7 @@ These additional fields are necessary for a trustline since the reserve for this Existing invariants remain. -The common field `Sponsor` must not be on any `RippleState` objects (they must use `HighSponsor` and `LowSponsor` instead). +The common field `Sponsor` **must not** be on any `RippleState` objects (they must use `HighSponsor` and `LowSponsor` instead). ### 7.3. Example JSON @@ -507,19 +507,20 @@ Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the trans If the `SponsorSignature.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base\_fee$. -The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Sponsor.Signers|) * base\_fee$ (plus transaction-specific fees). +The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.SponsorSignature.Signers|) * base\_fee$ (plus transaction-specific fees). ### 8.3. Failure Conditions #### 8.3.1. General Failures -- `Sponsor.TxnSignature` is invalid. -- `Sponsor.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). +- `SponsorSignature.TxnSignature` is invalid. +- `SponsorSignature.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). - The `Sponsor` doesn't exist on the ledger. - An invalid sponsorship flag is used. - `SponsorSignature.SigningPubKey`, `SponsorSignature.TxnSignature`, and `SponsorSignature.Signers` are all included (or other incorrect combinations of signing fields). -- `Sponsor` or `SponsorSignature` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). -- Only one of `Sponsor` and `SponsorSignature` is included (they must either both be included, if the transaction is sponsored, or neither, if it is not). +- `Sponsor`, `SponsorFlags`, or `SponsorSignature` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). +- Only one or two of `Sponsor`, `SponsorFlags`, and `SponsorSignature` is included (they must either all be included, if the transaction is sponsored, or none, if it is not). +- `SponsorFlags` includes invalid flags (currently, the only two valid flags are `tfSponsorFee` and `tfSponsorReserve`). #### 8.3.2. Fee Sponsorship Failures @@ -596,7 +597,7 @@ This transaction creates and updates the `Sponsorship` object. | `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipSet`). | | `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the sponsor or the sponsee. | | `Sponsor` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | -| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account`, is assumed to be the `Sponsor`. | +| `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account` is assumed to be the `Sponsor`. | | `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. This value will replace what is currently in the `Sponsorship.FeeAmount` field (if it exists). | | `MaxFee` | | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | | `ReserveCount` | | `number` | `UInt32` | The (remaining) amount of reserves that the sponsor has provided for the sponsee to use. This value will replace what is currently in the `Sponsorship.ReserveCount` field (if it exists). | @@ -622,7 +623,7 @@ This transaction uses the standard transaction fee (currently 10 drops, subject - `tx.Account` is not equal to either `tx.Sponsor` or `tx.Sponsee` (`temMALFORMED`) - Both `Sponsor` and `Sponsee` are specified (`temMALFORMED`) - Neither `Sponsor` nor `Sponsee` is specified (`temMALFORMED`) -- `Sponsor` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled (`temMALFORMED` - only sponsor can create/update) +- `Sponsor` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled, as only the sponsor can create/update the `Sponsorship` object (`temMALFORMED`) - `MaxFee` is less than the base fee or is not denominated in XRP (`temBAD_AMOUNT`) - `FeeAmount` is not denominated in XRP (`temBAD_AMOUNT`) - `Sponsor` or `Sponsee` does not exist on the ledger (`terNO_ACCOUNT`) @@ -715,7 +716,7 @@ If it is not included, then it refers to the account sending the transaction. The `Sponsor` field is already added in the transaction common fields (see section [6.1.1](#611-sponsor)), but it has some additional rules associated with it on the `SponsorshipTransfer` transaction. -In this case, if `Sponsor` is included with the `tfSponsorReserve` flag, then the reserve sponsorship for the provided object will be transferred to the `Sponsor.Account` instead of passing back to the ledger object's owner. +In this case, if `Sponsor` is included with the `tfSponsorReserve` flag, then the reserve sponsorship for the provided object will be transferred to the `Sponsor` instead of passing back to the ledger object's owner. If there is no `Sponsor` field, or if the `tfSponsorReserve` flag is not included, then the burden of the reserve will be passed back to the ledger object's owner (the former sponsee). @@ -766,8 +767,8 @@ Additional failure conditions specific to `SponsorshipTransfer`: ### 10.6. State Changes -- The `Sponsor` field on the object is deleted if the `tx.Sponsor.Account` is the object's `Owner`, otherwise the `Sponsor` field is updated to the new `tx.Sponsor.Account`. -- The old sponsor has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. +- The `Sponsor` field on the object specified by `ObjectID` is deleted if the `tx.Sponsor` is the object's `Owner`, otherwise the `Sponsor` field is updated to the new `tx.Sponsor`. +- The old sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. - The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. - If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. @@ -900,7 +901,7 @@ This permission affects all transaction types that support the `Sponsor` field ( When a transaction includes a `Sponsor` field with the `tfSponsorFee` flag enabled, the sponsor must have granted the `SponsorFee` permission to the transaction submitter (the `tx.Account`), unless: -- The sponsor is signing the transaction directly (via `Sponsor.SigningPubKey` and `Sponsor.TxnSignature` or `Sponsor.Signers`), OR +- The sponsor is signing the transaction directly (via `SponsorSignature.SigningPubKey` and `SponsorSignature.TxnSignature` or `SponsorSignature.Signers`), OR - A `Sponsorship` object exists between the sponsor and sponsee with sufficient `FeeAmount` ### 13.3. Permission Value @@ -917,9 +918,9 @@ The `SponsorFee` permission allows an account to delegate its ability to sponsor This permission affects all transaction types that support the `Sponsor` field (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)) and create new ledger objects or accounts. -When a transaction includes a `Sponsor` field with the `tfSponsorReserve` flag enabled, the sponsor must have granted the `SponsorReserve` permission to the transaction submitter (the `tx.Account`), unless: +When a transaction includes a `SponsorFlags` field with the `tfSponsorReserve` flag enabled, the sponsor must have granted the `SponsorReserve` permission to the transaction submitter (the `tx.Account`), unless: -- The sponsor is signing the transaction directly (via `Sponsor.SigningPubKey` and `Sponsor.TxnSignature` or `Sponsor.Signers`), OR +- The sponsor is signing the transaction directly (via `SponsorSignature.SigningPubKey` and `SponsorSignature.TxnSignature` or `SponsorSignature.Signers`), OR - A `Sponsorship` object exists between the sponsor and sponsee with sufficient `ReserveCount` ### 14.3. Permission Value From 726b426ad3f9ef89fc741909308482468fdbf261 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 30 Jan 2026 12:06:56 -0500 Subject: [PATCH 42/53] add pubkey validation --- XLS-0068-sponsored-fees-and-reserves/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 698fbcf98..03a4f77df 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -514,7 +514,8 @@ The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Spo #### 8.3.1. General Failures - `SponsorSignature.TxnSignature` is invalid. -- `SponsorSignature.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, or signature(s) are invalid). +- `SponsorSignature.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, the public key(s) are invalid, or signature(s) are invalid). +- `SponsorSignature.SigningPubKey` is invalid (the public key doesn't match the account's master key or regular key, or the public key is otherwise invalid). - The `Sponsor` doesn't exist on the ledger. - An invalid sponsorship flag is used. - `SponsorSignature.SigningPubKey`, `SponsorSignature.TxnSignature`, and `SponsorSignature.Signers` are all included (or other incorrect combinations of signing fields). From 0afad892d5e849883e1046e75d6dab302d2e4bae Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 30 Jan 2026 12:34:18 -0500 Subject: [PATCH 43/53] respond to comments --- XLS-0068-sponsored-fees-and-reserves/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 03a4f77df..d6ad907c7 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -144,16 +144,16 @@ The `Sponsor` field **must not** appear on: #### 4.2.2. Presence Rules -- The field **must** be omitted when there is no sponsor; there is no sentinel value such as `0` or an empty string to indicate "no sponsor". +- The field **must** be omitted when there is no sponsor. - When present, the field **must** contain a valid `AccountID` that exists on the ledger. - The field is added when sponsorship is established (either at object creation or via `SponsorshipTransfer`). - The field is removed when sponsorship is dissolved via `SponsorshipTransfer`. +- The `Sponsor` must have a `SponsoringOwnerCount` that is greater than 0. #### 4.2.3. Constraints -- **Self-Sponsorship**: An account **may not** sponsor its own objects. -- **Relationship to Ownership**: The `Sponsor` value **must** differ from the object's owner (as indicated by the `Owner` or `Account` field). -- **Sponsor Account Requirements**: The sponsor account **must** have sufficient XRP to meet its reserve requirements, including reserves for all objects and accounts it sponsors. +- Self-Sponsorship: An account **may not** sponsor its own objects. The `Sponsor` value **must** differ from the object's owner (as indicated by the `Owner` or `Account` field). +- Sponsor Account Requirements: The sponsor account **must** have sufficient XRP to meet its reserve requirements, including reserves for all objects and accounts it sponsors. #### 4.2.4. Authoritative Indication From c6757868a2e87324d389d128c086b82b6830b565 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 30 Jan 2026 12:42:22 -0500 Subject: [PATCH 44/53] respond to comments --- XLS-0068-sponsored-fees-and-reserves/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index d6ad907c7..4b9b39f61 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -142,27 +142,27 @@ The `Sponsor` field **must not** appear on: - `FeeSettings` objects (global ledger objects) - `NegativeUNL` objects (global ledger objects) -#### 4.2.2. Presence Rules +#### 4.2.2. Constraints - The field **must** be omitted when there is no sponsor. - When present, the field **must** contain a valid `AccountID` that exists on the ledger. - The field is added when sponsorship is established (either at object creation or via `SponsorshipTransfer`). - The field is removed when sponsorship is dissolved via `SponsorshipTransfer`. - The `Sponsor` must have a `SponsoringOwnerCount` that is greater than 0. - -#### 4.2.3. Constraints - -- Self-Sponsorship: An account **may not** sponsor its own objects. The `Sponsor` value **must** differ from the object's owner (as indicated by the `Owner` or `Account` field). -- Sponsor Account Requirements: The sponsor account **must** have sufficient XRP to meet its reserve requirements, including reserves for all objects and accounts it sponsors. +- The `Sponsor` value **must** differ from the object's owner (as indicated by the `Owner` or `Account` field). An account **may not** sponsor its own objects. +- The sponsor account **must** have sufficient XRP to meet its reserve requirements when creating/sponsoring new objects, including reserves for all objects and accounts it sponsors. #### 4.2.4. Authoritative Indication -Implementations **must** treat the presence or absence of `Sponsor` as the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: +The presence or absence of `Sponsor` is the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: - The object's reserve is counted against the sponsor's `SponsoringOwnerCount` (or `SponsoringAccountCount` for `AccountRoot`), not the owner's `OwnerCount`. +- The sponsor's `SponsoringOwnerCount` (or `SponsoringAccountCount` for `AccountRoot`) is incremented. - The owner's `SponsoredOwnerCount` is incremented (for non-`AccountRoot` objects). - The reserve calculation for both sponsor and owner accounts is adjusted accordingly. +_NOTE: These values are likely not recalculated later due to performance issues, and the implementation should make sure that the `SponsoringOwnerCount`, `SponsoredOwnerCount`, and `SponsoringAccountCount` fields in all accounts are updated appropriately. The accuracy will only be verifiable via off-chain mechanisms._ + ## 5. Ledger Entry: `Sponsorship` `Sponsorship` is an object that reflects a sponsoring relationship between two accounts, `Sponsor` and `Sponsee`. This allows sponsors to "pre-fund" sponsees, if they so desire. From 90f9dce26c41b851a8d192bc47caec0d9e03e619 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 30 Jan 2026 13:52:28 -0500 Subject: [PATCH 45/53] respond to comments --- XLS-0068-sponsored-fees-and-reserves/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 4b9b39f61..8a22d5a5f 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -190,6 +190,7 @@ This ensures that there can be at most one `Sponsorship` object per sponsor-spon | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | | ------------------- | --------- | --------- | ------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The value `"Sponsorship"` (JSON) or a unique numeric value (internal, assigned during implementation) indicates this is a `Sponsorship` ledger object. | +| `Flags` | | | `0` | `number` | `UInt32` | A bit-map of boolean flags enabled for this object. | | `Owner` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | | `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | | `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | @@ -204,10 +205,10 @@ This ensures that there can be at most one `Sponsorship` object per sponsor-spon There are two flags on this object: -| Flag Name | Flag Value | Modifiable? | Description | -| ------------------------------------- | ------------ | ----------- | ----------------------------------------------------------------------------------------------------------- | -| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | -| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. | +| Flag Name | Flag Value | Modifiable? | Description | +| ------------------------------------- | ------------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lsfSponsorshipRequireSignForFee` | `0x00010000` | Yes | If set, indicates that every use of this sponsor for sponsoring fees requires a signature from the sponsor. If unset, no signature is necessary (the existence of the `Sponsorship` object is sufficient). | +| `lsfSponsorshipRequireSignForReserve` | `0x00020000` | Yes | If set, indicates that every use of this sponsor for sponsoring reserves requires a signature from the sponsor. If unset, no signature is necessary (the existence of the `Sponsorship` object is sufficient). | ### 5.4. Ownership @@ -355,6 +356,7 @@ The following invariants must hold for `AccountRoot` objects with sponsorship fi - `SponsoredOwnerCount <= OwnerCount` (cannot have more sponsored objects than total owned objects) - If `Sponsor` is present, it must be a valid `AccountID` that exists on the ledger - The reserve calculation must always result in a non-negative value +- The `Sponsor`'s `SponsoringAccountCount` must be greater than 0 **Global Invariant (referenced in section 18.2):** From bf9eba0eef1b5d595a3ac32254bd0fe81b1c3724 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 30 Jan 2026 18:12:52 -0500 Subject: [PATCH 46/53] more fixes --- XLS-0068-sponsored-fees-and-reserves/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 8a22d5a5f..e90417ab4 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -8,7 +8,7 @@ proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 requires: 74 created: 2024-05-02 - updated: 2025-01-06 + updated: 2026-01-30 # Sponsored Fees and Reserves @@ -152,6 +152,8 @@ The `Sponsor` field **must not** appear on: - The `Sponsor` value **must** differ from the object's owner (as indicated by the `Owner` or `Account` field). An account **may not** sponsor its own objects. - The sponsor account **must** have sufficient XRP to meet its reserve requirements when creating/sponsoring new objects, including reserves for all objects and accounts it sponsors. +_NOTE: A sponsor may also be a sponsee._ + #### 4.2.4. Authoritative Indication The presence or absence of `Sponsor` is the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: @@ -591,7 +593,7 @@ The `SponsoredOwnerCount`, `SponsoringOwnerCount`, and `SponsoringAccountCount` ## 9. Transaction: `SponsorshipSet` -This transaction creates and updates the `Sponsorship` object. +This transaction creates, updates, and deletes the `Sponsorship` object. ### 9.1. Fields @@ -670,7 +672,7 @@ If deleting the `Sponsorship` object (`tfDeleteObject` flag): _Note: Deleting a `Sponsorship` object does not affect already-sponsored ledger entries or accounts. Those existing sponsored objects/accounts will retain their `Sponsor` field and continue to be sponsored. To dissolve sponsorship for existing objects, the `SponsorshipTransfer` transaction must be used._ -### 9.5. Example JSON +### 9.6. Example JSON ```json { From 11372cda5e800539baf384bc111f5c8de60ec952 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 3 Feb 2026 15:49:37 -0500 Subject: [PATCH 47/53] respond to comments --- XLS-0068-sponsored-fees-and-reserves/README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 3cb3180ab..fb71a8b1d 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -195,7 +195,7 @@ This ensures that there can be at most one `Sponsorship` object per sponsor-spon | `Flags` | | | `0` | `number` | `UInt32` | A bit-map of boolean flags enabled for this object. | | `Owner` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. | | `Sponsee` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The sponsee associated with this relationship. | -| `FeeAmount` | | | `0` | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | +| `FeeAmount` | | | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. | | `MaxFee` | | | N/A | `string` | `Amount` | The maximum fee per transaction that will be sponsored. This is to prevent abuse/excessive draining of the sponsored fee pool. | | `ReserveCount` | | | `0` | `string` | `UInt32` | The (remaining) number of `OwnerCount` that the sponsor has provided for the sponsee to use for reserves. | | `OwnerNode` | ✔️ | ✔️ | N/A | `string` | `UInt64` | A hint indicating which page of the sponsor's owner directory links to this object, in case the directory consists of multiple pages. | @@ -256,6 +256,8 @@ The following invariants must always hold for a `Sponsorship` object: - At least one of `FeeAmount` and `ReserveCount` must be included - Both `Owner` and `Sponsee` must be valid `AccountID` values that exist on the ledger +_NOTE: The invariants in [4.2](#42-invariant-checks) also apply to `Sponsorship` objects, as they apply to all objects._ + ### 5.8. RPC Name The `snake_case` form of the ledger object name is `sponsorship`. @@ -432,6 +434,8 @@ Existing invariants remain. The common field `Sponsor` **must not** be on any `RippleState` objects (they must use `HighSponsor` and `LowSponsor` instead). +_NOTE: The invariants in [4.2](#42-invariant-checks) also apply to `RippleState` objects, as they apply to all objects._ + ### 7.3. Example JSON ```json @@ -480,7 +484,7 @@ We propose these modifications: ##### 8.1.1. `SponsorFlags` -The `SponsorFlags` field allows the user to specify which sponsorship type(s) they wish to participate in. This field **must** be included if the `Sponsor` field is included in a transaction, and at least one flag **must** be specified if the `Sponsor` field is included in a transaction. +The `SponsorFlags` field allows the user to specify which sponsorship type(s) they wish to participate in. This field **must** be included if the `Sponsor` field is included in a transaction, and at least one flag **must** be specified if the `Sponsor` field is included in a transaction. The `SponsorFlags` field **must not** be included if the `Sponsor` field is not included in a transaction. There are two flag values that are supported: @@ -509,9 +513,9 @@ Either `SigningPubKey`+`TxnSignature` or `Signers` must be included in the trans ### 8.2. Transaction Fee -If the `SponsorSignature.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base\_fee$. +If the `SponsorSignature.Signers` field is necessary, then the total fee of the transaction will be increased, due to the extra signatures that need to be processed. This is similar to the additional fees for [multisigning](https://xrpl.org/docs/concepts/accounts/multi-signing/). The minimum fee will be $(|signatures|+1)*base\textunderscore fee$. -The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.SponsorSignature.Signers|) * base\_fee$ (plus transaction-specific fees). +The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.SponsorSignature.Signers|) * base\textunderscore fee$ (plus transaction-specific fees). ### 8.3. Failure Conditions From e70455e7c0600f05c8c2ee6793eb9222bbafb0ea Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 4 Feb 2026 15:11:39 -0500 Subject: [PATCH 48/53] respond to comments --- .../README.md | 83 +++++++++++++------ 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index fb71a8b1d..8b8d29cb5 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -97,6 +97,8 @@ There are two ways in which he could do this: ## 4. Ledger Entries: Common Fields +This section describes the changes to the common fields of ledger entries, to indicate whether or not they are sponsored, and if so, who the sponsor is. + ### 4.1. Fields As a reference, here are the fields that all ledger objects currently have: @@ -106,15 +108,19 @@ As a reference, here are the fields that all ledger objects currently have: | `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The type of ledger entry. | | `Flags` | ✔️ | ✔️ | N/A | `number` | `UInt16` | Set of bit-flags for this ledger entry. | -We propose this additional field: +This spec proposes one additional field: | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | | ---------- | --------- | --------- | ------------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Sponsor` | | | N/A | `string` | `AccountID` | The sponsor paying the owner reserve for a given ledger object. When present, it indicates that the reserve burden for that object has been shifted from the owner to the sponsor. | -### 4.2. Invariant Checks +### 4.2. Ownership + +Any sponsored object is still owned by the original owner, and the sponsor is not added as an additional owner. + +### 4.3. Invariant Checks -#### 4.2.1. Allowed Ledger Entry Types +#### 4.3.1. Allowed Ledger Entry Types The `Sponsor` field **may** appear on the following ledger entry types: @@ -142,7 +148,7 @@ The `Sponsor` field **must not** appear on: - `FeeSettings` objects (global ledger objects) - `NegativeUNL` objects (global ledger objects) -#### 4.2.2. Constraints +#### 4.3.2. Constraints - The field **must** be omitted when there is no sponsor. - When present, the field **must** contain a valid `AccountID` that exists on the ledger. @@ -154,7 +160,7 @@ The `Sponsor` field **must not** appear on: _NOTE: A sponsor may also be a sponsee._ -#### 4.2.4. Authoritative Indication +#### 4.3.4. Authoritative Indication The presence or absence of `Sponsor` is the authoritative indication of whether an object is sponsored for reserves. The presence of this field triggers the following behaviors: @@ -256,7 +262,7 @@ The following invariants must always hold for a `Sponsorship` object: - At least one of `FeeAmount` and `ReserveCount` must be included - Both `Owner` and `Sponsee` must be valid `AccountID` values that exist on the ledger -_NOTE: The invariants in [4.2](#42-invariant-checks) also apply to `Sponsorship` objects, as they apply to all objects._ +_NOTE: The invariants in [4.3](#43-invariant-checks) also apply to `Sponsorship` objects, as they apply to all objects._ ### 5.8. RPC Name @@ -331,7 +337,7 @@ This spec proposes these additional fields: #### 6.1.1. `Sponsor` -The `Sponsor` field is already added in the ledger common fields (see section [4.2](#42-sponsoraccount)), but it has some additional rules associated with it on the `AccountRoot` object. +The `Sponsor` field is already added in the ledger common fields (see section [4.1](#41-fields)), but it has some additional rules associated with it on the `AccountRoot` object. This field is included if the account was created with a sponsor paying its account reserve. If this sponsored account is deleted, the destination of the `AccountDelete` transaction must equal `Sponsor`, so that the sponsor can recoup their fees. @@ -434,7 +440,7 @@ Existing invariants remain. The common field `Sponsor` **must not** be on any `RippleState` objects (they must use `HighSponsor` and `LowSponsor` instead). -_NOTE: The invariants in [4.2](#42-invariant-checks) also apply to `RippleState` objects, as they apply to all objects._ +_NOTE: The invariants in [4.3](#43-invariant-checks) also apply to `RippleState` objects, as they apply to all objects._ ### 7.3. Example JSON @@ -478,7 +484,7 @@ We propose these modifications: | Field Name | Required? | JSON Type | Internal Type | Description | | ------------------ | --------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Sponsor` | | `string` | `STAccount` | The sponsoring account. | +| `Sponsor` | | `string` | `AccountID` | The sponsoring account. | | `SponsorFlags` | | `number` | `UInt32` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | | `SponsorSignature` | | `object` | `STObject` | This field contains all the signing information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | @@ -605,6 +611,7 @@ This transaction creates, updates, and deletes the `Sponsorship` object. | ----------------- | --------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipSet`). | | `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the sponsor or the sponsee. | +| `Flags` | | `number` | `UInt32` | A bit-map of boolean flags enabled for this transaction. The flags are defined in [section 9.2](#92-flags). | | `Sponsor` | | `string` | `AccountID` | The sponsor associated with this relationship. This account also pays for the reserve of this object. If this field is included, the `Account` is assumed to be the `Sponsee`. | | `Sponsee` | | `string` | `AccountID` | The sponsee associated with this relationship. If this field is included, the `Account` is assumed to be the `Sponsor`. | | `FeeAmount` | | `string` | `Amount` | The (remaining) amount of XRP that the sponsor has provided for the sponsee to use for fees. This value will replace what is currently in the `Sponsorship.FeeAmount` field (if it exists). | @@ -634,6 +641,7 @@ This transaction uses the standard transaction fee (currently 10 drops, subject - Neither `Sponsor` nor `Sponsee` is specified (`temMALFORMED`) - `Sponsor` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled, as only the sponsor can create/update the `Sponsorship` object (`temMALFORMED`) - `MaxFee` is less than the base fee or is not denominated in XRP (`temBAD_AMOUNT`) +- `MaxFee` is greater than `FeeAmount` (`temBAD_AMOUNT`) - `FeeAmount` is not denominated in XRP (`temBAD_AMOUNT`) - `Sponsor` or `Sponsee` does not exist on the ledger (`terNO_ACCOUNT`) - `Owner == Sponsee` (attempting to create self-sponsorship) (`temMALFORMED`) @@ -693,7 +701,7 @@ _Note: Deleting a `Sponsorship` object does not affect already-sponsored ledger ## 10. Transaction: `SponsorshipTransfer` -This transaction transfers a sponsor relationship for a particular ledger object's object reserve. The sponsor relationship can either be passed on to a new sponsor, or dissolved entirely (with the sponsee taking on the reserve). Either the sponsor or sponsee may submit this transaction at any point in time. +This transaction transfers a sponsor relationship for a particular ledger object's reserve. The sponsor relationship can either be passed on to a new sponsor, or dissolved entirely (with the sponsee taking on the reserve). Either the sponsor or sponsee may submit this transaction at any point in time. There are three valid transfer scenarios: @@ -711,8 +719,8 @@ There are three valid transfer scenarios: | `TransactionType` | ✔️ | `string` | `UInt16` | The transaction type (`SponsorshipTransfer`). | | `Account` | ✔️ | `string` | `AccountID` | The account sending the transaction. This may be either the current sponsor or the current sponsee. | | `ObjectID` | | `string` | `Hash256` | The ID of the object to transfer sponsorship. | -| `Sponsor` | | `string` | `STAccount` | The new sponsor of the object. | -| `SponsorFlags` | | `number` | `UInt32` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). The `tfSponsorReserve` flag **must** be included. | +| `Sponsor` | | `string` | `AccountID` | The new sponsor of the object. | +| `SponsorFlags` | | `number` | `UInt32` | Flags on the sponsorship, indicating what type of sponsorship this is (fee vs. reserve). | | `SponsorSignature` | | `object` | `STObject` | This field contains all the signing information for the sponsorship happening in the transaction. It is included if the transaction is fee- and/or reserve-sponsored. | #### 10.1.1. `ObjectID` @@ -729,27 +737,54 @@ In this case, if `Sponsor` is included with the `tfSponsorReserve` flag, then th If there is no `Sponsor` field, or if the `tfSponsorReserve` flag is not included, then the burden of the reserve will be passed back to the ledger object's owner (the former sponsee). -### 10.2. Ending the Sponsorship for a Sponsored Ledger Object +### 10.2. Sponsorship Transfer Scenarios + +#### 10.2.1. Transferring from Sponsor to Sponsee (Sponsored to Unsponsored) + +This scenario ends the sponsorship for a sponsored ledger object or account. The sponsor and sponsee both have the right to end the relationship at any time. + +The following fields indicate this scenario: + +- `ObjectID` must be included (if sponsored object) +- `Sponsor` must be excluded +- `SponsorFlags.tfSponsorReserve` must be excluded +- The object specified by `ObjectID` must be have a `Sponsor` field + +#### 10.2.2. Transferring from Sponsee to Sponsor (Unsponsored to Sponsored) + +This scenario sponsors an object or account that was not previously sponsored. Only the sponsee can submit this transaction. + +The following fields indicate this scenario: + +- `ObjectID` must be included (if sponsored object) +- `Sponsor` must be included +- `SponsorFlags.tfSponsorReserve` must be included +- The object specified by `ObjectID` must **not** have a `Sponsor` field + +#### 10.2.3. Transferring from Sponsor to New Sponsor -A sponsored ledger object will have the `Sponsor` field attached to it. Ending the sponsor relationship for a sponsored ledger object requires the `ObjectID` parameter, to specify which ledger object. +This scenario migrates the sponsorship for a sponsored object or account to a new sponsor. Only the sponsee can submit this transaction. -Two accounts are allowed to submit a `SponsorshipTransfer` relationship to end the sponsor relationship for a sponsored ledger object: either the sponsor for that object or the owner of that object (the sponsee). +The following fields indicate this scenario: -### 10.3. Migrating a Sponsorship to a New Account +- `ObjectID` must be included (if sponsored object) +- `Sponsor` must be included +- `SponsorFlags.tfSponsorReserve` must be included +- The object specified by `ObjectID` must have a `Sponsor` field -A sponsorship can be migrated to a new account by including the `Sponsor` field with the `tfSponsorReserve` flag. This can be done for either a sponsored account or a sponsored ledger object. +_NOTE: The only difference between this scenario and the one specified in [10.2.2](#1022-transferring-from-sponsee-to-sponsor-unsponsored-to-sponsored) is that in this case, the object specified by `ObjectID` must already have a `Sponsor` field._ -Two accounts are allowed to submit a `SponsorshipTransfer` relationship to migrate the sponsor relationship: the sponsor or the sponsee. +#### 10.2.4. Sponsorship Transfer for Accounts -The sponsor will likely only rarely want to do this (such as if they are transferring accounts), but the sponsee may want to migrate if they change providers. +The same 3 scenarios above apply to accounts as well. The only difference is that for accounts, the `ObjectID` field is not included, and instead the `Account` field is used to specify which account the sponsorship is changing for. -### 10.4. Transaction Fee +### 10.3. Transaction Fee **Fee Structure:** Standard This transaction uses the standard transaction fee (currently 10 drops, subject to Fee Voting changes). -### 10.5. Failure Conditions +### 10.4. Failure Conditions All failure conditions mentioned in [section 8.3](#83-failure-conditions) still apply here. @@ -774,14 +809,14 @@ Additional failure conditions specific to `SponsorshipTransfer`: - The new sponsor account does not exist (`terNO_ACCOUNT`) - The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) -### 10.6. State Changes +### 10.5. State Changes - The `Sponsor` field on the object specified by `ObjectID` is deleted if the `tx.Sponsor` is the object's `Owner`, otherwise the `Sponsor` field is updated to the new `tx.Sponsor`. - The old sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` decremented by one. - The new sponsor (if applicable) has its `SponsoringOwnerCount`/`SponsoringAccountCount` incremented by one. - If there is no new sponsor, then the owner's `SponsoredOwnerCount` will be decremented by one. -### 10.7. Example JSON +### 10.6. Example JSON ```json { @@ -1350,7 +1385,7 @@ It would look something like this: | Field Name | Required | JSON Type | Internal Type | Description | | ----------------- | -------- | --------- | ------------- | ------------------------------------------------------------------------ | | `TransactionType` | Yes | `string` | `UInt16` | The transaction type (`Relay`). | -| `Account` | Yes | `string` | `STAccount` | The sponsor of the transaction. | +| `Account` | Yes | `string` | `AccountID` | The sponsor of the transaction. | | `Transaction` | Yes | `object` | `STTx` | The sponsee's transaction. | | `Fee` | Yes | `string` | `STAmount` | The fee for the transaction. This should match the fee in `Transaction`. | From 4c7b5852001233fd7fbb48801ed59bb13b029f61 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 5 Feb 2026 17:27:59 -0500 Subject: [PATCH 49/53] more invariants --- XLS-0068-sponsored-fees-and-reserves/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 8b8d29cb5..111b51082 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -438,7 +438,9 @@ These additional fields are necessary for a trustline since the reserve for this Existing invariants remain. -The common field `Sponsor` **must not** be on any `RippleState` objects (they must use `HighSponsor` and `LowSponsor` instead). +- The common field `Sponsor` **must not** be on any `RippleState` objects (they must use `HighSponsor` and `LowSponsor` instead). +- If `LowSponsor` is included, `lsfLowReserve` **must** be enabled. +- If `HighSponsor` is included, `lsfHighReserve` **must** be enabled. _NOTE: The invariants in [4.3](#43-invariant-checks) also apply to `RippleState` objects, as they apply to all objects._ From b06ec4bd50518335e0e100a4329545d18f80e27f Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 6 Feb 2026 13:53:46 -0500 Subject: [PATCH 50/53] add note --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index 111b51082..f4b8a2309 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -1057,6 +1057,8 @@ There are no additional failure conditions. The `account_sponsoring` RPC method is used to fetch a list of objects that an account is sponsoring; namely, a list of objects where the `Sponsor` is the given account. It has a very similar API to the [`account_objects` method](https://xrpl.org/account_objects.html). +_[NOTE: This API will not be implemented in rippled, but will instead be implemented in Clio. This is due to the fact that this API would likely require another database to keep track of the sponsorship relationships, which would be too expensive to maintain in rippled.]_ + ### 16.1. Request Fields | Field Name | Required? | JSON Type | Description | From 61106154c07b4a5fb7e22275474cfd75cc62d89b Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 11 Feb 2026 12:23:24 -0500 Subject: [PATCH 51/53] number failure conditions --- .../README.md | 142 +++++++++--------- 1 file changed, 72 insertions(+), 70 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index f4b8a2309..a325cc408 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -529,46 +529,46 @@ The total fee calculation for signatures will now be $( 1+|tx.Signers| + |tx.Spo #### 8.3.1. General Failures -- `SponsorSignature.TxnSignature` is invalid. -- `SponsorSignature.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, the public key(s) are invalid, or signature(s) are invalid). -- `SponsorSignature.SigningPubKey` is invalid (the public key doesn't match the account's master key or regular key, or the public key is otherwise invalid). -- The `Sponsor` doesn't exist on the ledger. -- An invalid sponsorship flag is used. -- `SponsorSignature.SigningPubKey`, `SponsorSignature.TxnSignature`, and `SponsorSignature.Signers` are all included (or other incorrect combinations of signing fields). -- `Sponsor`, `SponsorFlags`, or `SponsorSignature` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). -- Only one or two of `Sponsor`, `SponsorFlags`, and `SponsorSignature` is included (they must either all be included, if the transaction is sponsored, or none, if it is not). -- `SponsorFlags` includes invalid flags (currently, the only two valid flags are `tfSponsorFee` and `tfSponsorReserve`). +1. `SponsorSignature.TxnSignature` is invalid. +1. `SponsorSignature.Signers` is invalid (the signer list isn't on the account, quorum isn't reached, the public key(s) are invalid, or signature(s) are invalid). +1. `SponsorSignature.SigningPubKey` is invalid (the public key doesn't match the account's master key or regular key, or the public key is otherwise invalid). +1. The `Sponsor` doesn't exist on the ledger. +1. An invalid sponsorship flag is used. +1. `SponsorSignature.SigningPubKey`, `SponsorSignature.TxnSignature`, and `SponsorSignature.Signers` are all included (or other incorrect combinations of signing fields). +1. `Sponsor`, `SponsorFlags`, or `SponsorSignature` is included in a transaction that does not support sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)). +1. Only one or two of `Sponsor`, `SponsorFlags`, and `SponsorSignature` is included (they must either all be included, if the transaction is sponsored, or none, if it is not). +1. `SponsorFlags` includes invalid flags (currently, the only two valid flags are `tfSponsorFee` and `tfSponsorReserve`). #### 8.3.2. Fee Sponsorship Failures -- The sponsor's account does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) +1. The sponsor's account does not have enough XRP to cover the sponsored transaction fee (`telINSUF_FEE_P`) If a `Sponsorship` object exists: -- The `lsfRequireSignatureForFee` flag is enabled and there is no sponsor signature included. -- There is not enough XRP in the `FeeAmount` to pay for the transaction. - - Paying fees via sponsorship will _not_ be able to [go below the reserve requirement](https://xrpl.org/docs/concepts/accounts/reserves#going-below-the-reserve-requirement). -- The fee in `tx.Fee` is greater than `Sponsorship.MaxFee` +1. The `lsfRequireSignatureForFee` flag is enabled and there is no sponsor signature included. +1. There is not enough XRP in the `FeeAmount` to pay for the transaction. +1. Paying fees via sponsorship will _not_ be able to [go below the reserve requirement](https://xrpl.org/docs/concepts/accounts/reserves#going-below-the-reserve-requirement). +1. The fee in `tx.Fee` is greater than `Sponsorship.MaxFee` If a `Sponsorship` object does not exist: -- There is no sponsor signature included. +1. There is no sponsor signature included. Note: if a transaction doesn't charge a fee (such as an account's first `SetRegularKey` transaction), the transaction will still succeed. #### 8.3.3. Reserve Sponsorship Failures -- The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) -- The transaction does not support reserve sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)) +1. The sponsor does not have enough XRP to cover the reserve (`tecINSUFFICIENT_RESERVE`) +1. The transaction does not support reserve sponsorship (see section [8.3.4](#834-transactions-that-cannot-be-sponsored)) If a `Sponsorship` object exists: -- The `lsfRequireSignatureForReserve` flag is enabled and there is no sponsor signature included. -- There is not enough remaining count in the `ReserveCount` to pay for the transaction. +1. The `lsfRequireSignatureForReserve` flag is enabled and there is no sponsor signature included. +1. There is not enough remaining count in the `ReserveCount` to pay for the transaction. If a `Sponsorship` object does not exist: -- There is no sponsor signature included. +1. There is no sponsor signature included. Note: if a transaction doesn't charge a reserve (such as `AccountSet`), the transaction will still succeed. @@ -638,25 +638,25 @@ This transaction uses the standard transaction fee (currently 10 drops, subject ### 9.4. Failure Conditions -- `tx.Account` is not equal to either `tx.Sponsor` or `tx.Sponsee` (`temMALFORMED`) -- Both `Sponsor` and `Sponsee` are specified (`temMALFORMED`) -- Neither `Sponsor` nor `Sponsee` is specified (`temMALFORMED`) -- `Sponsor` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled, as only the sponsor can create/update the `Sponsorship` object (`temMALFORMED`) -- `MaxFee` is less than the base fee or is not denominated in XRP (`temBAD_AMOUNT`) -- `MaxFee` is greater than `FeeAmount` (`temBAD_AMOUNT`) -- `FeeAmount` is not denominated in XRP (`temBAD_AMOUNT`) -- `Sponsor` or `Sponsee` does not exist on the ledger (`terNO_ACCOUNT`) -- `Owner == Sponsee` (attempting to create self-sponsorship) (`temMALFORMED`) -- Sponsor does not have sufficient XRP to cover the reserve for the `Sponsorship` object (`tecINSUFFICIENT_RESERVE`) -- If `tfDeleteObject` is enabled: - - `FeeAmount` is specified (`temMALFORMED`) - - `MaxFee` is specified (`temMALFORMED`) - - `ReserveCount` is specified (`temMALFORMED`) - - `tfSponsorshipSetRequireSignForFee` is enabled (`temINVALID_FLAG`) - - `tfSponsorshipSetRequireSignForReserve` is enabled (`temINVALID_FLAG`) - - `tfSponsorshipClearRequireSignForFee` is enabled (`temINVALID_FLAG`) - - `tfSponsorshipClearRequireSignForReserve` is enabled (`temINVALID_FLAG`) - - The `Sponsorship` object does not exist (`tecNO_ENTRY`) +1. `tx.Account` is not equal to either `tx.Sponsor` or `tx.Sponsee` (`temMALFORMED`) +1. Both `Sponsor` and `Sponsee` are specified (`temMALFORMED`) +1. Neither `Sponsor` nor `Sponsee` is specified (`temMALFORMED`) +1. `Sponsor` is specified (which means that the `Sponsee` is submitting the transaction) and `tfDeleteObject` is not enabled, as only the sponsor can create/update the `Sponsorship` object (`temMALFORMED`) +1. `MaxFee` is less than the base fee or is not denominated in XRP (`temBAD_AMOUNT`) +1. `MaxFee` is greater than `FeeAmount` (`temBAD_AMOUNT`) +1. `FeeAmount` is not denominated in XRP (`temBAD_AMOUNT`) +1. `Sponsor` or `Sponsee` does not exist on the ledger (`terNO_ACCOUNT`) +1. `Owner == Sponsee` (attempting to create self-sponsorship) (`temMALFORMED`) +1. Sponsor does not have sufficient XRP to cover the reserve for the `Sponsorship` object (`tecINSUFFICIENT_RESERVE`) +1. If `tfDeleteObject` is enabled: +1. `FeeAmount` is specified (`temMALFORMED`) +1. `MaxFee` is specified (`temMALFORMED`) +1. `ReserveCount` is specified (`temMALFORMED`) +1. `tfSponsorshipSetRequireSignForFee` is enabled (`temINVALID_FLAG`) +1. `tfSponsorshipSetRequireSignForReserve` is enabled (`temINVALID_FLAG`) +1. `tfSponsorshipClearRequireSignForFee` is enabled (`temINVALID_FLAG`) +1. `tfSponsorshipClearRequireSignForReserve` is enabled (`temINVALID_FLAG`) +1. The `Sponsorship` object does not exist (`tecNO_ENTRY`) ### 9.5. State Changes @@ -792,24 +792,24 @@ All failure conditions mentioned in [section 8.3](#83-failure-conditions) still Additional failure conditions specific to `SponsorshipTransfer`: -- `ObjectID` is specified but does not exist on the ledger (`tecNO_TARGET`) -- `ObjectID` is specified but does not have a `Sponsor` field (object is not sponsored) (`tecNO_PERMISSION`) -- `ObjectID` is not specified and the `tx.Account` does not have a `Sponsor` field (account is not sponsored) (`tecNO_PERMISSION`) -- `tx.Account` is neither the current sponsor nor the owner (sponsee) of the object/account specified by `ObjectID` (`tecNO_PERMISSION`) -- If dissolving the sponsorship (no `Sponsor` field or `tfSponsorReserve` flag not set): - - The owner does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) -- If creating a new sponsorship (unsponsored to sponsored): - - The transaction is not submitted by the sponsee (`tecNO_PERMISSION`) - - The `Sponsor` field or the `SponsorFlags` field is missing (`temMALFORMED`) - - The `SponsorFlags` field does not include the `tfSponsorReserve` flag (`temINVALID_FLAG`) - - The new sponsor account does not exist (`terNO_ACCOUNT`) - - The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) -- If transferring the sponsorship to a new sponsor: - - The transaction is not submitted by the sponsee (`tecNO_PERMISSION`) - - The `Sponsor` field or the `SponsorFlags` field is missing (`temMALFORMED`) - - The `SponsorFlags` field does not include the `tfSponsorReserve` flag (`temINVALID_FLAG`) - - The new sponsor account does not exist (`terNO_ACCOUNT`) - - The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) +1. `ObjectID` is specified but does not exist on the ledger (`tecNO_TARGET`) +1. `ObjectID` is specified but does not have a `Sponsor` field (object is not sponsored) (`tecNO_PERMISSION`) +1. `ObjectID` is not specified and the `tx.Account` does not have a `Sponsor` field (account is not sponsored) (`tecNO_PERMISSION`) +1. `tx.Account` is neither the current sponsor nor the owner (sponsee) of the object/account specified by `ObjectID` (`tecNO_PERMISSION`) +1. If dissolving the sponsorship (no `Sponsor` field or `tfSponsorReserve` flag not set): +1. The owner does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) +1. If creating a new sponsorship (unsponsored to sponsored): +1. The transaction is not submitted by the sponsee (`tecNO_PERMISSION`) +1. The `Sponsor` field or the `SponsorFlags` field is missing (`temMALFORMED`) +1. The `SponsorFlags` field does not include the `tfSponsorReserve` flag (`temINVALID_FLAG`) +1. The new sponsor account does not exist (`terNO_ACCOUNT`) +1. The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) +1. If transferring the sponsorship to a new sponsor: +1. The transaction is not submitted by the sponsee (`tecNO_PERMISSION`) +1. The `Sponsor` field or the `SponsorFlags` field is missing (`temMALFORMED`) +1. The `SponsorFlags` field does not include the `tfSponsorReserve` flag (`temINVALID_FLAG`) +1. The new sponsor account does not exist (`terNO_ACCOUNT`) +1. The new sponsor does not have enough XRP to cover the reserve for this object/account (`tecINSUFFICIENT_RESERVE`) ### 10.5. State Changes @@ -864,14 +864,14 @@ This spec proposes the following additions: Existing failure conditions still apply (see [Payment documentation](https://xrpl.org/docs/references/protocol/transactions/types/payment)), with one exception: -- If `tfSponsorCreatedAccount` is enabled, the XRP amount does not need to be greater than or equal to the account reserve requirement. +1. If `tfSponsorCreatedAccount` is enabled, the XRP amount does not need to be greater than or equal to the account reserve requirement. Additional failure conditions when `tfSponsorCreatedAccount` is enabled: -- `tfNoRippleDirect`, `tfPartialPayment`, or `tfLimitQuality` are enabled (`temINVALID_FLAG`) -- `Amount` specifies a non-XRP currency (`temBAD_AMOUNT`) -- `Destination` already exists (`tecNO_SPONSOR_PERMISSION`) -- `Account` does not have enough XRP to cover the account reserve requirement (`tecNO_DST_INSUF_XRP`) +1. `tfNoRippleDirect`, `tfPartialPayment`, or `tfLimitQuality` are enabled (`temINVALID_FLAG`) +1. `Amount` specifies a non-XRP currency (`temBAD_AMOUNT`) +1. `Destination` already exists (`tecNO_SPONSOR_PERMISSION`) +1. `Account` does not have enough XRP to cover the account reserve requirement (`tecNO_DST_INSUF_XRP`) ### 11.4. State Changes @@ -909,11 +909,13 @@ Existing failure conditions still apply (see [AccountDelete documentation](https Additional failure conditions for sponsored accounts: -- If the `AccountRoot` associated with `tx.Account` has a `Sponsor` field: - - The `Destination` is not equal to `AccountRoot.Sponsor` (`tecNO_PERMISSION` - sponsored account funds must go to sponsor) +1. If the `AccountRoot` associated with `tx.Account` has a `Sponsor` field: -- If the `AccountRoot` associated with `tx.Account` has a non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` field: - - The transaction fails with `tecHAS_OBLIGATIONS` (account is currently sponsoring other accounts or objects and cannot be deleted until those sponsorships are transferred or dissolved) +- The `Destination` is not equal to `AccountRoot.Sponsor` (`tecNO_PERMISSION` - sponsored account funds must go to sponsor) + +2. If the `AccountRoot` associated with `tx.Account` has a non-zero `SponsoringOwnerCount` or `SponsoringAccountCount` field: + +- The transaction fails with `tecHAS_OBLIGATIONS` (account is currently sponsoring other accounts or objects and cannot be deleted until those sponsorships are transferred or dissolved) ### 12.3. State Changes @@ -1088,10 +1090,10 @@ The response fields are nearly identical to `account_objects`. ### 16.3. Failure Conditions -- Any of the [universal error types](https://xrpl.org/docs/references/http-websocket-apis/api-conventions/error-formatting#universal-errors). -- `invalidParams` - One or more fields are specified incorrectly, or one or more required fields are missing. -- `actNotFound` - The [address](https://xrpl.org/docs/references/protocol/data-types/basic-data-types#addresses) specified in the `account` field of the request does not correspond to an account in the ledger. -- `lgrNotFound` - The ledger specified by the `ledger_hash` or `ledger_index` does not exist, or it does exist but the server does not have it. +1. Any of the [universal error types](https://xrpl.org/docs/references/http-websocket-apis/api-conventions/error-formatting#universal-errors). +1. `invalidParams` - One or more fields are specified incorrectly, or one or more required fields are missing. +1. `actNotFound` - The [address](https://xrpl.org/docs/references/protocol/data-types/basic-data-types#addresses) specified in the `account` field of the request does not correspond to an account in the ledger. +1. `lgrNotFound` - The ledger specified by the `ledger_hash` or `ledger_index` does not exist, or it does exist but the server does not have it. ### 16.4. Example Request From ad012fecfa2ddc48128916cc46f62bab85f0aea6 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 11 Feb 2026 12:28:07 -0500 Subject: [PATCH 52/53] Update XLS-0068-sponsored-fees-and-reserves/README.md Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> --- XLS-0068-sponsored-fees-and-reserves/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index a325cc408..bccef7ff7 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -6,7 +6,7 @@ category: Amendment status: Draft proposal-from: https://github.com/XRPLF/XRPL-Standards/discussions/196 - requires: 74 + requires: XLS-74 created: 2024-05-02 updated: 2026-01-30 From e52ad03030334b0068be15474b611855ead176dd Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 11 Feb 2026 12:32:01 -0500 Subject: [PATCH 53/53] respond to comments --- .../README.md | 83 ++----------------- 1 file changed, 5 insertions(+), 78 deletions(-) diff --git a/XLS-0068-sponsored-fees-and-reserves/README.md b/XLS-0068-sponsored-fees-and-reserves/README.md index bccef7ff7..d10e9c117 100644 --- a/XLS-0068-sponsored-fees-and-reserves/README.md +++ b/XLS-0068-sponsored-fees-and-reserves/README.md @@ -292,39 +292,7 @@ An [`AccountRoot` ledger entry](https://xrpl.org/docs/references/protocol/ledger ### 6.1. Fields -
- - -As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/accountroot/#accountroot-fields) are the fields that the `AccountRoot` ledger object currently has. - - - -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ---------------------- | --------- | --------- | ------------- | --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Account` | ✔️ | ✔️ | N/A | `string` | `AccountID` | The identifying (classic) address of this account. | -| `AccountTxnID` | | | N/A | `string` | `Hash256` | The identifying hash of the transaction most recently sent by this account. | -| `AMMID` | ✔️ | | N/A | `string` | `Hash256` | The ledger entry ID of the corresponding AMM ledger entry, if this is an AMM pseudo-account. | -| `Balance` | | | N/A | `string` | `Amount` | The account's current XRP balance. | -| `BurnedNFTokens` | | | `0` | `number` | `UInt32` | How many total of this account's issued NFTs have been burned. | -| `Domain` | | | N/A | `string` | `Blob` | A domain associated with this account. | -| `EmailHash` | | | N/A | `string` | `Hash128` | The md5 hash of an email address. | -| `FirstNFTokenSequence` | ✔️ | | N/A | `number` | `UInt32` | The account's Sequence Number at the time it minted its first non-fungible-token. | -| `LedgerEntryType` | ✔️ | ✔️ | N/A | `string` | `UInt16` | The value `0x0061`, mapped to the string `AccountRoot`, indicates that this is an `AccountRoot `object. | -| `MessageKey` | | | N/A | `string` | `Blob` | A public key that may be used to send encrypted messages to this account. | -| `MintedNFTokens` | | | `0` | `number` | `UInt32` | How many total non-fungible tokens have been minted by/on behalf of this account. | -| `NFTokenMinter` | | | N/A | `string` | `AccountID` | Another account that can mint NFTs on behalf of this account. | -| `OwnerCount` | | ✔️ | N/A | `number` | `UInt32` | The number of objects this account owns in the ledger, which contributes to its owner reserve. | -| `PreviousTxnID` | | ✔️ | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this object. | -| `PreviousTxnLgrSeq` | | ✔️ | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this object. | -| `RegularKey` | | | N/A | `string` | `AccountID` | The address of a key pair that can be used to sign transactions for this account instead of the master key. | -| `Sequence` | | ✔️ | N/A | `number` | `UInt32` | The [sequence number](https://xrpl.org/docs/references/protocol/data-types/basic-data-types/#account-sequence) of the next valid transaction for this account. | -| `TicketCount` | | | N/A | `number` | `UInt32` | How many Tickets this account owns in the ledger. | -| `TickSize` | | | N/A | `number` | `UInt8` | [How many significant digits to use for exchange rates of Offers involving currencies issued by this address.](https://xrpl.org/resources/known-amendments/#ticksize) | -| `TransferRate` | | | N/A | `number` | `UInt32` | A [transfer fee](https://xrpl.org/docs/concepts/tokens/transfer-fees/) to charge other users for sending currency issued by this account to each other. | -| `WalletLocator` | | | N/A | `string` | `Hash256` | An arbitrary 256-bit value that users can set. | -| `WalletSize` | | | N/A | `number` | `UInt32` | Unused. | - -
+As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/accountroot#accountroot-fields) are the fields that the `AccountRoot` ledger object currently has. This spec proposes these additional fields: @@ -353,7 +321,7 @@ The total account reserve should now be calculated as: $$ \displaylines{ -(acct.Sponsor ext{ ? } 0 : acctReserve) + +(acct.Sponsor \text{ ? } 0 : acctReserve) + objReserve * (acct.OwnerCount + acct.SponsoringOwnerCount - acct.SponsoredOwnerCount) + acctReserve * acct.SponsoringAccountCount } @@ -398,33 +366,8 @@ A `RippleState` ledger entry represents a [trust line](https://xrpl.org/docs/con ### 7.1. Fields -
- - As a reference, [here](https://xrpl.org/docs/references/protocol/ledger-data/ledger-entry-types/ripplestate#ripplestate-fields) are the fields that the `RippleState` ledger object currently has. - - -| Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | -| ------------------- | --------- | --------- | ------------- | -------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `Balance` | No | Yes | N/A | `object` | `Amount` | The balance of the trust line, from the perspective of the low account. A negative balance indicates that the high account holds tokens issued by the low account. The issuer in this is always set to the neutral value [ACCOUNT_ONE](https://xrpl.org/docs/concepts/accounts/addresses#special-addresses). | -| `Flags` | No | Yes | N/A | `number` | `UInt32` | A bit-map of boolean options enabled for this entry. | -| `HighLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the high account has set on the trust line. The `issuer` is the address of the high account that set this limit. | -| `HighNode` | Yes | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the high account's owner directory links to this entry, in case the directory consists of multiple pages. | -| `HighQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the high account, as an integer in the implied ratio `HighQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `HighQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the high account, as an integer in the implied ratio `HighQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `LedgerEntryType` | Yes | Yes | `RippleState` | `string` | `UInt16` | The value `0x0072`, mapped to the string `RippleState`, indicates that this is a RippleState entry. | -| `LockCount` | No | No | N/A | `object` | `Amount` | The total number of lock balances on a `RippleState` ledger object. | -| `LockedBalance` | No | No | N/A | `object` or `string` | `Amount` | The total number of locked tokens on a `RippleState` ledger object. | -| `LowLimit` | No | Yes | N/A | `object` | `Amount` | The limit that the low account has set on the trust line. The `issuer` is the address of the low account that set this limit. | -| `LowNode` | No | Yes | N/A | `string` | `UInt64` | (Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this entry, in case the directory consists of multiple pages. | -| `LowQualityIn` | No | No | N/A | `number` | `UInt32` | The inbound quality set by the low account, as an integer in the implied ratio `LowQualityIn`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `LowQualityOut` | No | No | N/A | `number` | `UInt32` | The outbound quality set by the low account, as an integer in the implied ratio `LowQualityOut`:1,000,000,000. As a special case, the value 0 is equivalent to 1 billion, or face value. | -| `PreviousTxnID` | No | Yes | N/A | `string` | `Hash256` | The identifying hash of the transaction that most recently modified this entry. | -| `PreviousTxnLgrSeq` | No | Yes | N/A | `number` | `UInt32` | The ledger index that contains the transaction that most recently modified this entry. | - -
- This spec proposes these additional fields: | Field Name | Constant? | Required? | Default Value | JSON Type | Internal Type | Description | @@ -480,8 +423,6 @@ _NOTE: The invariants in [4.3](#43-invariant-checks) also apply to `RippleState` As a reference, [here](https://xrpl.org/docs/references/protocol/transactions/common-fields/) are the fields that all transactions currently have. - - We propose these modifications: | Field Name | Required? | JSON Type | Internal Type | Description | @@ -977,25 +918,11 @@ When a transaction includes a `SponsorFlags` field with the `tfSponsorReserve` f ## 15. RPC: `account_objects` -### 15.1. Request Fields - -The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. As a reference, here are the fields that `account_objects` currently accepts: +The [`account_objects` RPC method](https://xrpl.org/account_objects.html) already exists on the XRPL. This spec proposes an addition to the `account_objects` RPC method, to better support sponsored accounts. -| Field Name | Required? | JSON Type | Description | -| ------------------------ | --------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `account` | ✔️ | `string` | Get ledger entries associated with this account. | -| `deletion_blockers_only` | | `boolean` | If `true`, only return ledger entries that would block this account from being deleted. The default is `false`. | -| `ledger_hash` | | `string` | The unique hash of the ledger version to use. | -| `ledger_index` | | `number` or `string` | The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. | -| `limit` | | `number` | The maximum number of ledger entries to include in the results. Must be within the inclusive range `10` to `400` on non-admin connections. The default is `200`. | -| `marker` | | `any` | Value from a previous paginated response. Resume retrieving data where that response left off. | -| `type` | | `string` | Filter results to a specific type of ledger entry. This field accepts canonical names of ledger entry types (case insensitive) or short names. Ledger entry types that can't appear in an owner directory are not allowed. If omitted, return ledger entries of all types. | - -We propose this additional field: +### 15.1. Request Fields -| Field Name | Required? | JSON Type | Description | -| ----------- | --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sponsored` | | `boolean` | If `true`, only return ledger entries that are sponsored. If `false`, only return ledger entries that are not sponsored. If omitted, return all objects. | +As a reference, [here](https://xrpl.org/account_objects.html#request-format) are the fields that `account_objects` currently accepts. ### 15.2. Response Fields