# Manage a Sponsorship Pool

This tutorial shows you how to manage a pre-funded [sponsorship](/es-es/docs/concepts/accounts/sponsored-fees-and-reserves#how-sponsorship-works) pool as a sponsor. The example creates a pool, checks what it spends, tops it up, and deletes it to reclaim any unspent XRP.

_Requires the [Sponsor amendment](/resources/known-amendments#sponsor). (Open for Voting: 5.71%)_

## Goals

By the end of this tutorial, you should be able to:

- Create a sponsorship pool and track what it spends.
- Update a pool to adjust its fee allocation and reserve allowance.
- Delete a pool to return the remaining funds to the sponsor.


## Prerequisites

To complete this tutorial, you should:

- Have a basic understanding of the XRP Ledger and [Sponsored Fees and Reserves](/es-es/docs/concepts/accounts/sponsored-fees-and-reserves).
- Have an XRP Ledger client library set up in your development environment. This page provides examples for the following:
  - **JavaScript** with the [xrpl.js library](https://github.com/XRPLF/xrpl.js). See [Get Started Using JavaScript](/es-es/docs/tutorials/get-started/get-started-javascript) for setup steps.
  - **Python** with the [xrpl-py library](https://github.com/XRPLF/xrpl-py). See [Get Started Using Python](/es-es/docs/tutorials/get-started/get-started-python) for setup steps.


## Source Code

You can find the complete source code for this tutorial's example in the [code samples section of this website's repository](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/sponsored-fees-and-reserves).

## Steps

### 1. Install dependencies

JavaScript
From the code sample folder, use `npm` to install dependencies:

```bash
npm install
```

Python
From the code sample folder, set up a virtual environment and use `pip` to install dependencies:

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

### 2. Set up the client

Import the necessary libraries and instantiate a client to connect to the XRPL. This example imports:

JavaScript
- `xrpl`: Used for XRPL client connection, transaction submission, and wallet handling. `SponsorshipSetFlags` holds the flags that update and delete the pool.


```js
import {
  Client,
  SponsorFlags,
  SponsorshipSetFlags,
  validate
} from 'xrpl'

// Connect to the network ----------------------
const client = new Client('wss://s.devnet.rippletest.net:51233')
await client.connect()
```

Python
- `xrpl`: Used for XRPL client connection, transaction submission, and wallet handling. `SponsorshipSetFlag` holds the flags that update and delete the pool.
- `json`: Used for formatting JSON data.
- `sys`: Used to exit on transaction failures.


```py
import json
import sys

from xrpl.clients import JsonRpcClient
from xrpl.models import (
    AccountInfo,
    DepositPreauth,
    SponsorFlag,
    SponsorshipSet,
    SponsorshipSetFlag,
)
from xrpl.transaction import submit_and_wait
from xrpl.wallet import generate_faucet_wallet

# Set up client ----------------------
client = JsonRpcClient("https://s.devnet.rippletest.net:51234")
```

### 3. Create the wallets

Create and fund the sponsor and the sponsee accounts.

JavaScript
```js
// Create the sponsor and sponsee wallets ----------------------
console.log(`\n=== Creating the sponsor and sponsee wallets... ===`)
const { wallet: sponsor } = await client.fundWallet()
const { wallet: sponsee } = await client.fundWallet()

console.log(`Sponsor address: ${sponsor.address}`)
console.log(`Sponsee address: ${sponsee.address}`)
```

Python
```py
# Create the sponsor and sponsee wallets ----------------------
print("\n=== Creating the sponsor and sponsee wallets... ===")
sponsor = generate_faucet_wallet(client)
sponsee = generate_faucet_wallet(client)

print(f"Sponsor address: {sponsor.address}")
print(f"Sponsee address: {sponsee.address}")
```

### 4. Create the pool

Submit a [SponsorshipSet transaction](/docs/references/protocol/transactions/types/sponsorshipset) to create the pool.

JavaScript
```js
// Prepare SponsorshipSet transaction ----------------------
// FeeAmountDelta adds 1 XRP to the fee pool, MaxFee caps the pool's contribution
// to any single transaction, and RemainingOwnerCountDelta allows five sponsored ledger entries.
console.log(`\n=== Preparing SponsorshipSet transaction... ===`)
const createPoolTx = {
  TransactionType: 'SponsorshipSet',
  Account: sponsor.address,
  Sponsee: sponsee.address,
  FeeAmountDelta: '1000000',
  MaxFee: '1000',
  RemainingOwnerCountDelta: 5
}

validate(createPoolTx)
console.log(JSON.stringify(createPoolTx, null, 2))
```

Python
```py
# Prepare SponsorshipSet transaction ----------------------
# FeeAmountDelta adds 1 XRP to the fee pool, MaxFee caps the pool's contribution
# to any single transaction, and RemainingOwnerCountDelta allows five sponsored ledger entries.
print("\n=== Preparing SponsorshipSet transaction... ===")
create_pool_tx = SponsorshipSet(
    account=sponsor.address,
    sponsee=sponsee.address,
    fee_amount_delta="1000000",
    max_fee="1000",
    remaining_owner_count_delta=5,
)

print(json.dumps(create_pool_tx.to_xrpl(), indent=2))
```

Only the sponsor can create a pool, so it signs and submits the transaction alone. The metadata returns the new Sponsorship entry's ID.

JavaScript
```js
// Submit the SponsorshipSet transaction ----------------------
console.log(`\n=== Submitting SponsorshipSet transaction... ===`)
const createResponse = await client.submitAndWait(createPoolTx, {
  wallet: sponsor,
  autofill: true
})

if (createResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = createResponse.result.meta.TransactionResult
  console.error('Error: Unable to create the sponsorship:', resultCode)
  await client.disconnect()
  process.exit(1)
}

const sponsorshipNode = createResponse.result.meta.AffectedNodes.find(
  node => node.CreatedNode?.LedgerEntryType === 'Sponsorship'
)
console.log('Sponsorship created successfully!')
console.log(`Sponsorship ID: ${sponsorshipNode.CreatedNode.LedgerIndex}`)
console.log(`Transaction URL: https://devnet.xrpl.org/transactions/${createResponse.result.hash}`)
```

Python
```py
# Submit the SponsorshipSet transaction ----------------------
print("\n=== Submitting SponsorshipSet transaction... ===")
create_response = submit_and_wait(create_pool_tx, client, sponsor)

if create_response.result["meta"]["TransactionResult"] != "tesSUCCESS":
    result_code = create_response.result["meta"]["TransactionResult"]
    print(f"Error: Unable to create the sponsorship: {result_code}")
    sys.exit(1)

sponsorship_node = next(
    node for node in create_response.result["meta"]["AffectedNodes"]
    if node.get("CreatedNode", {}).get("LedgerEntryType") == "Sponsorship"
)
print("Sponsorship created successfully!")
print(f"Sponsorship ID: {sponsorship_node['CreatedNode']['LedgerIndex']}")
print(f"Transaction URL: https://devnet.xrpl.org/transactions/{create_response.result['hash']}")
```

Warning
The Sponsorship entry appears in both accounts' owner directories, which makes it a [deletion blocker](/es-es/docs/concepts/accounts/deleting-accounts#deletion-blockers) for the sponsor and the sponsee alike. Neither account can be deleted until the pool is.

### 5. Spend part of the pool

Spend part of the pool by submitting a [DepositPreauth transaction](/docs/references/protocol/transactions/types/depositpreauth) that draws the fee and one owner reserve from the pool.

JavaScript
```js
// Spend part of the pool ----------------------
// The sponsee creates a DepositPreauth entry, drawing the fee and one owner reserve
// from the pool.
console.log(`\n=== Submitting sponsored DepositPreauth transaction... ===`)
const depositPreauthTx = {
  TransactionType: 'DepositPreauth',
  Account: sponsee.address,
  Authorize: sponsor.address,
  Sponsor: sponsor.address,
  SponsorFlags: SponsorFlags.spfSponsorFee | SponsorFlags.spfSponsorReserve
}
validate(depositPreauthTx)

const depositPreauthResponse = await client.submitAndWait(depositPreauthTx, {
  wallet: sponsee,
  autofill: true
})

if (depositPreauthResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = depositPreauthResponse.result.meta.TransactionResult
  console.error('Error: Unable to create the preauthorization:', resultCode)
  await client.disconnect()
  process.exit(1)
}

let fields = depositPreauthResponse.result.meta.AffectedNodes.find(
  node => node.ModifiedNode?.LedgerEntryType === 'Sponsorship'
).ModifiedNode.FinalFields
console.log('Sponsorship pool:')
console.log(`  Fee amount:            ${fields.FeeAmount} drops`)
console.log(`  Owner reserves count:  ${fields.RemainingOwnerCount}`)
console.log(`Transaction URL: https://devnet.xrpl.org/transactions/${depositPreauthResponse.result.hash}`)
```

Python
```py
# Spend part of the pool ----------------------
# The sponsee creates a DepositPreauth entry, drawing the fee and one owner reserve
# from the pool.
print("\n=== Submitting sponsored DepositPreauth transaction... ===")
deposit_preauth_tx = DepositPreauth(
    account=sponsee.address,
    authorize=sponsor.address,
    sponsor=sponsor.address,
    sponsor_flags=SponsorFlag.SPF_SPONSOR_FEE | SponsorFlag.SPF_SPONSOR_RESERVE,
)
deposit_preauth_response = submit_and_wait(deposit_preauth_tx, client, sponsee)

if deposit_preauth_response.result["meta"]["TransactionResult"] != "tesSUCCESS":
    result_code = deposit_preauth_response.result["meta"]["TransactionResult"]
    print(f"Error: Unable to create the preauthorization: {result_code}")
    sys.exit(1)

fields = next(
    node["ModifiedNode"]["FinalFields"]
    for node in deposit_preauth_response.result["meta"]["AffectedNodes"]
    if node.get("ModifiedNode", {}).get("LedgerEntryType") == "Sponsorship"
)
print("Sponsorship pool:")
print(f"  Fee amount:            {fields['FeeAmount']} drops")
print(f"  Owner reserves count:  {fields['RemainingOwnerCount']}")
print(f"Transaction URL: https://devnet.xrpl.org/transactions/{deposit_preauth_response.result['hash']}")
```

The Sponsorship entry's `FeeAmount` and `RemainingOwnerCount` fields drop accordingly.

### 6. Top up the pool

Send another SponsorshipSet transaction for the same sponsee to change the pool's allowances.

`FeeAmountDelta` and `RemainingOwnerCountDelta` apply changes to the current allowances. Positive deltas add budget and negative deltas reduce it. `MaxFee` remains an absolute per-transaction cap.

Note
The pool doesn't automatically regain `RemainingOwnerCount` when a sponsored ledger entry is deleted or reassigned. Those actions free the sponsor's reserve and lower its `SponsoringOwnerCount`, but the pool's spent reserve allowance remains spent. Top the pool up with `RemainingOwnerCountDelta` to sponsor more ledger entries.

JavaScript
```js
// Prepare SponsorshipSet transaction to top up the pool ----------------------
// A second SponsorshipSet on the same sponsee applies deltas to the current allowances.
// Here the sponsor adds another 1 XRP of fee budget and five more reserve units.
console.log(`\n=== Preparing SponsorshipSet transaction to top up sponsorship pool... ===`)
const updatePoolTx = {
  TransactionType: 'SponsorshipSet',
  Account: sponsor.address,
  Sponsee: sponsee.address,
  FeeAmountDelta: '1000000',
  MaxFee: '1000',
  RemainingOwnerCountDelta: 5
}

validate(updatePoolTx)
console.log(JSON.stringify(updatePoolTx, null, 2))
```

Python
```py
# Prepare SponsorshipSet transaction to top up the pool ----------------------
# A second SponsorshipSet on the same sponsee applies deltas to the current allowances.
# Here the sponsor adds another 1 XRP of fee budget and five more reserve units.
print("\n=== Preparing SponsorshipSet transaction to top up sponsorship pool... ===")
update_pool_tx = SponsorshipSet(
    account=sponsor.address,
    sponsee=sponsee.address,
    fee_amount_delta="1000000",
    max_fee="1000",
    remaining_owner_count_delta=5,
)

print(json.dumps(update_pool_tx.to_xrpl(), indent=2))
```

The sponsor submits the update, and the metadata shows the entry's raised `FeeAmount` and `RemainingOwnerCount`.

JavaScript
```js
// Submit the SponsorshipSet transaction to top up the pool ----------------------
console.log(`\n=== Submitting SponsorshipSet transaction... ===`)
const updateResponse = await client.submitAndWait(updatePoolTx, {
  wallet: sponsor,
  autofill: true
})

if (updateResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = updateResponse.result.meta.TransactionResult
  console.error('Error: Unable to update the sponsorship:', resultCode)
  await client.disconnect()
  process.exit(1)
}

fields = updateResponse.result.meta.AffectedNodes.find(
  node => node.ModifiedNode?.LedgerEntryType === 'Sponsorship'
).ModifiedNode.FinalFields
console.log('Sponsorship pool topped up successfully:')
console.log(`  Fee amount:            ${fields.FeeAmount} drops`)
console.log(`  Owner reserves count:  ${fields.RemainingOwnerCount}`)
console.log(`Transaction URL: https://devnet.xrpl.org/transactions/${updateResponse.result.hash}`)
```

Python
```py
# Submit the SponsorshipSet transaction to top up the pool ----------------------
print("\n=== Submitting SponsorshipSet transaction... ===")
update_response = submit_and_wait(update_pool_tx, client, sponsor)

if update_response.result["meta"]["TransactionResult"] != "tesSUCCESS":
    result_code = update_response.result["meta"]["TransactionResult"]
    print(f"Error: Unable to update the sponsorship: {result_code}")
    sys.exit(1)

fields = next(
    node["ModifiedNode"]["FinalFields"]
    for node in update_response.result["meta"]["AffectedNodes"]
    if node.get("ModifiedNode", {}).get("LedgerEntryType") == "Sponsorship"
)
print("Sponsorship pool topped up successfully:")
print(f"  Fee amount:            {fields['FeeAmount']} drops")
print(f"  Owner reserves count:  {fields['RemainingOwnerCount']}")
print(f"Transaction URL: https://devnet.xrpl.org/transactions/{update_response.result['hash']}")
```

### 7. Delete the pool

Submit a SponsorshipSet transaction with the `tfDeleteObject` flag enabled to delete the Sponsorship entry and return the unspent `FeeAmount` to the sponsor. The example looks up the sponsor's balance with the [account_info method](/docs/references/http-websocket-apis/public-api-methods/account-methods/account_info) first, so it can compare the balance after the deletion.

JavaScript
```js
// Prepare SponsorshipSet transaction to delete the sponsorship ----------------------
// tfDeleteObject returns the unspent FeeAmount to the sponsor. Ledger entries the
// pool already paid reserves for stay sponsored until they're transferred or deleted.
console.log(`\n=== Preparing SponsorshipSet transaction to delete the sponsorship... ===`)
const balanceBeforeResponse = await client.request({
  command: 'account_info',
  account: sponsor.address,
  ledger_index: 'validated'
})
const sponsorBalanceBefore = BigInt(
  balanceBeforeResponse.result.account_data.Balance
)

const deletePoolTx = {
  TransactionType: 'SponsorshipSet',
  Account: sponsor.address,
  Sponsee: sponsee.address,
  Flags: SponsorshipSetFlags.tfDeleteObject
}

validate(deletePoolTx)
console.log(JSON.stringify(deletePoolTx, null, 2))
```

Python
```py
# Prepare SponsorshipSet transaction to delete the sponsorship ----------------------
# tfDeleteObject returns the unspent FeeAmount to the sponsor. Ledger entries the
# pool already paid reserves for stay sponsored until they're transferred or deleted.
print("\n=== Preparing SponsorshipSet transaction to delete the sponsorship... ===")
sponsor_balance_before = int(
    client.request(
        AccountInfo(account=sponsor.address, ledger_index="validated")
    ).result["account_data"]["Balance"]
)

delete_pool_tx = SponsorshipSet(
    account=sponsor.address,
    sponsee=sponsee.address,
    flags=SponsorshipSetFlag.TF_DELETE_OBJECT,
)

print(json.dumps(delete_pool_tx.to_xrpl(), indent=2))
```

The sponsor submits the deletion, and the metadata confirms the Sponsorship entry is deleted.

JavaScript
```js
// Submit the SponsorshipSet transaction to delete the sponsorship ----------------------
console.log(`\n=== Submitting SponsorshipSet transaction... ===`)
const deleteResponse = await client.submitAndWait(deletePoolTx, {
  wallet: sponsor,
  autofill: true
})

if (deleteResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = deleteResponse.result.meta.TransactionResult
  console.error('Error: Unable to delete the sponsorship:', resultCode)
  await client.disconnect()
  process.exit(1)
}

const deletedNode = deleteResponse.result.meta.AffectedNodes.find(
  node => node.DeletedNode?.LedgerEntryType === 'Sponsorship'
)
console.log('Sponsorship deleted successfully!')
console.log(`Transaction URL: https://devnet.xrpl.org/transactions/${deleteResponse.result.hash}`)
```

Python
```py
# Submit the SponsorshipSet transaction to delete the sponsorship ----------------------
print("\n=== Submitting SponsorshipSet transaction... ===")
delete_response = submit_and_wait(delete_pool_tx, client, sponsor)

if delete_response.result["meta"]["TransactionResult"] != "tesSUCCESS":
    result_code = delete_response.result["meta"]["TransactionResult"]
    print(f"Error: Unable to delete the sponsorship: {result_code}")
    sys.exit(1)

deleted_node = next(
    node for node in delete_response.result["meta"]["AffectedNodes"]
    if node.get("DeletedNode", {}).get("LedgerEntryType") == "Sponsorship"
)
print("Sponsorship deleted successfully!")
print(f"Transaction URL: https://devnet.xrpl.org/transactions/{delete_response.result['hash']}")
```

Note
Deleting a pool does not revoke sponsorship on ledger entries that already consumed its reserve allowance. Those entries stay sponsored until the sponsorship ends through a [SponsorshipTransfer transaction](/docs/references/protocol/transactions/types/sponsorshiptransfer) with the `tfSponsorshipEnd` flag, or until the entries are deleted.

### 8. Verify the sponsor reclaimed XRP

Send another [account_info method](/docs/references/http-websocket-apis/public-api-methods/account-methods/account_info) request to compare the sponsor's balance before and after the deletion. The sponsor gets the pool's unspent `FeeAmount` back, minus the transaction fee paid to submit the delete transaction.

Deleting the Sponsorship entry also releases the sponsor's owner reserve requirement, but that does not appear as an XRP balance increase. The balance check only shows the returned `FeeAmount` minus the delete transaction fee.

JavaScript
```js
// Show the reclaimed XRP ----------------------
console.log(`\n=== Reclaimed Funds ===`)
const balanceAfterResponse = await client.request({
  command: 'account_info',
  account: sponsor.address,
  ledger_index: 'validated'
})
const sponsorBalanceAfter = BigInt(
  balanceAfterResponse.result.account_data.Balance
)
const deleteFee = BigInt(deleteResponse.result.tx_json.Fee)

console.log(`Unspent fee amount returned from pool: ${deletedNode.DeletedNode.FinalFields.FeeAmount} drops`)
console.log(`Sponsor balance "before" deletion:     ${sponsorBalanceBefore} drops`)
console.log(`Sponsor balance "after" deletion:      ${sponsorBalanceAfter} drops`)
console.log(`Delete transaction (fee paid):         ${deleteFee} drops`)

await client.disconnect()
```

Python
```py
# Show the reclaimed XRP ----------------------
print("\n=== Reclaimed Funds ===")
sponsor_balance_after = int(
    client.request(
        AccountInfo(account=sponsor.address, ledger_index="validated")
    ).result["account_data"]["Balance"]
)
delete_fee = int(delete_response.result["tx_json"]["Fee"])

print(f"Unspent fee amount returned from pool: {deleted_node['DeletedNode']['FinalFields']['FeeAmount']} drops")
print(f"Sponsor balance \"before\" deletion:     {sponsor_balance_before} drops")
print(f"Sponsor balance \"after\" deletion:      {sponsor_balance_after} drops")
print(f"Delete transaction (fee paid):         {delete_fee} drops")
```

## See Also

- **Concepts:**
  - [Sponsored Fees and Reserves](/es-es/docs/concepts/accounts/sponsored-fees-and-reserves)
  - [Reserves](/es-es/docs/concepts/accounts/reserves)
- **Tutorials:**
  - [Sponsor a Transaction by Co-Signing](/es-es/docs/tutorials/best-practices/account-management/sponsor-a-transaction-by-co-signing)
  - [Sponsor a Transaction with a Pre-funded Pool](/es-es/docs/tutorials/best-practices/account-management/sponsor-a-transaction-with-a-pre-funded-pool)
  - [Transfer a Reserve Sponsorship](/es-es/docs/tutorials/best-practices/account-management/transfer-a-reserve-sponsorship)
- **References:**
  - [SponsorshipSet transaction](/docs/references/protocol/transactions/types/sponsorshipset)
  - [DepositPreauth transaction](/docs/references/protocol/transactions/types/depositpreauth)
  - [Sponsorship ledger entry](/docs/references/protocol/ledger-data/ledger-entry-types/sponsorship)