# Sponsor a Transaction by Co-Signing

This tutorial shows you how to use the co-signed [sponsorship](/ja/docs/concepts/accounts/sponsored-fees-and-reserves#how-sponsorship-works) flow, where the sponsor approves a single transaction by adding a signature to it. In this example, a sponsor creates an account for a new user who holds no XRP, then covers the fee and the owner reserve for a transaction that user sends.

Use this flow when the sponsor needs to review each sponsored transaction before it is submitted. If the sponsee needs to be able to transact without waiting on the sponsor, use a [pre-funded pool](/ja/docs/tutorials/best-practices/account-management/sponsor-a-transaction-with-a-pre-funded-pool) instead.

_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 an account for a sponsee and pay its account reserve as the sponsor.
- Co-sign a transaction so the sponsor pays the fee and the new ledger entry's reserve.
- Confirm that the sponsor paid the fee and reserve.


## Prerequisites

To complete this tutorial, you should:

- Have a basic understanding of the XRP Ledger and [Sponsored Fees and Reserves](/ja/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](/ja/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](/ja/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.


```js
import {
  Client,
  PaymentFlags,
  SponsorFlags,
  Wallet,
  signAsSponsor,
  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.
- `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 DepositPreauth, Payment, PaymentFlag, SponsorFlag
from xrpl.transaction import autofill, sign, sign_as_sponsor, submit_and_wait
from xrpl.wallet import Wallet, generate_faucet_wallet

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

### 3. Create the wallets

Fund the sponsor and generate a key pair for the sponsee. The sponsee has no account on the ledger yet, and no XRP to pay for one.

JavaScript
```js
// Create the sponsor and sponsee wallets ----------------------
// Only the sponsor is funded. The sponsee has no account on the ledger yet, and no
// XRP to pay for one.
console.log(`\n=== Creating the sponsor and sponsee wallets... ===`)
const { wallet: sponsor } = await client.fundWallet()
const sponsee = Wallet.generate()

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

Python
```py
# Create the sponsor and sponsee wallets ----------------------
# Only the sponsor is funded. The sponsee has no account on the ledger yet, and no
# XRP to pay for one.
print("\n=== Creating the sponsor and sponsee wallets... ===")
sponsor = generate_faucet_wallet(client)
sponsee = Wallet.create()

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

### 4. Create the sponsee's account

Create a [Payment transaction](/docs/references/protocol/transactions/types/payment) with the `tfSponsorCreatedAccount` flag enabled to create the sponsee's account. The flag makes the sponsor responsible for the new account's reserve, so the payment only needs to deliver the smallest possible XRP amount.

JavaScript
```js
// Prepare Payment transaction to create the sponsee's account ----------------------
// The tfSponsorCreatedAccount flag makes the sponsor pay the new account's reserve,
// so the payment itself only needs to deliver 1 drop.
console.log(`\n=== Preparing Payment transaction to create the sponsee's account... ===`)
const createAccountTx = {
  TransactionType: 'Payment',
  Account: sponsor.address,
  Destination: sponsee.address,
  Amount: '1',
  Flags: PaymentFlags.tfSponsorCreatedAccount
}

validate(createAccountTx)
console.log(JSON.stringify(createAccountTx, null, 2))

// Submit the Payment transaction ----------------------
console.log(`\n=== Submitting Payment transaction... ===`)
const createAccountResponse = await client.submitAndWait(createAccountTx, {
  wallet: sponsor,
  autofill: true
})
if (createAccountResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = createAccountResponse.result.meta.TransactionResult
  console.error(`Error: Unable to create the sponsee's account:`, resultCode)
  await client.disconnect()
  process.exit(1)
}

// Confirm the new AccountRoot entry records the sponsor
const accountNode = createAccountResponse.result.meta.AffectedNodes.find(
  node => node.CreatedNode?.LedgerEntryType === 'AccountRoot'
)
console.log('Sponsee account created successfully!')
console.log(`Account reserve sponsored by: ${accountNode.CreatedNode.NewFields.Sponsor}`)
console.log(`Transaction URL: https://devnet.xrpl.org/transactions/${createAccountResponse.result.hash}`)
```

Python
```py
# Prepare Payment transaction to create the sponsee's account ----------------------
# The tfSponsorCreatedAccount flag makes the sponsor pay the new account's reserve,
# so the payment itself only needs to deliver 1 drop.
print("\n=== Preparing Payment transaction to create the sponsee's account... ===")
create_account_tx = Payment(
    account=sponsor.address,
    destination=sponsee.address,
    amount="1",
    flags=PaymentFlag.TF_SPONSOR_CREATED_ACCOUNT,
)

print(json.dumps(create_account_tx.to_xrpl(), indent=2))

# Submit the Payment transaction ----------------------
print("\n=== Submitting Payment transaction... ===")
create_account_response = submit_and_wait(create_account_tx, client, sponsor)

if create_account_response.result["meta"]["TransactionResult"] != "tesSUCCESS":
    result_code = create_account_response.result["meta"]["TransactionResult"]
    print(f"Error: Unable to create the sponsee's account: {result_code}")
    sys.exit(1)

# Confirm the new AccountRoot entry records the sponsor
account_node = next(
    node for node in create_account_response.result["meta"]["AffectedNodes"]
    if node.get("CreatedNode", {}).get("LedgerEntryType") == "AccountRoot"
)
print("Sponsee account created successfully!")
print(f"Account reserve sponsored by: {account_node['CreatedNode']['NewFields']['Sponsor']}")
print(f"Transaction URL: https://devnet.xrpl.org/transactions/{create_account_response.result['hash']}")
```

### 5. Prepare the sponsored transaction

In this example, the sponsee submits a [DepositPreauth transaction](/docs/references/protocol/transactions/types/depositpreauth), but many other transaction types can also be sponsored; see [SponsorFlags field](/ja/docs/references/protocol/transactions/common-fields#sponsorflags-field) to learn more.

JavaScript
```js
// Prepare the sponsored DepositPreauth transaction ----------------------
// The sponsee is the sending account. The Sponsor and SponsorFlags fields ask the
// sponsor to cover both the fee and the reserve for the new DepositPreauth entry.
console.log(`\n=== Preparing sponsored DepositPreauth transaction... ===`)
const depositPreauthTx = {
  TransactionType: 'DepositPreauth',
  Account: sponsee.address,
  Authorize: sponsor.address,
  Sponsor: sponsor.address,
  SponsorFlags: SponsorFlags.spfSponsorFee | SponsorFlags.spfSponsorReserve
}
validate(depositPreauthTx)

const preparedTx = await client.autofill(depositPreauthTx)
console.log(JSON.stringify(preparedTx, null, 2))
```

Python
```py
# Prepare the sponsored DepositPreauth transaction ----------------------
# The sponsee is the sending account. The Sponsor and SponsorFlags fields ask the
# sponsor to cover both the fee and the reserve for the new DepositPreauth entry.
print("\n=== Preparing 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_tx = autofill(deposit_preauth_tx, client)
print(json.dumps(deposit_preauth_tx.to_xrpl(), indent=2))
```

The `Sponsor` field names the account that pays, and the `SponsorFlags` field states what it pays for:

- `spfSponsorFee` for the transaction fee.
- `spfSponsorReserve` for the reserve of the new `DepositPreauth` entry.


The sponsor must sign the transaction with the exact `Fee` amount it agrees to pay. Autofill the transaction before signing so the `Fee` field is set; if the fee is added or changed after signing, the signature no longer matches the transaction.

### 6. Co-sign the transaction

A sponsored transaction needs a signature from both parties, and each one has its own place in the transaction.

The sponsee's signature fills the usual `SigningPubKey` and `TxnSignature` fields. The sponsor's goes into a separate `SponsorSignature` field, which carries its own `SigningPubKey` and `TxnSignature`.

JavaScript
```js
// Sign as the sponsee ----------------------
const sponseeSignedTx = sponsee.sign(preparedTx)

// Co-sign as the sponsor ----------------------
const coSignedTx = signAsSponsor(sponsor, sponseeSignedTx.tx_blob)
```

Python
```py
# Sign as the sponsee ----------------------
sponsee_signed_tx = sign(deposit_preauth_tx, sponsee)

# Co-sign as the sponsor ----------------------
co_signed_tx = sign_as_sponsor(sponsor, sponsee_signed_tx)
```

### 7. Submit the transaction and confirm the sponsorship

Submit the fully signed transaction and wait for validation.

JavaScript
```js
// Submit the fully signed transaction and wait for validation ----------------------
console.log(`\n=== Submitting sponsored DepositPreauth transaction... ===`)
console.log(JSON.stringify(coSignedTx.tx, null, 2))
const submitResponse = await client.submitAndWait(coSignedTx.tx_blob)

if (submitResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = submitResponse.result.meta.TransactionResult
  console.error('Error: Unable to create the preauthorization:', resultCode)
  await client.disconnect()
  process.exit(1)
}
console.log('Transaction sponsored successfully!')
console.log(`Transaction URL: https://devnet.xrpl.org/transactions/${submitResponse.result.hash}`)
```

Python
```py
# Submit the fully signed transaction and wait for validation ----------------------
print("\n=== Submitting sponsored DepositPreauth transaction... ===")
print(json.dumps(co_signed_tx.tx.to_xrpl(), indent=2))
submit_response = submit_and_wait(co_signed_tx.tx, client)

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

print("Transaction sponsored successfully!")
print(f"Transaction URL: https://devnet.xrpl.org/transactions/{submit_response.result['hash']}")
```

Finally, inspect the affected nodes to confirm the sponsor paid the fee and reserve.

JavaScript
```js
// Extract sponsorship information from the transaction result ----------------------
console.log(`\n=== Sponsorship Information ===`)
const preauthNode = submitResponse.result.meta.AffectedNodes.find(
  node => node.CreatedNode?.LedgerEntryType === 'DepositPreauth'
)
console.log(`DepositPreauth ID: ${preauthNode.CreatedNode.LedgerIndex}`)
console.log(`DepositPreauth reserve sponsored by: ${preauthNode.CreatedNode.NewFields.Sponsor}`)

// The sponsor's AccountRoot shows the fee it paid and the reserves it now covers.
// The sponsee's balance is untouched.
for (const node of submitResponse.result.meta.AffectedNodes) {
  const modified = node.ModifiedNode
  if (modified?.LedgerEntryType !== 'AccountRoot') {
    continue
  }

  const fields = modified.FinalFields
  const previousBalance = modified.PreviousFields?.Balance ?? fields.Balance
  const feePaid = Number(previousBalance) - Number(fields.Balance)

  if (fields.Account === sponsor.address) {
    console.log(`\nSponsor fee paid: ${feePaid} drops`)
    console.log(`Sponsor balance:  ${fields.Balance} drops`)
    console.log(`Reserves sponsored (SponsoringOwnerCount): ${fields.SponsoringOwnerCount ?? 0}`)
  } else if (fields.Account === sponsee.address) {
    console.log(`\nSponsee fee paid: ${feePaid} drops`)
    console.log(`Sponsee balance:  ${fields.Balance} drops`)
    console.log(`Sponsee owner count: ${fields.OwnerCount ?? 0}`)
  }
}

await client.disconnect()
```

Python
```py
# Extract sponsorship information from the transaction result ----------------------
print("\n=== Sponsorship Information ===")
preauth_node = next(
    node for node in submit_response.result["meta"]["AffectedNodes"]
    if node.get("CreatedNode", {}).get("LedgerEntryType") == "DepositPreauth"
)
print(f"DepositPreauth ID: {preauth_node['CreatedNode']['LedgerIndex']}")
print(f"DepositPreauth reserve sponsored by: {preauth_node['CreatedNode']['NewFields']['Sponsor']}")

# The sponsor's AccountRoot shows the fee it paid and the reserves it now covers.
# The sponsee's balance is untouched.
for node in submit_response.result["meta"]["AffectedNodes"]:
    modified = node.get("ModifiedNode", {})
    if modified.get("LedgerEntryType") != "AccountRoot":
        continue

    fields = modified["FinalFields"]
    previous = modified.get("PreviousFields", {})
    fee_paid = int(previous.get("Balance", fields["Balance"])) - int(fields["Balance"])

    if fields["Account"] == sponsor.address:
        print(f"\nSponsor fee paid: {fee_paid} drops")
        print(f"Sponsor balance:  {fields['Balance']} drops")
        print(f"Reserves sponsored (SponsoringOwnerCount): {fields.get('SponsoringOwnerCount', 0)}")
    elif fields["Account"] == sponsee.address:
        print(f"\nSponsee fee paid: {fee_paid} drops")
        print(f"Sponsee balance:  {fields['Balance']} drops")
        print(f"Sponsee owner count: {fields.get('OwnerCount', 0)}")
```

## See Also

- **Concepts:**
  - [Sponsored Fees and Reserves](/ja/docs/concepts/accounts/sponsored-fees-and-reserves)
  - [Reserves](/ja/docs/concepts/accounts/reserves)
- **Tutorials:**
  - [Sponsor a Transaction with a Pre-funded Pool](/ja/docs/tutorials/best-practices/account-management/sponsor-a-transaction-with-a-pre-funded-pool)
  - [Manage a Sponsorship Pool](/ja/docs/tutorials/best-practices/account-management/manage-a-sponsorship-pool)
  - [Transfer a Reserve Sponsorship](/ja/docs/tutorials/best-practices/account-management/transfer-a-reserve-sponsorship)
- **References:**
  - [Payment transaction](/docs/references/protocol/transactions/types/payment)
  - [DepositPreauth transaction](/docs/references/protocol/transactions/types/depositpreauth)
  - [Common Fields](/ja/docs/references/protocol/transactions/common-fields#sponsorflags-field)