This release is a pre-release and may not be stable for production use.
aquarius-sdk
The Python SDK for Aquarius — swaps, liquidity, and rewards on Stellar.
Status: 0.6.x — swaps and the liquidity lifecycle are complete and verified with real testnet transactions. Concentrated liquidity position management is available as a beta surface (the contracts are under an ongoing external audit). The API may still change before 1.0.
Swap through a specific pool
client.quote()/swap() route through the path-finding API. To pin a swap to
one pool you trust — your own routing, API independence — quote and swap on the
pool itself; the estimate comes from the router's on-chain estimate_swap:
pool = next(p for p in client.pools_for_pair(XLM, AQUA) if p.fee_bps == 10)
quote = pool.quote(XLM, AQUA, amount_in=100_0000000) # no signer needed
receipt = quote.execute() # swaps in this pool only
# or: pool.swap(XLM, AQUA, amount_in=100_0000000, retries=3)
Exact input only — the router has no single-pool strict-receive; for exact
output use client.quote(..., amount_out=...).
Concentrated liquidity positions (beta)
A position is the key (owner, tick_lower, tick_upper) on the pool contract — no
NFTs, merged on re-deposit, at most 20 ranges per account. Quotes come from the
contract's own estimators; execute/withdraw_position derive real slippage
guards from them:
pool = next(p for p in aqua.pools_for_pair(XLM, AQUA) if p.type == "concentrated")
est = pool.estimate_position_deposit(
{XLM: 10_0000000, AQUA: 100_0000000},
price_range=("0.9", "1.1"), # snaps to tick spacing; or tick_range=(lower, upper)
)
opened = est.execute(slippage=0.01) # min-liquidity guard from the estimate
pool.position_ranges() # all of the signer's ranges
pool.position_range_status(est.tick_lower, est.tick_upper) # in_range / below / above
pool.position_value(est.tick_lower, est.tick_upper) # principal / fees / total, split
pool.claim_position_fees(est.tick_lower, est.tick_upper)
pool.withdraw_position(est.tick_lower, est.tick_upper) # full close, auto-claims fees
tick_from_price, price_at_tick, and snap_tick are exported for range math —
integer-exact and identical across both language packages.
pip install aquarius-sdk
from stellar_sdk import Keypair
from aquarius import AquariusClient, Asset, XLM, SlippageError
AQUA = Asset.classic("AQUA", "GBNZ...AQUA")
aqua = AquariusClient(network="mainnet", signer=Keypair.from_secret(secret))
# exact input: quote, inspect, execute
quote = aqua.quote(XLM, AQUA, amount_in=100_0000000, slippage=0.01)
receipt = quote.execute()
# exact output: pass amount_out instead — strict-receive throughout
quote = aqua.quote(XLM, AQUA, amount_out=500_0000000)
Provider fees
Pass the collector configuration with the quote. The SDK keeps the fee math exact, routes through the collector, and sends output to recipient without requiring that address to sign:
from aquarius import ProviderFeeConfig
provider_fee = ProviderFeeConfig(
contract_id="C...",
fee_fraction=30, # 30 / fee_denominator=10_000 -> 0.3% of the output
fee_denominator=10_000,
recipient="G...",
)
quote = aqua.quote(
XLM,
AQUA,
amount_in=100_0000000,
slippage=0.005,
provider_fee=provider_fee,
)
receipt = quote.execute()
Omit recipient to send output back to the signer through the collector's legacy swap methods. Use amount_out instead of amount_in for an exact-output provider swap.
Liquidity
pools = aqua.pools_for_pair(XLM, AQUA) # discovered on-chain, sorted by type and fee
pool = pools[0] # Pool(type="volatile", fee_bps=10, ...)
result = pool.deposit({XLM: 50_0000000, AQUA: 2500_0000000}, slippage=0.01)
print(result.shares) # pool share tokens minted
pool.pending_rewards() # accrued AQUA, in stroops
pool.claim_rewards()
pool.withdraw(result.shares, slippage=0.01)
aqua.positions() # every pool where the signer holds shares
Deposit and withdrawal guards come from a simulation of the exact call, reduced by slippage — quoted-versus-executed drift is bounded the same way as for swaps. Reads (reserves(), pending_rewards(), pools_for_pair()) need no signer.
What the SDK handles for you
- Routing — quotes come from the find-path API; the swap chain XDR is passed through untouched.
- Transaction lifecycle — simulation, assembly, submission with congestion retries (same-hash resubmission with backoff), and confirmation polling.
- Fixed authorization — every contract write and unsigned transaction uses client-built authorization trees. Preparation simulates in
enforcemode, uses the response for resources, and preserves the exact operation and auth through restore and sequence retries. RPC-returned auth is never added, including when the supplied list is empty. - Archived state — if simulation reports expired ledger entries, the SDK restores them (one extra signed transaction) and retries automatically.
- Typed errors —
SlippageError(withrequote()),PausedError(kill switches — not your bug),NoRouteError,UserRejectedError,TxTimeoutError.
Signers
A stellar-sdk Keypair works as-is. Custom signers provide public_key plus sign(tx_xdr) -> str returning the signed envelope XDR. Reads — quote() — need no signer at all.
Escape hatches
quote.build_transaction() returns the simulated, unsigned envelope XDR for external signing flows, with the same fixed authorization as execute(). client.api is the typed REST client.
client.contract_call(fn, *scvals) invokes the router raw and authorizes only
that exact root call with source-account credentials. Raw calls that previously
relied on simulation to discover nested permissions must now provide those
permissions explicitly with auth=[...], or use the corresponding high-level
method such as pool.deposit(). An incomplete tree fails during enforced
simulation before the contract transaction is signed.
For example, an explicit raw reward claim mirrors the router and pool calls:
from stellar_sdk import scval
from aquarius.authorization import authorized_invocation, source_account_entry
user = scval.to_address(public_key) # the transaction source account
args = [
user,
scval.to_vec([scval.to_address(token) for token in pool.tokens]),
scval.to_bytes(pool.pool_hash),
]
entry = source_account_entry(authorized_invocation(
aqua.network.router, "claim", args,
[authorized_invocation(pool.address, "claim", [user])],
))
aqua.contract_call("claim", *args, auth=[entry])
Pass auth=[] when the call requires no authorization; the empty list remains
empty after assembly. Reads and estimates can still use recording simulation,
but their recorded auth never enters a transaction for signing. Archived-state
restoration remains a separate footprint operation when a signer is available.
Infrastructure
Defaults point at the protocol's own endpoints: the mainnet RPC is
https://soroban-rpc.aqua.network — the same node the Aquarius web app and
backend use. Running your own infrastructure? Every endpoint is overridable:
aqua = AquariusClient(
network="mainnet",
rpc_url="https://your-rpc.example.com",
horizon_url="https://your-horizon.example.com",
)
Fees
base_fee (default 100_000 stroops) is the inclusion fee bid. On top of it,
every Soroban transaction declares a resource fee taken from simulation, raised
by resource_fee_headroom (default 0.5, i.e. +50%; 0 disables):
aqua = AquariusClient(network="mainnet", signer=signer, resource_fee_headroom=0.3)
The declared resource fee is a cap: the network charges the non-refundable part
plus the refundable part actually consumed (rent, events, return value) and
refunds the rest, so headroom costs nothing unless it is used. The refundable
part depends on ledger state at apply time, so the bare simulated fee can fall
short and fail the transaction on-chain with
INVOKE_HOST_FUNCTION_INSUFFICIENT_REFUNDABLE_FEE. The headroom is applied
before signing (also to automatic restore transactions and to
quote.build_transaction()), so wallets see and sign the final fee.
The source account must hold the full declared fee — inclusion fee plus
resource fee plus headroom: it is debited before execution, and the unused part
is refunded after apply. When spending the whole XLM balance, leave that margin.
Re-preparing the XDR from quote.build_transaction() with stellar-sdk's own
prepare_transaction re-simulates and replaces the resource fee, which drops the
headroom.
Declared CPU instructions are a hard limit too: a transaction that needs more
than it declares fails on-chain with
INVOKE_HOST_FUNCTION_RESOURCE_LIMIT_EXCEEDED, and actual usage can exceed the
simulated count by several percent. So the simulated instructions are raised by
instruction_headroom (default 0.25, i.e. +25%; 0 disables), capped at the
network's per-transaction limit (read from the network config and cached), and
the resource fee by the compute fee of the added instructions — non-refundable,
under 0.0001 XLM for a typical swap at current mainnet rates — before the fee
headroom is applied on top. Restore transactions run no contract code and keep
the simulated instructions.
aqua = AquariusClient(network="mainnet", signer=signer, instruction_headroom=0.4)
Amounts
All amounts are integers in token base units (stroops for classic assets: 1 token = 10^7).
Questions and integration help: Discord.
Release files for aquarius-sdk 0.6.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aquarius_sdk-0.6.0a1.tar.gz | 56.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aquarius_sdk-0.6.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 98.7 kB
Release files / aquarius_sdk-0.6.0a1.tar.gz
| Download URL | aquarius_sdk-0.6.0a1.tar.gz |
|---|---|
| Size | 56.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4e65dc261e87c36ede7cb61bdb1b7df17c6b92ddb9d685570b241a0ab87a6814
|
|
BLAKE2b-256 checksum How to use checksums |
c528eea50b107b9a60503b15b360377f57259363b84e1a245abcc38b651a24a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency logRelease files / aquarius_sdk-0.6.0a1-py3-none-any.whl
| Download URL | aquarius_sdk-0.6.0a1-py3-none-any.whl |
|---|---|
| Size | 41.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f4e1b0b55e7e74fbc3b3eb3c4c4e50c3b8e3c896487047a4b38c9e72b28ddd14
|
|
BLAKE2b-256 checksum How to use checksums |
2aaa5e6affb4666024247da062546e923628c72394450b5224ce71fee35bf07f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency log