♒
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
  1. Developers
  2. Code Examples

Executing Swaps Through Specific Pool

This article explains how to prepare and execute swap through specific pool.

PreviousExecuting Swaps Through Optimal PathNextDeposit Liquidity

Last updated 5 months ago

In this example we will swap 1 XLM to AQUA with Aquarius AMM. The swap will be executed by calling corresponding method of specific liquidity pool.

Direct swap through specific pool is good for contract sub-invocations as it requires less resources than chained path swap. But direct swap may lead to less optimal result.

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. Identify pool address: This part was covered earlier, please check .

2. Specify user secret key, pool address, amount in, input and output token indices: You need to specify pool address, the amount of the input token you want to swap in stroops and swap direction by in and out tokens.

# Your Secret Key (keep it secure!)
user_secret_key = "S......."
# XLM/AQUA pool address
pool_address = "CCY2PXGMKNQHO7WNYXEWX76L2C5BH3JUW3RCATGUYKY7QQTRILBZIFWV"

# Amount in (0.1 XLM), in stroops (1 XLM = 10^7 stroops)
amount_in = 1000000
# tokens are [XLM, AQUA], so XLM index is 0
in_idx = 0
out_idx = 1
# slippage percent
slippage_percent = 1
// Your Secret Key (keep it secure!)
const userSecretKey = "S.......";
// XLM/AQUA pool address
const poolAddress = "CCY2PXGMKNQHO7WNYXEWX76L2C5BH3JUW3RCATGUYKY7QQTRILBZIFWV";
// 0.1 XLM in stroops
const amountIn = 1000000;
// tokens are [XLM, AQUA], so XLM index is 0
const inIdx = 0;
const outIdx = 1;
// slippage percent
const slippagePercent = 1;

3. Calculate minimum amount of token to receive: This can be achieved by simulating estimate_swap pool method.

def estimate_swap():
    print("Estimating swap amount...")
    soroban_server = SorobanServer(soroban_server_url)
    keypair = Keypair.from_secret(user_secret_key)

    source_account = soroban_server.load_account(keypair.public_key)
    tx = (
        TransactionBuilder(
            source_account=source_account,
            network_passphrase=Network.PUBLIC_NETWORK_PASSPHRASE,
            base_fee=100
        )
        .append_invoke_contract_function_op(
            contract_id=pool_address,
            function_name="estimate_swap",
            parameters=[
                scval.to_uint32(in_idx),
                scval.to_uint32(out_idx),
                scval.to_uint128(amount_in),
            ]
        )
        .set_timeout(300)
        .build()
    )

    print("Simulating 'estimate_swap' transaction...")
    sim_result = soroban_server.simulate_transaction(tx)
    if not sim_result or sim_result.error:
        print("Simulation failed.", sim_result.error if sim_result else "")
        return math.nan

    retval = xdr.SCVal.from_xdr(sim_result.results[0].xdr)
    estimated = u128_to_int(retval.u128)
    print(f"Estimated result: {estimated / 1e7}")
    return estimated
async function estimateSwap() {
    const sorobanServer = new rpc.Server(sorobanServerUrl);

    const amount = new XdrLargeInt("u128", amountIn.toFixed()).toU128();

    const inIdxSCVal = xdr.ScVal.scvU32(inIdx);
    const outIdxSCVal = xdr.ScVal.scvU32(outIdx);

    const keypair = Keypair.fromSecret(userSecretKey);
    // Load the user account information from Soroban server
    const account = await sorobanServer.getAccount(keypair.publicKey());

    const contract = new Contract(poolAddress);

    // Build the deposit transaction
    const tx = new TransactionBuilder(account, {
        fee: BASE_FEE,
        networkPassphrase: Networks.PUBLIC,
    })
        // Append the invoke_contract_function operation for deposit
        .addOperation(
            contract.call(
                "estimate_swap",
                inIdxSCVal,
                outIdxSCVal,
                amount,
            ),
        )
        .setTimeout(TimeoutInfinite)
        .build();

    // Simulate the result
    const simulateResult = await sorobanServer.simulateTransaction(tx);

    if (!simulateResult.result) {
        console.log(simulateResult.error);
        console.log("Unable to simulate transaction");
        return NaN;
    }

    const result = u128ToInt(simulateResult.result.retval.value());

    console.log(`Estimated result: ${result}`);

    return result;
}

4. Perform swap operation by submitting corresponding transaction.

def execute_swap():
    print("Executing swap...")
    soroban_server = SorobanServer(soroban_server_url)
    horizon_server = Server(horizon_server_url)
    keypair = Keypair.from_secret(user_secret_key)

    estimated_result = estimate_swap()
    if math.isnan(estimated_result):
        print("Estimation failed. Cannot proceed with swap.")
        return

    # Calculate minimum out after slippage
    slippage_coefficient = (100 - slippage_percent) / 100.0
    minimum_out = math.floor(estimated_result * slippage_coefficient)

    # Build swap transaction
    source_account = soroban_server.load_account(keypair.public_key)
    tx = (
        TransactionBuilder(
            source_account=source_account,
            network_passphrase=Network.PUBLIC_NETWORK_PASSPHRASE,
            base_fee=100
        )
        .append_invoke_contract_function_op(
            contract_id=pool_address,
            function_name="swap",
            parameters=[
                scval.to_address(keypair.public_key),
                scval.to_uint32(in_idx),
                scval.to_uint32(out_idx),
                scval.to_uint128(amount_in),
                scval.to_uint128(minimum_out),
            ]
        )
        .set_timeout(300)
        .build()
    )

    print("Preparing transaction for submission...")
    prepared_tx = soroban_server.prepare_transaction(tx)
    prepared_tx.sign(keypair)

    print("Submitting transaction to Horizon...")
    response = horizon_server.submit_transaction(prepared_tx)
    if 'result_meta_xdr' not in response:
        print("Transaction failed.")
        return

    meta = xdr.TransactionMeta.from_xdr(response['result_meta_xdr'])
    if meta.v3 and meta.v3.soroban_meta:
        out_value = u128_to_int(meta.v3.soroban_meta.return_value.u128)
        print("Swap successful!")
        print(f"Received token out: {out_value / 1e7}")
    else:
        print("No result returned by the contract.")
async function executeSwap() {
    const sorobanServer = new rpc.Server(sorobanServerUrl);
    const horizonServer = new Horizon.Server(horizonServerUrl);

    const amount = new XdrLargeInt("u128", amountIn.toFixed()).toU128();

    const slippageCoefficient = (100 - slippagePercent) / 100;
    const estimatedResult = await estimateSwap();
    const estimateWithSlippage = Math.floor(estimatedResult * slippageCoefficient);
    const minimumOut = new XdrLargeInt("u128", estimateWithSlippage.toFixed()).toU128();

    const inIdxSCVal = xdr.ScVal.scvU32(inIdx);
    const outIdxSCVal = xdr.ScVal.scvU32(outIdx);

    const keypair = Keypair.fromSecret(userSecretKey);
    // Load the user account information from Soroban server
    const account = await sorobanServer.getAccount(keypair.publicKey());

    const contract = new Contract(poolAddress);

    // Build the swap transaction
    const tx = new TransactionBuilder(account, {
        fee: BASE_FEE,
        networkPassphrase: Networks.PUBLIC,
    })
        // Append the invoke_contract_function operation for swap
        .addOperation(
            contract.call(
                "swap",
                xdr.ScVal.scvAddress(
                    Address.fromString(keypair.publicKey()).toScAddress(),
                ),
                inIdxSCVal,
                outIdxSCVal,
                amount,
                minimumOut,
            ),
        )
        .setTimeout(TimeoutInfinite)
        .build();

    // Prepare and sign the transaction
    const preparedTx = await sorobanServer.prepareTransaction(tx);
    preparedTx.sign(keypair);

    // Submit the transaction to the Horizon server
    const result = await horizonServer.submitTransaction(preparedTx);

    // Parse the transaction metadata to extract results
    const meta = (await sorobanServer.getTransaction(result.id)).resultMetaXdr;
    const returnValue = meta.v3().sorobanMeta().returnValue();

    // Extract swapped amount
    const outValue = u128ToInt(returnValue.value());

    console.log("Swap successful!");
    console.log(`Received token out: ${outValue / 1e7}`);
}

Complete Code Examples

This code swaps 1 XLM to AQUA with Aquarius AMM on mainnet.

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 JavaScript
const StellarSdk = require("@stellar/stellar-sdk");
const {
    Address,
    Contract,
    TransactionBuilder,
    rpc,
    Horizon,
    BASE_FEE,
    Networks,
    xdr,
    TimeoutInfinite,
    XdrLargeInt,
    Keypair,
} = StellarSdk;

// Step 1. Specify user secret key, pool address, amount in and direction

// User secret key (ensure this is kept secure)
const userSecretKey = "S.......";
// XLM/AQUA pool address
const poolAddress = "CCY2PXGMKNQHO7WNYXEWX76L2C5BH3JUW3RCATGUYKY7QQTRILBZIFWV";
// 0.1 XLM in stroops
const amountIn = 1000000;
// tokens are [XLM, AQUA], so XLM index is 0
const inIdx = 0;
const outIdx = 1;
// slippage percent
const slippagePercent = 1;

// ==========================
// Configuration Variables
// ==========================

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

// ==========================
// Utility Functions
// ==========================

function u128ToInt(value) {
    /**
     * Converts UInt128Parts from Stellar's XDR to a JavaScript number.
     *
     * @param {Object} value - UInt128Parts object from Stellar SDK, with `hi` and `lo` properties.
     * @returns {number|null} Corresponding JavaScript number, or null if the number is too large.
     */
    const result =
        (BigInt(value.hi()._value) << 64n) + BigInt(value.lo()._value);

    // Check if the result is within the safe integer range for JavaScript numbers
    if (result <= BigInt(Number.MAX_SAFE_INTEGER)) {
        return Number(result);
    } else {
        console.warn("Value exceeds JavaScript's safe integer range");
        return null;
    }
}

// ==========================
// Swap Function
// ==========================

async function estimateSwap() {
    const sorobanServer = new rpc.Server(sorobanServerUrl);

    const amount = new XdrLargeInt("u128", amountIn.toFixed()).toU128();

    const inIdxSCVal = xdr.ScVal.scvU32(inIdx);
    const outIdxSCVal = xdr.ScVal.scvU32(outIdx);

    const keypair = Keypair.fromSecret(userSecretKey);
    // Load the user account information from Soroban server
    const account = await sorobanServer.getAccount(keypair.publicKey());

    const contract = new Contract(poolAddress);

    // Build the deposit transaction
    const tx = new TransactionBuilder(account, {
        fee: BASE_FEE,
        networkPassphrase: Networks.PUBLIC,
    })
        // Append the invoke_contract_function operation for deposit
        .addOperation(
            contract.call(
                "estimate_swap",
                inIdxSCVal,
                outIdxSCVal,
                amount,
            ),
        )
        .setTimeout(TimeoutInfinite)
        .build();

    // Simulate the result
    const simulateResult = await sorobanServer.simulateTransaction(tx);

    if (!simulateResult.result) {
        console.log(simulateResult.error);
        console.log("Unable to simulate transaction");
        return NaN;
    }

    const result = u128ToInt(simulateResult.result.retval.value());

    console.log(`Estimated result: ${result}`);

    return result;
}

// Step 3. Make a contract call to the pool's "swap" method.
async function executeSwap() {
    const sorobanServer = new rpc.Server(sorobanServerUrl);
    const horizonServer = new Horizon.Server(horizonServerUrl);

    const amount = new XdrLargeInt("u128", amountIn.toFixed()).toU128();

    const slippageCoefficient = (100 - slippagePercent) / 100;
    const estimatedResult = await estimateSwap();
    const estimateWithSlippage = Math.floor(estimatedResult * slippageCoefficient);
    const minimumOut = new XdrLargeInt("u128", estimateWithSlippage.toFixed()).toU128();

    const inIdxSCVal = xdr.ScVal.scvU32(inIdx);
    const outIdxSCVal = xdr.ScVal.scvU32(outIdx);

    const keypair = Keypair.fromSecret(userSecretKey);
    // Load the user account information from Soroban server
    const account = await sorobanServer.getAccount(keypair.publicKey());

    const contract = new Contract(poolAddress);

    // Build the swap transaction
    const tx = new TransactionBuilder(account, {
        fee: BASE_FEE,
        networkPassphrase: Networks.PUBLIC,
    })
        // Append the invoke_contract_function operation for swap
        .addOperation(
            contract.call(
                "swap",
                xdr.ScVal.scvAddress(
                    Address.fromString(keypair.publicKey()).toScAddress(),
                ),
                inIdxSCVal,
                outIdxSCVal,
                amount,
                minimumOut,
            ),
        )
        .setTimeout(TimeoutInfinite)
        .build();

    // Prepare and sign the transaction
    const preparedTx = await sorobanServer.prepareTransaction(tx);
    preparedTx.sign(keypair);

    // Submit the transaction to the Horizon server
    const result = await horizonServer.submitTransaction(preparedTx);

    // Parse the transaction metadata to extract results
    const meta = (await sorobanServer.getTransaction(result.id)).resultMetaXdr;
    const returnValue = meta.v3().sorobanMeta().returnValue();

    // Extract swapped amount
    const outValue = u128ToInt(returnValue.value());

    console.log("Swap successful!");
    console.log(`Received token out: ${outValue / 1e7}`);
}

// ==========================
// Entry Point
// ==========================
executeSwap();
Copy the full code Python
from stellar_sdk import (
    Keypair,
    TransactionBuilder,
    Network,
    Server,
    xdr,
    scval,
    SorobanServer,
)
import math

# =========================================
# Configuration and Input Variables
# =========================================

user_secret_key = "S......"  # Keep this secret
pool_address = "CCY2PXGMKNQHO7WNYXEWX76L2C5BH3JUW3RCATGUYKY7QQTRILBZIFWV"

# 0.1 XLM in stroops (1 XLM = 10^7 stroops)
amount_in = 1000000

# Token indices: [XLM, AQUA]
in_idx = 0
out_idx = 1

# Slippage in percent
slippage_percent = 1

# Servers
soroban_server_url = "https://mainnet.sorobanrpc.com"
horizon_server_url = "https://horizon.stellar.org"


# =========================================
# Utility Functions
# =========================================
def u128_to_int(parts: xdr.UInt128Parts) -> int:
    """Convert Uint128Parts to Python int."""
    return (parts.hi.uint64 << 64) + parts.lo.uint64


# =========================================
# Soroban Functions
# =========================================
def estimate_swap():
    print("Estimating swap amount...")
    soroban_server = SorobanServer(soroban_server_url)
    keypair = Keypair.from_secret(user_secret_key)

    source_account = soroban_server.load_account(keypair.public_key)
    tx = (
        TransactionBuilder(
            source_account=source_account,
            network_passphrase=Network.PUBLIC_NETWORK_PASSPHRASE,
            base_fee=100
        )
        .append_invoke_contract_function_op(
            contract_id=pool_address,
            function_name="estimate_swap",
            parameters=[
                scval.to_uint32(in_idx),
                scval.to_uint32(out_idx),
                scval.to_uint128(amount_in),
            ]
        )
        .set_timeout(300)
        .build()
    )

    print("Simulating 'estimate_swap' transaction...")
    sim_result = soroban_server.simulate_transaction(tx)
    if not sim_result or sim_result.error:
        print("Simulation failed.", sim_result.error if sim_result else "")
        return math.nan

    retval = xdr.SCVal.from_xdr(sim_result.results[0].xdr)
    estimated = u128_to_int(retval.u128)
    print(f"Estimated result: {estimated / 1e7}")
    return estimated


def execute_swap():
    print("Executing swap...")
    soroban_server = SorobanServer(soroban_server_url)
    horizon_server = Server(horizon_server_url)
    keypair = Keypair.from_secret(user_secret_key)

    estimated_result = estimate_swap()
    if math.isnan(estimated_result):
        print("Estimation failed. Cannot proceed with swap.")
        return

    # Calculate minimum out after slippage
    slippage_coefficient = (100 - slippage_percent) / 100.0
    minimum_out = math.floor(estimated_result * slippage_coefficient)

    # Build swap transaction
    source_account = soroban_server.load_account(keypair.public_key)
    tx = (
        TransactionBuilder(
            source_account=source_account,
            network_passphrase=Network.PUBLIC_NETWORK_PASSPHRASE,
            base_fee=100
        )
        .append_invoke_contract_function_op(
            contract_id=pool_address,
            function_name="swap",
            parameters=[
                scval.to_address(keypair.public_key),
                scval.to_uint32(in_idx),
                scval.to_uint32(out_idx),
                scval.to_uint128(amount_in),
                scval.to_uint128(minimum_out),
            ]
        )
        .set_timeout(300)
        .build()
    )

    print("Preparing transaction for submission...")
    prepared_tx = soroban_server.prepare_transaction(tx)
    prepared_tx.sign(keypair)

    print("Submitting transaction to Horizon...")
    response = horizon_server.submit_transaction(prepared_tx)
    if 'result_meta_xdr' not in response:
        print("Transaction failed.")
        return

    meta = xdr.TransactionMeta.from_xdr(response['result_meta_xdr'])
    if meta.v3 and meta.v3.soroban_meta:
        out_value = u128_to_int(meta.v3.soroban_meta.return_value.u128)
        print("Swap successful!")
        print(f"Received token out: {out_value / 1e7}")
    else:
        print("No result returned by the contract.")


# =========================================
# Entry Point
# =========================================
if __name__ == "__main__":
    execute_swap()
☝️
documentation on Stellar.org
here
corresponding article