# Claiming & Swapping Accumulated Fees

Swap fees are accumulated on the fee collector contract and should be claimed by the `operator` set on deployment of the fee collector.

Keep in mind that the fees are taken in the tokens being swapped so to claim all these tokens to the `fee_destination` address the latter must have all the trustlines.

There is a method to claim and immediately swap everything in 1 token, then settle to `fee_destination`. "Raw" claiming is also available though.

Below there are examples of these operations. Claims can be executed as frequently as necessary.

### Option 1: Claim & Immediately Swap

* **Function:** `claim_fees_and_swap(e, operator: Address, swaps_chain: Vec<…>, token: Address, out_min: u128) -> u128`
* **Effect:**
  1. Claims all of `token`.
  2. Swaps via your router along `swaps_chain`.
  3. Sends resulting tokens to `fee_destination`.
* **Returns:**\
  Final output amount in the target asset.
* **Function:** `claim_fees_and_swap(e, operator: Address, swaps_chain: Vec<…>, token: Address, out_min: u128) -> u128`
* **Effect:**
  1. Claims all of `token`.
  2. Swaps via your router along `swaps_chain`.
  3. Sends resulting tokens to `fee_destination`.
* **Returns:**\
  Final output amount in the target asset.

{% tabs %}
{% tab title="Python" %}

```python
import requests
from stellar_sdk import Asset, Keypair, Network, scval, Server, SorobanServer, TransactionBuilder
from stellar_sdk.xdr import Int128Parts, SCVal, TransactionMeta, UInt128Parts

# =========================================
# Configuration & Setup
# =========================================

# swap provider contract operator
operator_secret_key = "SA..........."
keypair = Keypair.from_secret(operator_secret_key)

# Input and output tokens
token_to_claim = Asset("AQUA", "GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA")
token_out = Asset.native()  # XLM
slippage = "0.005"  # 0.5% slippage

# swap provider contract for AMM swaps on mainnet
# IMPORTANT: Replace with your deployed contract ID. This contract address is only for test purposes.
provider_swap_contract_id = "CDJCVXFIT2UIVNLC22OWCHABTWKXUSYYLPKKBLJW67ISMIWX56YJBGLS"

# Soroban and Horizon servers
soroban_server = SorobanServer("https://mainnet.sorobanrpc.com")
horizon_server = Server("https://horizon.stellar.org/")
network = Network.PUBLIC_NETWORK_PASSPHRASE

# AQUA AMM API endpoint
base_api = 'https://amm-api.aqua.network/api/external/v2'


# =========================================
# Utility Function
# =========================================

def u128_to_int(value: UInt128Parts) -> int:
    """Convert Uint128Parts to Python int."""
    return (value.hi.uint64 << 64) + value.lo.uint64


def i128_to_int(value: Int128Parts) -> int:
    """Convert int128Parts to Python int."""
    return (value.hi.int64 << 64) + value.lo.uint64


# =========================================
# Functions
# =========================================

def find_swap_path(base_api: str, token_in_address: str, token_out_address: str, amount: int, is_send: bool) -> (int, str):
    """
    Call the Find Path API to retrieve the swap chain and estimated amount.
    """
    print("Requesting swap path from AMM API...")
    data = {
        'token_in_address': token_in_address,
        'token_out_address': token_out_address,
        'amount': amount,
        'slippage': slippage,
    }
    endpoint = '/find-path/' if is_send else '/find-path-strict-receive/'
    response = requests.post(f'{base_api}{endpoint}', json=data)
    swap_result = response.json()
    print(swap_result)
    """
        {
          'success': True,
          'swap_chain_xdr': 'AAAAEAAAAAEAAAABAAAAEAAAAAEAAAADAAAAEAAAAAEAAAACAAAAEgAAAAEltPzYWa7C+mNIQ4xImzw8EMmLbSG+T9PLMMtolT75dwAAABIAAAABKIUvaMGYSI40b7EhLtUCkFN2HMJPRTOS41OYIBsIJecAAAANAAAAILLgL8/KbJb4rVy9hOd4Snd7NtnJaiRZQCxPRYRiqrfwAAAAEgAAAAEohS9owZhIjjRvsSEu1QKQU3Ycwk9FM5LjU5ggGwgl5w==',
          'pools': [
            'CDE57N6XTUPBKYYDGQMXX7E7SLNOLFY3JEQB4MULSMR2AKTSAENGX2HC'
          ],
          'tokens': [
            'native',
            'AQUA:GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA'
          ],
          'amount': 3627808902,
          'amount_with_fee': 3627808002,
        }
    """

    if not swap_result.get('success', False):
        raise Exception("Failed to retrieve swap path from the API.")

    print("Swap path retrieved. Estimated amount:", swap_result['amount'])
    print("Estimated amount with fee:", swap_result['amount_with_fee'])
    return int(swap_result['amount_with_fee']), swap_result['swap_chain_xdr']


def get_contract_balance(
        network: str,
        soroban_rpc_server: SorobanServer,
        keypair: Keypair,
        contract_address: str,
        token_address: str,
) -> int:
    """
    Retrieves the balance of a specific token for the given account.
    """
    tx = (
        TransactionBuilder(
            source_account=soroban_rpc_server.load_account(keypair.public_key),
            network_passphrase=network,
            base_fee=10000
        )
        .set_timeout(300)
        .append_invoke_contract_function_op(
            contract_id=token_address,
            function_name="balance",
            parameters=[
                scval.to_address(contract_address),
            ],
        )
        .build()
    )
    simulation = soroban_rpc_server.simulate_transaction(tx)
    return i128_to_int(SCVal.from_xdr(simulation.results[0].xdr).i128)


def execute_claim_with_swap(
        network: str,
        soroban_rpc_server: SorobanServer,
        horizon_server: Server,
        keypair: Keypair,
        contract_id: str,
        token_in_address: str,
        amount_with_slippage: int,
        swap_path: str,
) -> int:
    """
    Executes the chained swap transaction on Soroban and returns the final amount out.
    """
    print("Preparing and building swap transaction...")
    source_account = horizon_server.load_account(keypair.public_key)

    # Build the transaction to invoke `swap_chained`
    tx = (
        TransactionBuilder(
            source_account=source_account,
            network_passphrase=network,
            base_fee=10000
        )
        .set_timeout(300)
        .append_invoke_contract_function_op(
            contract_id=contract_id,
            function_name='claim_fees_and_swap',
            parameters=[
                scval.to_address(keypair.public_key),
                SCVal.from_xdr(swap_path),
                scval.to_address(token_in_address),
                scval.to_uint128(amount_with_slippage),
            ],
        )
        .build()
    )

    # Prepare transaction to get Soroban-specific data (footprint, etc.)
    print("Preparing transaction on Soroban...")
    prepared_tx = soroban_rpc_server.prepare_transaction(tx)

    # Sign the prepared transaction
    print("Signing transaction...")
    prepared_tx.sign(keypair)

    # Submit the transaction to Horizon
    print("Submitting transaction to Horizon...")
    submit_response = horizon_server.submit_transaction(prepared_tx)

    if not submit_response.get('successful', False):
        raise Exception("Transaction failed: " + str(submit_response))

    print("Transaction submitted successfully. Fetching result...")

    # Get the transaction result from Soroban server to access Soroban metadata
    tx_info = soroban_server.get_transaction(submit_response['id'])
    if not tx_info or not tx_info.result_meta_xdr:
        raise Exception("No transaction metadata found.")

    # Extract the result from the Soroban metadata
    transaction_meta = TransactionMeta.from_xdr(tx_info.result_meta_xdr)
    return_val = transaction_meta.v3.soroban_meta.return_value
    final_amount = u128_to_int(return_val.u128)
    print("Swap executed successfully.")
    return final_amount


# =========================================
# Entry Point
# =========================================

print("Starting claim process...")
print(f"Swapping {token_to_claim.code} for {token_out.code}...")

# 0. Get the balance of the token to claim
amount = get_contract_balance(
    network,
    soroban_server,
    keypair,
    provider_swap_contract_id,
    token_to_claim.contract_id(network),
)

# 1. Find the swap path and estimated output
amount_with_slippage, swap_path_xdr = find_swap_path(
    base_api,
    token_to_claim.contract_id(network),
    token_out.contract_id(network),
    amount,
    True,
)

# 2. Execute the claim
amount = execute_claim_with_swap(
    network,
    soroban_server,
    horizon_server,
    keypair,
    provider_swap_contract_id,
    token_to_claim.contract_id(network),
    amount_with_slippage,
    swap_path_xdr,
)

print("Claim with swap completed successfully!")
print(f"Amount out: {amount / 10 ** 7} {token_out.code}")
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const {
    Horizon,
    Keypair,
    Asset,
    Networks,
    TransactionBuilder,
    nativeToScVal,
    Address,
    Operation,
    xdr,
    rpc
} = require('@stellar/stellar-sdk');

// =========================================
// Configuration
// =========================================

// swap provider contract operator
const operatorSecretKey = 'SA...........'; // Replace with actual secret
const keypair = Keypair.fromSecret(operatorSecretKey);
const publicKey = keypair.publicKey();

// Input and output tokens
const tokenToClaim = new Asset('AQUA', 'GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA');
const tokenOut = Asset.native(); // XLM
const slippage = '0.005';


// swap provider contract for AMM swaps on mainnet
// IMPORTANT: Replace with your deployed contract ID. This contract address is only for test purposes.
const providerSwapContractId = 'CDJCVXFIT2UIVNLC22OWCHABTWKXUSYYLPKKBLJW67ISMIWX56YJBGLS';

// Soroban and Horizon servers
const sorobanServer = new rpc.Server('https://mainnet.sorobanrpc.com');
const horizonServer = new Horizon.Server('https://horizon.stellar.org');
const networkPassphrase = Networks.PUBLIC;

// AQUA AMM API endpoint
const baseApi = 'https://amm-api.aqua.network/api/external/v2';

// =========================================
// Helpers
// =========================================

function u128ToInt(value) {
    const result = (BigInt(value.hi()._value) << 64n) + BigInt(value.lo()._value);
    if (result <= BigInt(Number.MAX_SAFE_INTEGER)) {
        return Number(result);
    } else {
        console.warn("Value exceeds JS safe integer range");
        return result;
    }
}

// Retrieves the balance of a specific token for the given account.
async function getContractTokenBalance(tokenAddress) {
    const sourceAccount = await horizonServer.loadAccount(publicKey);
    const tx = new TransactionBuilder(sourceAccount, {
        fee: '10000',
        networkPassphrase
    })
        .setTimeout(300)
        .addOperation(
            Operation.invokeContractFunction({
                contract: tokenAddress,
                function: 'balance',
                args: [nativeToScVal(new Address(providerSwapContractId))],
            })
        )
        .build();

    const sim = await sorobanServer.simulateTransaction(tx);
    return u128ToInt(sim.result.retval.value());
}

// Call the Find Path API to retrieve the swap chain and estimated amount.
async function findSwapPath(tokenIn, tokenOut, amount) {
    const endpoint = `${baseApi}/find-path/`;
    const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            token_in_address: tokenIn,
            token_out_address: tokenOut,
            amount,
            slippage
        })
    });

    const data = await response.json();
    if (!data.success) throw new Error('Failed to retrieve swap path');
    return {
        amountWithFee: data.amount_with_fee,
        swapPathXDR: data.swap_chain_xdr
    };
}

// Executes the chained swap transaction on Soroban and returns the final amount out.
async function executeClaimAndSwap(tokenIn, amountWithSlippage, swapPathXDR) {
    const account = await horizonServer.loadAccount(publicKey);
    
    // Build the transaction to invoke `swap_chained`
    const tx = new TransactionBuilder(account, {
        fee: '10000',
        networkPassphrase
    })
        .setTimeout(300)
        .addOperation(
            Operation.invokeContractFunction({
                contract: providerSwapContractId,
                function: 'claim_fees_and_swap',
                args: [
                    nativeToScVal(new Address(publicKey)),
                    xdr.ScVal.fromXDR(swapPathXDR, 'base64'),
                    nativeToScVal(new Address(tokenIn)),
                    nativeToScVal(amountWithSlippage, { type: 'u128' })
                ]
            })
        )
        .build();

    // Prepare transaction to get Soroban-specific data (footprint, etc.)
    const preparedTx = await sorobanServer.prepareTransaction(tx);
    // Sign the prepared transaction
    preparedTx.sign(keypair);

    // Submit the transaction to Horizon
    const submitResponse = await horizonServer.submitTransaction(preparedTx);
    if (!submitResponse.successful) {
        throw new Error('Transaction submission failed: ' + JSON.stringify(submitResponse));
    }

    // Get the transaction result from Soroban server to access Soroban metadata
    const txInfo = await sorobanServer.getTransaction(submitResponse.hash);
    const metaXdr = txInfo.resultMetaXdr;
    if (!metaXdr) throw new Error('No metadata');

    // Extract the result from the Soroban metadata
    const returnVal = metaXdr.v3().sorobanMeta().returnValue();
    return u128ToInt(returnVal.u128());
}

// =========================================
// Main
// =========================================

(async () => {
    try {
        console.log(`Swapping ${tokenToClaim.code} for ${tokenOut.code}...`);

        const tokenInAddress = tokenToClaim.contractId(networkPassphrase);
        const tokenOutAddress = tokenOut.contractId(networkPassphrase);

        // 1. Get the balance of the token to claim
        const balance = await getContractTokenBalance(tokenInAddress);
        console.log(`Token balance to claim: ${balance / 1e7} ${tokenToClaim.code}`);

        // 2. Find the swap path and estimated output
        const { amountWithFee, swapPathXDR } = await findSwapPath(tokenInAddress, tokenOutAddress, balance);
        console.log(`Swap path found. Final amount with slippage: ${amountWithFee / 1e7}`);

        // 3. Execute the claim
        const resultAmount = await executeClaimAndSwap(tokenInAddress, amountWithFee, swapPathXDR);
        console.log(`Claim + swap successful. Amount received: ${resultAmount / 1e7} ${tokenOut.code}`);
    } catch (err) {
        console.error('Error:', err.message || err);
    }
})();
```

{% endtab %}
{% endtabs %}

### Option 2: Claim Raw Fees

* **Function:** `claim_fees(e, operator: Address, token: Address) -> u128`
* **Effect:** Transfers entire `token` balance from the contract to `fee_destination`.

{% tabs %}
{% tab title="Python" %}

```python
from stellar_sdk import Asset, Keypair, Network, scval, Server, SorobanServer, TransactionBuilder
from stellar_sdk.xdr import Int128Parts, TransactionMeta, UInt128Parts

# =========================================
# Configuration & Setup
# =========================================

# swap provider contract operator
operator_secret_key = "SA..........."
keypair = Keypair.from_secret(operator_secret_key)

# Input and output tokens
token_to_claim = Asset.native()

# swap provider contract for AMM swaps on mainnet
# IMPORTANT: Replace with your deployed contract ID. This contract address is only for test purposes.
provider_swap_contract_id = "CDJCVXFIT2UIVNLC22OWCHABTWKXUSYYLPKKBLJW67ISMIWX56YJBGLS"

# Soroban and Horizon servers
soroban_server = SorobanServer("https://mainnet.sorobanrpc.com")
horizon_server = Server("https://horizon.stellar.org/")
network = Network.PUBLIC_NETWORK_PASSPHRASE


# =========================================
# Utility Function
# =========================================

def u128_to_int(value: UInt128Parts) -> int:
    """Convert Uint128Parts to Python int."""
    return (value.hi.uint64 << 64) + value.lo.uint64


def i128_to_int(value: Int128Parts) -> int:
    """Convert int128Parts to Python int."""
    return (value.hi.int64 << 64) + value.lo.uint64


# =========================================
# Entry Point
# =========================================

print("Starting claim process...")
print(f"Claiming {token_to_claim.code}...")

tx = (
    TransactionBuilder(
        source_account=horizon_server.load_account(keypair.public_key),
        network_passphrase=network,
        base_fee=10000
    )
    .set_timeout(300)
    .append_invoke_contract_function_op(
        contract_id=provider_swap_contract_id,
        function_name='claim_fees',
        parameters=[
            scval.to_address(keypair.public_key),
            scval.to_address(token_to_claim.contract_id(network)),
        ],
    )
    .build()
)

# Prepare transaction to get Soroban-specific data (footprint, etc.)
print("Preparing transaction on Soroban...")
prepared_tx = soroban_server.prepare_transaction(tx)

# Sign the prepared transaction
print("Signing transaction...")
prepared_tx.sign(keypair)

# Submit the transaction to Horizon
print("Submitting transaction to Horizon...")
submit_response = horizon_server.submit_transaction(prepared_tx)

if not submit_response.get('successful', False):
    raise Exception("Transaction failed: " + str(submit_response))

print("Transaction submitted successfully. Fetching result...")

# Get the transaction result from Soroban server to access Soroban metadata
tx_info = soroban_server.get_transaction(submit_response['id'])
if not tx_info or not tx_info.result_meta_xdr:
    raise Exception("No transaction metadata found.")

# Extract the result from the Soroban metadata
transaction_meta = TransactionMeta.from_xdr(tx_info.result_meta_xdr)
return_val = transaction_meta.v3.soroban_meta.return_value
final_amount = u128_to_int(return_val.u128)

print("Claim completed successfully!")
print(f"Amount out: {final_amount / 10 ** 7} {token_to_claim.code}")
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const {
    Horizon,
    Keypair,
    Asset,
    Networks,
    TransactionBuilder,
    nativeToScVal,
    Address,
    Operation,
    xdr,
    rpc
} = require('@stellar/stellar-sdk');

// =========================================
// Config & Setup
// =========================================

const operatorSecretKey = 'SA...........'; // your real key
const keypair = Keypair.fromSecret(operatorSecretKey);

const tokenToClaim = Asset.native();

// swap provider contract for AMM swaps on mainnet
// IMPORTANT: Replace with your deployed contract ID. This contract address is only for test purposes.
const providerSwapContractId = 'CDJCVXFIT2UIVNLC22OWCHABTWKXUSYYLPKKBLJW67ISMIWX56YJBGLS';

const sorobanServer = new rpc.Server('https://mainnet.sorobanrpc.com');
const horizonServer = new Horizon.Server('https://horizon.stellar.org');
const networkPassphrase = Networks.PUBLIC;

// =========================================
// Helpers
// =========================================

function u128ToInt(value) {
    const result = (BigInt(value.hi()._value) << 64n) + BigInt(value.lo()._value);
    if (result <= BigInt(Number.MAX_SAFE_INTEGER)) {
        return Number(result);
    } else {
        console.warn("Value exceeds JavaScript's safe integer range");
        return null;
    }
}

// =========================================
// Main
// =========================================

(async () => {
    try {
        console.log(`Claiming ${tokenToClaim.code} fees...`);

        const account = await horizonServer.loadAccount(keypair.publicKey());

        // Build transaction
        const tx = new TransactionBuilder(account, {
            fee: '10000',
            networkPassphrase
        })
            .setTimeout(300)
            .addOperation(
                Operation.invokeContractFunction({
                    contract: providerSwapContractId,
                    function: 'claim_fees',
                    args: [
                        nativeToScVal(new Address(keypair.publicKey())),
                        nativeToScVal(new Address(tokenToClaim.contractId(networkPassphrase)))
                    ],
                })
            )
            .build();

        // Prepare transaction to get Soroban-specific data (footprint, etc.)
        console.log('Preparing transaction...');
        const preparedTx = await sorobanServer.prepareTransaction(tx);

        // Sign the prepared transaction
        console.log('Signing...');
        preparedTx.sign(keypair);

        // Submit the transaction to Horizon
        console.log('Submitting...');
        const submitResponse = await horizonServer.submitTransaction(preparedTx);

        if (!submitResponse.successful) {
            throw new Error('Transaction failed: ' + JSON.stringify(submitResponse));
        }

        console.log('Transaction submitted. Fetching result...');

        // Get the transaction result from Soroban server to access Soroban metadata
        const txInfo = await sorobanServer.getTransaction(submitResponse.hash);
        const metaXdr = txInfo.resultMetaXdr;

        if (!metaXdr) {
            throw new Error('No result metadata.');
        }

        // Extract the result from the Soroban metadata
        const returnVal = metaXdr.v3().sorobanMeta().returnValue();
        const u128 = returnVal.u128();
        const finalAmount = u128ToInt(u128);

        console.log('Claim successful!');
        console.log(`Amount claimed: ${finalAmount / 1e7} ${tokenToClaim.code}`);
    } catch (err) {
        console.error('Error:', err.message || err);
    }
})();

```

{% endtab %}
{% endtabs %}
