Withdraw Liquidity

Example of code that withdraw liquidity into Aquarius pool

# withdraw.py

from typing import List

from stellar_sdk import Address, Keypair, scval, SorobanServer, xdr, TransactionBuilder, Server
from stellar_sdk.xdr import UInt128Parts

# ==========================
# Configuration Variables
# ==========================

# Soroban and Horizon server RPC endpoints
SOROBAN_SERVER_RPC = 'https://soroban-testnet.stellar.org:443/'
HORIZON_SERVER = 'https://horizon-testnet.stellar.org'

# Distributor's secret key (ensure this is kept secure)
SECRET = 'SCJF..........KTXQ'

# Contract IDs for the router and tokens
ROUTER_CONTRACT_ID = 'CDMSJQ4TPCTAYDRYN46FVMYIWV2A4ZTHCWWIN2NW3QZIFPJWBBEGDKDY'
TOKEN_A_CONTRACT_ID = 'CAZRY5GSFBFXD7H6GAFBA5YGYQTDXU4QKWKMYFWBAZFUCURN3WKX6LF5'
TOKEN_B_CONTRACT_ID = 'CBL6KD2LFMLAUKFFWNNXWOXFN73GAXLEA4WMJRLQ5L76DMYTM3KWQVJN'

# Stellar network passphrase and known pool hash
NETWORK_PASSPHRASE = 'Test SDF Network ; September 2015'
POOL_HASH = '8328b65c4d32886fb1876be01b4ad13ef5ff14719d7c735d4060da683315473d'


# ==========================
# Utility Functions
# ==========================

def order_token_ids(tokens: List[xdr.SCVal]) -> List[xdr.SCVal]:
   """
   Orders token IDs based on their contract ID to maintain consistency.

   Args:
       tokens (List[xdr.SCVal]): List of token addresses as SCVal objects.

   Returns:
       List[xdr.SCVal]: Ordered list of token SCVal objects.
   """
   return sorted(tokens, key=lambda token: int(token.address.contract_id.hash.hex(), 16))


def u128_to_int(value: UInt128Parts) -> int:
   """
   Converts UInt128Parts from Stellar's XDR to a Python integer.

   Args:
       value (UInt128Parts): UInt128Parts object from Stellar SDK.

   Returns:
       int: Corresponding Python integer.
   """
   return int(value.hi.uint64 << 64) + value.lo.uint64


# ==========================
# Withdraw Function
# ==========================

def execute_withdraw(pool_hash: str):
   """
   Executes the withdraw operation from a specified AMM pool.

   Args:
       pool_hash (str): The known hash of the target AMM pool (32-byte hex string).
   """
   # Initialize Soroban and Horizon servers
   server = SorobanServer(SOROBAN_SERVER_RPC)
   horizon_server = Server(HORIZON_SERVER)

   # Load distributor's keypair using the secret key
   keypair = Keypair.from_secret(SECRET)

   print(f"Initiating withdrawal using router contract ID: {ROUTER_CONTRACT_ID}")

   # Order token IDs to ensure consistency
   token_a = scval.to_address(TOKEN_A_CONTRACT_ID)
   token_b = scval.to_address(TOKEN_B_CONTRACT_ID)
   ordered_tokens = order_token_ids([token_a, token_b])
   token_a, token_b = ordered_tokens

   # Define the amount of liquidity shares to redeem
   # For example, redeeming all shares. Adjust as necessary.
   # You may need to query your current share balance before deciding the amount to withdraw.
   # Here, we assume you want to withdraw a specific amount.
   SHARES_TO_REDEEM = 5_0000000  # Example: 5 shares with 7 decimal places

   print(f"Withdrawing from pool {POOL_HASH}")

   # Load the distributor's account information from Soroban server
   account = server.load_account(keypair.public_key)

   # Build the withdraw transaction
   tx_builder = TransactionBuilder(
       source_account=account,
       network_passphrase=NETWORK_PASSPHRASE,
       base_fee=1000000  # Set base fee; adjust as necessary
   ).set_timeout(3600)  # Set transaction timeout

   # Append the invoke_contract_function operation for withdraw
   tx = tx_builder.append_invoke_contract_function_op(
       contract_id=ROUTER_CONTRACT_ID,
       function_name="withdraw",
       parameters=[
           Address(keypair.public_key).to_xdr_sc_val(),  # Distributor's address
           scval.to_vec([token_a, token_b]),  # Token pair
           scval.to_bytes(bytes.fromhex(pool_hash)),  # Known pool hash as bytes
           scval.to_uint128(SHARES_TO_REDEEM),  # Amount of shares to redeem
           scval.to_vec([
               scval.to_uint128(1),  # Minimum amount of Token A to receive
               scval.to_uint128(1),  # Minimum amount of Token B to receive
           ]),
       ],
   )
   tx = server.prepare_transaction(tx.build())
   tx.sign(keypair)

   tx_response = horizon_server.submit_transaction(tx)
   transaction_meta = xdr.TransactionMeta.from_xdr(tx_response['result_meta_xdr'])
   tx_result = transaction_meta.v3.soroban_meta.return_value
   if not tx_result:
       raise RuntimeError

   amount_a, amount_b = map(u128_to_int, [r.u128 for r in tx_result.vec.sc_vec])
   print("Withdrawal successful!")
   print(f"Received Amounts: Token A = {amount_a}, Token B = {amount_b}")


if __name__ == "__main__":
   execute_withdraw(POOL_HASH)

Last updated