npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@spreadless-dex/sdk

v0.0.1

Published

TypeScript SDK for the Spreadless liquidity-pool Soroban contract.

Readme

@spreadless-dex/sdk

TypeScript SDK for the Spreadless liquidity-pool Soroban contract — a StableSwap-style AMM on Stellar for swapping between correlated assets with low slippage.

The SDK is a thin, fully-typed client generated from the deployed contract. It wraps every entrypoint (swaps, deposits, withdrawals, the SEP-41 LP token, and admin controls) in an async method and talks to the network over Soroban RPC.

Install

npm install @spreadless-dex/sdk

@stellar/stellar-sdk is a dependency and is re-exported, so you don't need to install it separately — Keypair, Address, the contract helpers, and the rpc client are all available from this package.

Requires Node 18+ (or any runtime with global fetch and BigInt).

Deployed contracts

The SDK does not bundle a networks constant — you pass the contract ID, network passphrase, and RPC URL yourself. The current testnet deployment:

| What | Value | | --- | --- | | Pool contract | CCAD3EH4P74PVYL3IC6ND7RSV6NYYOMUMNKRNVBJYOVIZP7Z2QS5XTSN | | Network passphrase | Test SDF Network ; September 2015 | | RPC URL | https://soroban-testnet.stellar.org |

Pool tokens, in canonical order (this is the order every amount array uses — see Token order):

| Index | Symbol | Token contract | Decimals | | --- | --- | --- | --- | | 0 | sDAI | CBXN4CMLFVDNVFSGNXFGP5EWI77ISC5KH5UXSDBQETZCJHYHA3KEP4JJ | 7 | | 1 | sUSDT | CB2NS6KYG5ZBHHVKXCHYWLRRH4AKFXNWRYNSQTKNFW23CAY4SGSQVG75 | 7 | | 2 | SUSD (SAC) | CDDE66QMXWVUVEHLA5IRUJBHPJK3RFH6JIXCIJ5S6HOAXAPYR2AIZUWD | 7 | | 3 | sUSDC | CDKFYHC3EPRCZY4DIMCIBQ3PO5QPD6KZFFXNMLS4XENY2QNTZN2KLMRM | 7 |

The three s* tokens are open-mint test tokens: anyone can call mint(to, amount) to fund an account for testing. Always treat deployments/testnet.json in the repo as the source of truth — addresses change when the pool is redeployed. Don't hardcode the reserve order from this table; read it live with get_tokens().

Quick start

Read pool state (no signing needed)

Read-only calls just simulate against the RPC, so you can omit signing options:

import { Client } from "@spreadless-dex/sdk";

const pool = new Client({
  contractId: "CCAD3EH4P74PVYL3IC6ND7RSV6NYYOMUMNKRNVBJYOVIZP7Z2QS5XTSN",
  rpcUrl: "https://soroban-testnet.stellar.org",
  networkPassphrase: "Test SDF Network ; September 2015",
});

// Every method returns an AssembledTransaction; `.result` holds the simulated value.
const tokens = (await pool.get_tokens()).result;   // string[]  (token order)
const reserves = (await pool.get_reserves()).result; // bigint[]  (raw units, same order)
const amp = (await pool.get_amp()).result;          // number

console.log({ tokens, reserves, amp });

Swap (signs and submits a transaction)

State-changing calls need a source account that can sign. The account you pass as to provides the input funds and must authorize the transaction — so the signer's public key must equal to.

import { Client, contract, Keypair } from "@spreadless-dex/sdk";

const networkPassphrase = "Test SDF Network ; September 2015";
const kp = Keypair.fromSecret(process.env.STELLAR_SECRET_KEY!);

const pool = new Client({
  contractId: "CCAD3EH4P74PVYL3IC6ND7RSV6NYYOMUMNKRNVBJYOVIZP7Z2QS5XTSN",
  rpcUrl: "https://soroban-testnet.stellar.org",
  networkPassphrase,
  publicKey: kp.publicKey(),
  ...contract.basicNodeSigner(kp, networkPassphrase),
});

const [DAI, USDT] = (await pool.get_tokens()).result;

// Swap exactly 10.0 sDAI (7 decimals) for USDT, reverting below 9.95 out.
const tx = await pool.swap_exact_in({
  to: kp.publicKey(),
  token_in: DAI,
  token_out: USDT,
  amount_in: 100_000_000n, // 10.0 * 10^7
  min_out: 99_500_000n,    // slippage floor, raw units of USDT
});

// tx.result is the *simulated* output. Nothing has been submitted yet:
console.log("quote:", tx.result);

// Sign and broadcast to make it real:
const { result } = await tx.signAndSend();
console.log("received:", result, "raw units of USDT");

How the client works

A few things to internalize before using the rest of the API.

Every method returns an AssembledTransaction

Calling a method builds and simulates a transaction against the RPC, then returns an AssembledTransaction<T>:

  • Read/view methods (get_reserves, balance, paused, …) — the answer is already in .result. You never need to sign or send.
  • Write methods (swap_*, deposit, withdraw*, approve, transfer, admin setters, …) — .result holds the predicted result from simulation. Call await tx.signAndSend() to submit; its return also has a .result with the on-chain outcome.
const tx = await pool.deposit({ to, amounts_in, min_lp_out });
tx.result;                       // simulated LP shares you'd receive
const sent = await tx.signAndSend();
sent.result;                     // actual LP shares minted

Numbers: i128/u64/u128 are bigint

Contract integer types map to JS bigint. Pass amounts as bigint literals (100_000_000n), not number. u32 values (amp, live_until_ledger) are plain number.

Amounts are raw token units

There is no decimal handling. A token with 7 decimals means 1.0 token = 10_000_000n. Reserves, swap amounts, caps, and payouts are all in each token's raw on-chain units. The LP share token (SLP) uses 9 decimals.

Token order matters

Every array argument and return — amounts_in, min_amounts_out, get_reserves() — is indexed by the pool's canonical token order, which you get from get_tokens(). Index into that array rather than assuming a fixed layout, since order can differ from display labels and changes between pools.

Always set slippage bounds

deposit takes min_lp_out, withdraw takes min_amounts_out, swaps take min_out / max_in. These are enforced on-chain and revert with SlippageExceeded (error 14) if the trade moves against you past the bound. In production, derive them from the simulated .result minus your tolerance.

Recipes

Add liquidity

amounts_in follows get_tokens() order. The first deposit into a pool must fund every token; later deposits may be balanced, imbalanced, or single-sided.

const tx = await pool.deposit({
  to: kp.publicKey(),
  amounts_in: [100_000_000n, 100_000_000n, 100_000_000n, 100_000_000n],
  min_lp_out: 0n, // set from the simulated quote in production
});
const { result: lpMinted } = await tx.signAndSend();

Remove liquidity

Proportional exit (fee-free) — returns a slice of every reserve:

const tx = await pool.withdraw({
  to: kp.publicKey(),
  lp_amount: lpMinted,
  min_amounts_out: [0n, 0n, 0n, 0n],
});
const { result: amountsOut } = await tx.signAndSend(); // bigint[], token order

Single-token exit (charges the swap fee on the imbalanced portion):

const tx = await pool.withdraw_one_token({
  to: kp.publicKey(),
  lp_amount: lpMinted,
  token_out: USDT,
  min_amount_out: 0n,
});
const { result } = await tx.signAndSend();

Swap for an exact output

const tx = await pool.swap_exact_out({
  to: kp.publicKey(),
  token_in: DAI,
  token_out: USDT,
  amount_out: 100_000_000n, // want exactly 10.0 USDT out
  max_in: 101_000_000n,     // spend at most 10.1 DAI
});
const { result: amountIn } = await tx.signAndSend();

Check an LP share balance

The pool contract is itself the SEP-41 LP token, so token methods live on the same client:

const shares = (await pool.balance({ account: kp.publicKey() })).result;
const supply = (await pool.total_supply()).result;

Method reference

Every method returns Promise<AssembledTransaction<T>>; T is listed below. Inline JSDoc for each is available in your editor via pool. autocomplete.

Views (read-only)

| Method | Returns | Description | | --- | --- | --- | | get_tokens() | string[] | Pool token addresses, in canonical order. | | get_reserves() | bigint[] | Current reserves in raw units, token order. | | get_amp() | number | Effective amplification factor (reflects any ramp). | | paused() | boolean | Whether liquidity/swap ops are paused. | | get_owner() | string \| undefined | Owner address, or undefined if renounced. |

Swaps

| Method | Args | Returns | | --- | --- | --- | | swap_exact_in | { to, token_in, token_out, amount_in, min_out } | bigint output sent | | swap_exact_out | { to, token_in, token_out, amount_out, max_in } | bigint input taken |

Liquidity

| Method | Args | Returns | | --- | --- | --- | | deposit | { to, amounts_in, min_lp_out } | bigint LP shares minted | | withdraw | { to, lp_amount, min_amounts_out } | bigint[] amounts out | | withdraw_one_token | { to, lp_amount, token_out, min_amount_out } | bigint amount out |

LP token (SEP-41)

balance · total_supply · decimals · name · symbol · allowance · approve · transfer · transfer_from · burn · burn_from. Standard SEP-41 semantics. Note: direct LP burn outside withdraw* keeps supply and reserves in sync only through the withdrawal paths — burning shares directly to reduce your position is disabled (DirectLpBurnDisabled, error 20).

Admin (owner only)

| Method | Args | | --- | --- | | set_amp_ramp | { target_factor, duration } — linear A ramp over duration seconds | | set_swap_fee | { swap_fee }1_000_000_000 = 100% | | set_protocol_fee | { protocol_fee } — cut of the swap fee routed to beneficiary | | set_beneficiary | { beneficiary } | | set_max_supply | { max_supply } — LP supply cap | | set_token_cap | { token, max_cap } — per-token reserve cap, raw units | | pause / unpause | — |

Ownership (two-step)

transfer_ownership({ new_owner, live_until_ledger })accept_ownership() (called by the new owner) · renounce_ownership().

Error codes

Contract failures surface as a thrown error carrying a numeric code. The pool-specific codes:

| Code | Name | Meaning | | --- | --- | --- | | 1 | InvalidTokenCount | Pool needs 2–5 tokens. | | 2 | TokensNotSorted | Constructor tokens not strictly ascending. | | 9 | AmountsLengthMismatch | Array length ≠ token count. | | 10 | InvalidAmount | Negative or otherwise invalid amount. | | 11 | ZeroDeposit | Deposit contributes nothing. | | 12 | FirstDepositNotFull | First deposit must fund every token. | | 13 | MathError | Invariant/quote math failed to converge. | | 14 | SlippageExceeded | Result crossed your min_* / max_in bound. | | 15 | CapExceeded | A reserve or LP-supply cap would be exceeded. | | 17 | UnknownToken | Address is not a pool token. | | 18 | SameToken | token_in == token_out. | | 19 | TransferAmountMismatch | Token moved a different amount than expected (fee-on-transfer tokens are rejected). | | 20 | DirectLpBurnDisabled | LP shares can only exit via withdraw*. |

The full list, plus the Errors, PausableError (1000–1001), OwnableError (2100–2102), RoleTransferError (2200–2203), and FungibleTokenError (100–114) maps, are exported for programmatic lookup:

import { Errors, FungibleTokenError } from "@spreadless-dex/sdk";
Errors[14];              // { message: "SlippageExceeded" }
FungibleTokenError[100]; // { message: "InsufficientBalance" }

Building from source

This package is published pre-built; installing from npm is all you need to use it. To rebuild it (e.g. after regenerating bindings from a new contract build):

npm install
npm run build   # tsc -> dist/

Bindings are regenerated from the compiled wasm via the repo's make bindings target. See the main repository for contract sources, the full protocol description, and deployment tooling.