♒
Aquarius Guide
  • 👋Welcome to Aquarius
  • Developers
    • Integrating with Aquarius
    • Aquarius Soroban Functions
    • Code Examples
      • Prerequisites & Basics
      • Executing Swaps Through Optimal Path
      • Executing Swaps Through Specific Pool
      • Deposit Liquidity
      • Withdraw Liquidity
      • Get Pools Info
      • Claim LP Rewards
      • Add Fees To Swap
        • Deploying a New Fee Collector
        • Executing Swaps with Provider Fees
        • Claiming & Swapping Accumulated Fees
  • Ecosystem Overview
    • 🌐What is Stellar?
      • What are Lumens (XLM)?
      • What are Anchors?
      • What are Trustlines?
      • How much are network fees on Stellar?
      • What are network reserves?
      • Where to trade Stellar assets?
    • 🧮What is Soroban?
  • AQUA tokens
    • ♒What are AQUA tokens?
      • AQUAnomics
      • AQUA Wallets
      • Where can I buy AQUA?
  • ICE
    • 🧊ICE tokens: locking AQUA and getting benefits
    • ICE boosts - how to maximize LP rewards
  • Aquarius AMMs
    • 💱What are Aquarius AMMs?
      • Pools
        • Creating a Pool
        • Deposit & Withdraw Liquidity
      • Swap
      • System limitations
        • Aquarius AMM: Limitations in Support for Fee-on-Transfer, Rebasing, and Deflationary Tokens
        • Aquarius AMM: Token Address Migration Limitations and Mitigation Strategy
  • My Aquarius
    • 👤My Aquarius
      • Main Overview
      • Balances
      • My Liquidity
      • SDEX Rewards
      • Liquidity Votes
      • Governance Votes
      • Airdrop #2
      • ICE Locks
      • Payments History
  • Aquarius AQUA Rewards
    • 🗳️Aquarius voting
      • Aquarius voting: asset Flag Restrictions
    • 🪙SDEX Rewards
    • 🤖Aquarius AMM Rewards
  • Bribes
    • 🎁What are bribes?
      • What are the advantages of protocol level bribes?
  • Aquarius Governance
    • 🧑‍⚖️Aquarius Governance: Community-Led Decision Making
  • Airdrops
    • 1️⃣The Initial Airdrop
      • Am I Eligible For the Initial Airdrop?
      • How can I see if I am eligible?
      • What are Claimable Balances?
      • How is the Initial airdrop distributed?
      • Where can I find more information?
    • 🌠Airdrop #2
      • How could I have been eligible for Airdrop #2?
      • How can I see if I am eligible?
      • When was the Airdrop #2 snapshot?
      • Were there any CEX's taking part?
      • How big was Airdrop #2?
      • How will the airdrop be distributed and for how long?
      • Could I have increased my potential reward?
      • Where can I find more information?
  • Signers Guild
    • 📜What is the signers guild?
      • What percentage of the AQUA supply will be controlled by the Signers Guild?
      • Who will be in the Signers Guild?
      • How does the Signing process work?
      • What will be expected from a guild member?
      • How can I sign up for this position?
      • What are wallets that Guild members will manage?
      • How can I learn more about this?
  • Guides
    • ❔How to use AQUA Locker tool and get ICE tokens
    • ❔How to vote for markets on Aquarius
    • How to create bribes
    • ❔How to use Aquarius Governance
      • How to make a governance vote
      • How to create a proposal
    • ❔How to earn SDEX rewards
    • ❔How to earn AMM rewards
  • Technical Documents
    • 📜Audits
    • 🪲Bug Bounties
    • 🛄Claimable Balances
    • 🗳️The Aquarius Voting Mechanism
    • 🎁SDEX v2 proposal & algorithm
    • ⏩ICE Boost Formula
  • Useful Links
    • Aquarius Home
    • Liquidity Voting
    • Liquidity Rewards
    • Aquarius Bribes
    • ICE locker
    • Aquarius Governance
    • Airdrop #2
Powered by GitBook
On this page
  • Executing Swap: Step by Step Guide
  • Complete Code Examples
  • Swap Example Transactions
  1. Developers
  2. Code Examples

Executing Swaps Through Optimal Path

Executing swaps is the most common use of Aquarius protocol. This article explains how to prepare, find the best path and execute swap.

PreviousPrerequisites & BasicsNextExecuting Swaps Through Specific Pool

Last updated 1 month ago

This is the recommended way to execute swaps with Aquarius, as it provides the best swap results. It supports swaps through multiple pools (multi-hop, up to 4 pools).

In this example we will swap XLM to AQUA with Aquarius AMM. The swap will be executed using router and will be optimized for best results. The example will also demonstrate the ability to specify either the amount of the asset being sent or the amount of the asset to be received, similar to strict-send and strict-receive behavior.

Please make sure account has established trustline for asset to receive. Otherwise, swap will fail until trustline is created. For more information please refer to.

Scroll to see the complete code.

Executing Swap: Step by Step Guide

To perform a swap, you need to follow these steps:

1. Specify user secret key, input token, output token, whether it’s a send or receive operation, and the token amount: You need to specify the input and output tokens and a single amount (in stroops) — either the amount of the token to send or to receive, depending on the selected mode.

# This account must have at least 3 XLM and a trustline to AQUA.
user_secret_key = "S..."
# XLM
token_in = Asset.native()
# AQUA
token_out = Asset("AQUA", "GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA")
# If True, the swap behaves like strict-send: the amount of the sending asset is fixed.
# If False, the swap behaves like strict-receive: the amount of the receiving asset is fixed.
is_send=True
# Amount of 1 XLM or 1 AQUA in stroops (depending on is_send)
amount = 1_0000000
const userSecretKey = 'S...';
// XLM
const tokenIn = Asset.native();
// AQUA
const tokenOut = new Asset('AQUA','GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA'); 
// if true, the swap behaves like strict-send: the amount of the sending asset is fixed.
// if false, the swap behaves like strict-receive: the amount of the receiving asset is fixed.
const isSend = true;
// amount of 1 XLM or 1 AQUA in stroops (depending on isSend)
const amount = 10000000;

2. Call the Find Path API to calculate the swap route and generate the XDR: Send a POST request to the appropriate endpoint — either /api/external/v1/find-path/ (for strict-send) or /api/external/v1/find-path-strict-receive/ (for strict-receive).

The request body must be a JSON object containing the following fields:

  • token_in_address: The address of the token you want to swap from.

  • token_out_address: The address of the token you want to swap to.

  • amount: The amount of the token to be swapped, in stroops. This represents either the input amount (for send mode) or the output amount (for receive mode), depending on the endpoint used.

def find_swap_path(base_api: str, token_in_address: str, token_out_address: str, amount: int, is_send: bool) -> (int, str):) -> (int, str):
    data = {
        'token_in_address': token_in_address,
        'token_out_address': token_out_address,
        'amount': amount
    }
    endpoint = '/find-path/' if is_send else '/find-path-strict-receive/'
    response = requests.post(f'{base_api}{endpoint}', json=data)
    swap_result = response.json()
    assert swap_result['success']
    return int(swap_result['amount']), swap_result['swap_chain_xdr']
async function findSwapPath() {
    const headers = { 'Content-Type': 'application/json' };
    const body = JSON.stringify({
        token_in_address: tokenIn.contractId(Networks.PUBLIC),
        token_out_address: tokenOut.contractId(Networks.PUBLIC),
        amount: amount.toString(),
    });

    const endpoint = isSend ? '/find-path/' : '/find-path-strict-receive/';
    const estimateResponse = await fetch(`${baseApi}${endpoint}`, { method: 'POST', body, headers });
    const estimateResult = await estimateResponse.json();

    console.log(estimateResult);
    // {
    //   success: true,
    //   swap_chain_xdr: 'AAAAEAAAAAE...SEu1QKQU3Ycwk9FM5LjU5ggGwgl5w==',
    //   pools: [ 'CDE57N6XTUPBKYYDGQMXX7E7SLNOLFY3JEQB4MULSMR2AKTSAENGX2HC' ],
    //   tokens: [
    //     'native',
    //     'AQUA:GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA'
    //   ],
    //   amount: 1724745895
    // }

    if (!estimateResult.success) {
        throw new Error('Estimate failed');
    }

    return estimateResult;
}

3. Execute the AMM Router smart contract call using the swap XDR: Use the XDR generated by the Find Path API to perform a smart contract invocation on the AMM Router. Depending on the swap type, call either the swap_chained (for strict-send) or swap_chained_strict_receive (for strict-receive) method. This XDR contains all the necessary routing and pricing logic for the optimal swap execution.

def execute_swap(
        network: str,
        soroban_rpc_server: SorobanServer,
        horizon_server: Server,
        keypair: Keypair,
        router_contract_id: str,
        token_in_address: str,
        amount: int,
        amount_with_slippage: int,
        swap_path: str,
        is_send: bool,
) -> int:
    function_name = 'swap_chained' if is_send else 'swap_chained_strict_receive'
    tx = soroban_rpc_server.prepare_transaction(
        TransactionBuilder(
            horizon_server.load_account(keypair.public_key),
            network_passphrase=network,
            base_fee=10000
        ).set_timeout(300)
        .append_invoke_contract_function_op(
            contract_id=router_contract_id,
            function_name=function_name,
            parameters=[
                scval.to_address(keypair.public_key),
                SCVal.from_xdr(swap_path),
                scval.to_address(token_in_address),
                scval.to_uint128(amount),
                scval.to_uint128(amount_with_slippage),
            ],
        )
        .build()
    )
    tx.sign(keypair)

    submit_response = horizon_server.submit_transaction(tx)

    assert submit_response['successful']

    result_meta = soroban_server.get_transaction(submit_response['id']).result_meta_xdr
    transaction_meta = TransactionMeta.from_xdr(result_meta)
    result = transaction_meta.v3.soroban_meta.return_value

    return u128_to_int(result.u128)
async function executeSwap(estimateResult) {
    const keypair = Keypair.fromSecret(userSecretKey);

    const sorobanServer = new rpc.Server(sorobanServerUrl);
    const horizonServer = new Horizon.Server(horizonServerUrl);

    // No need to generate swapsChain manually, use value received from find-path api
    const swapsChain = xdr.ScVal.fromXDR(estimateResult.swap_chain_xdr, 'base64');
    const tokenInScVal = Address.contract(StrKey.decodeContract(tokenIn.contractId(Networks.PUBLIC))).toScVal()
    const amountU128 = new XdrLargeInt('u128', amount.toFixed()).toU128();
    const amountWithSlippage = isSend ? estimateResult.amount * 0.99 : estimateResult.amount * 1.01; // slippage 1%
    const amountWithSlippageU128 = new XdrLargeInt('u128', amountWithSlippage.toFixed()).toU128();

    const account = await sorobanServer.getAccount(keypair.publicKey());
    const functionName = isSend ? 'swap_chained' : 'swap_chained_strict_receive';
    const tx = new TransactionBuilder(account, {
        fee: BASE_FEE,
        networkPassphrase: Networks.PUBLIC,
    })
        .addOperation(
            new StellarSdk.Contract(routerContractId).call(
                functionName,
                xdr.ScVal.scvAddress(Address.fromString(keypair.publicKey()).toScAddress()),
                swapsChain,
                tokenInScVal,
                amountU128,
                amountWithSlippageU128
            )
        )
        .setTimeout(TimeoutInfinite)
        .build();

    const preparedTx = await sorobanServer.prepareTransaction(tx);

    preparedTx.sign(keypair);

    const result = await horizonServer.submitTransaction(preparedTx);

    const meta = (await sorobanServer.getTransaction(result.id)).resultMetaXdr

    const returnValue = meta.v3().sorobanMeta().returnValue();

    const swapResult = u128ToInt(returnValue.value());

    console.log('Swap successful!');
    console.log(`Swapped: ${amount / 1e7} ${tokenIn.code} => ${swapResult / 1e7} ${tokenOut.code}`);
}

Complete Code Examples

This code performs a swap using Aquarius AMM on the Stellar mainnet. Depending on the is_send parameter: if is_send is True, it swaps 1 XLM for AQUA; if is_send is False, it swaps XLM for 1 AQUA.

To successfully execute the code, provide the secret key of a Stellar account with at least 3 XLM and an established trustline for AQUA.

Copy the full code Python
import requests
from stellar_sdk import scval, SorobanServer, TransactionBuilder, Network, Server, Keypair, Asset
from stellar_sdk.xdr import SCVal, TransactionMeta, UInt128Parts

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

# This account must have at least 3 XLM and a trustline to AQUA.
user_secret_key = "S....."
keypair = Keypair.from_secret(user_secret_key)

# Input and output tokens
token_in = Asset.native()  # XLM
token_out = Asset("AQUA", "GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA")

# If True, the swap behaves like strict-send: the amount of the sending asset is fixed.
# If False, the swap behaves like strict-receive: the amount of the receiving asset is fixed.
is_send=True
# Amount of 1 XLM or 1 AQUA in stroops (depending on is_send)
amount = 1_0000000

# Router contract for AMM swaps on mainnet
router_contract_id = "CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK"

# 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/v1'


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

def u128_to_int(value: UInt128Parts) -> int:
    """Convert Uint128Parts to Python int."""
    return (value.hi.uint64 << 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
    }
    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
        }
    """

    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'])
    return int(swap_result['amount']), swap_result['swap_chain_xdr']


def execute_swap(
    network: str,
    soroban_rpc_server: SorobanServer,
    horizon_server: Server,
    keypair: Keypair,
    router_contract_id: str,
    token_in_address: str,
    amount: int,
    amount_with_slippage: int,
    swap_path: str,
    is_send: bool,
) -> 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)
    
    function_name = 'swap_chained' if is_send else 'swap_chained_strict_receive'

    # 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=router_contract_id,
            function_name=function_name,
            parameters=[
                scval.to_address(keypair.public_key),
                SCVal.from_xdr(swap_path),
                scval.to_address(token_in_address),
                scval.to_uint128(amount),
                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 swap process...")
print(f"Swapping {token_in.code} for {token_out.code}...")

# 1. Find the swap path and estimated output
amount_estimated, swap_path_xdr = find_swap_path(
    base_api,
    token_in.contract_id(network),
    token_out.contract_id(network),
    amount,
    is_send,
)

# Apply 1% slippage tolerance
amount_with_slippage = int(amount_estimated * 0.99) if is_send else int(amount_estimated * 1.01)
print(f"Applying 1% slippage. Expected amount: {amount_with_slippage / 1e7}")

# 2. Execute the swap
amount = execute_swap(
    network,
    soroban_server,
    horizon_server,
    keypair,
    router_contract_id,
    token_in.contract_id(network),
    amount,
    amount_with_slippage,
    swap_path_xdr,
    is_send,
)

print("Swap completed successfully!")

if is_send:
    print(f"Amount out: {amount / 10 ** 7} {token_out.code}")  # If it's strict-send, show output amount
else:
    print(f"Amount in: {amount / 10 ** 7} {token_in.code}")  # If it's strict-receive, show input amount
Copy the full code JavaScript
const StellarSdk = require('@stellar/stellar-sdk');
const {
    xdr,
    Address,
    Asset,
    StrKey,
    XdrLargeInt,
    Networks,
    TransactionBuilder,
    rpc,
    BASE_FEE,
    TimeoutInfinite,
    Keypair,
    Horizon,
} = StellarSdk;

// =========================================
// Configuration & Setup
// =========================================

// TODO: Enter the secret key of the account executing the swap.
// The account must have at least 3 XLM and a trustline to AQUA.
const userSecretKey = 'S.....';

// Input and output tokens
const tokenIn = Asset.native();
const tokenOut = new Asset(
    'AQUA',
    'GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA'
);

// if true, the swap behaves like strict-send: the amount of the sending asset is fixed.
// if false, the swap behaves like strict-receive: the amount of the receiving asset is fixed.
const isSend = true;
// amount of 1 XLM or 1 AQUA in stroops (depending on isSend)
const amount = 10_000_000;

// Soroban and Horizon server endpoints
const horizonServerUrl = "https://horizon.stellar.org";
const sorobanServerUrl = 'https://mainnet.sorobanrpc.com';

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

// Router contract ID
const routerContractId = "CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK";

// =========================================
// Utility Function
// =========================================
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;
    }
}

// =========================================
// Functions
// =========================================
async function findSwapPath() {
    console.log("Requesting swap path from the AMM API...");
    const headers = { 'Content-Type': 'application/json' };
    const body = JSON.stringify({
        token_in_address: tokenIn.contractId(Networks.PUBLIC),
        token_out_address: tokenOut.contractId(Networks.PUBLIC),
        amount: amount.toString(),
    });

    const endpoint = isSend ? '/find-path/' : '/find-path-strict-receive/';
    const estimateResponse = await fetch(`${baseApi}${endpoint}`, { method: 'POST', body, headers });
    const estimateResult = await estimateResponse.json();

    if (!estimateResult.success) {
        throw new Error('Failed to retrieve swap path from AMM API.');
    }

    if (isSend) {
        console.log(`Swap path obtained. Estimated output amount: ${estimateResult.amount / 1e7} ${tokenOut.code}`);
    } else {
        console.log(`Swap path obtained. Estimated input amount: ${estimateResult.amount / 1e7} ${tokenIn.code}`);
    }
    
    return estimateResult;
}

async function executeSwap(estimateResult) {
    console.log("Preparing swap transaction...");
    const keypair = Keypair.fromSecret(userSecretKey);
    const sorobanServer = new rpc.Server(sorobanServerUrl);
    const horizonServer = new Horizon.Server(horizonServerUrl);

    // Construct the parameters from the estimate result
    const swapsChain = xdr.ScVal.fromXDR(estimateResult.swap_chain_xdr, 'base64');
    const tokenInScVal = Address.contract(StrKey.decodeContract(tokenIn.contractId(Networks.PUBLIC))).toScVal();
    const amountU128 = new XdrLargeInt('u128', amount.toString()).toU128();

    // Apply 1% slippage
    const amountWithSlippage = isSend ? estimateResult.amount * 0.99 : estimateResult.amount * 1.01;
    const amountWithSlippageU128 = new XdrLargeInt('u128', Math.floor(amountWithSlippage).toString()).toU128();

    console.log("Loading account from Soroban server...");
    const account = await sorobanServer.getAccount(keypair.publicKey());

    console.log("Building transaction...");
    const functionName = isSend ? 'swap_chained' : 'swap_chained_strict_receive';
    const tx = new TransactionBuilder(account, {
        fee: BASE_FEE,
        networkPassphrase: Networks.PUBLIC,
    })
        .addOperation(
            new StellarSdk.Contract(routerContractId).call(
                functionName,
                xdr.ScVal.scvAddress(Address.fromString(keypair.publicKey()).toScAddress()),
                swapsChain,
                tokenInScVal,
                amountU128,
                amountWithSlippageU128
            )
        )
        .setTimeout(TimeoutInfinite)
        .build();

    console.log("Preparing transaction on Soroban server...");
    const preparedTx = await sorobanServer.prepareTransaction(tx);

    console.log("Signing transaction...");
    preparedTx.sign(keypair);

    console.log("Submitting transaction to Horizon...");
    const result = await horizonServer.submitTransaction(preparedTx);

    if (!result) {
        throw new Error("Transaction submission failed.");
    }

    console.log("Transaction successful. Extracting results...");
    
    const meta = (await sorobanServer.getTransaction(result.id)).resultMetaXdr;
    const returnValue = meta.v3().sorobanMeta().returnValue();
    const swapResult = u128ToInt(returnValue.value());

    console.log('Swap successful!');
    
    if (isSend) {
        console.log(`Swapped: ${amount / 1e7} ${tokenIn.code} => ${swapResult / 1e7} ${tokenOut.code}`);
    } else {
        console.log(`Swapped: ${swapResult / 1e7} ${tokenIn.code} => ${amount / 1e7} ${tokenOut.code}`);
    }
}

// =========================================
// Entry Point
// =========================================
findSwapPath()
    .then(estimated => executeSwap(estimated))
    .catch(err => console.error("Error during swap process:", err));

Swap Example Transactions

Swap through 1 pool:

Swap through 2 pools:

https://stellar.expert/explorer/public/tx/c52f43d518f8cebe8ec849fd5d50c394faa036f96399d723f1f2d286e36c7b94
https://stellar.expert/explorer/public/tx/d80b317ec117d77433e268e3b544eefdaa702a3fe92169e1f793f613863de1f2
☝️
documentation on Stellar.org
here