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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fluxpointstudios/dexter

v0.0.19

Published

Customizable Typescript SDK for interacting with Cardano DEXs (FluxPoint fork with SaturnSwap REST)

Readme

What You Can Do

  • Pull Liquidity Pools from DEX APIs or On-chain using Blockfrost / Kupo
  • Submit and cancel swap orders
  • Submit split swap orders across multple DEXs
  • Build your own data, wallet, or asset metadata providers to plug into Dexter
  • Build swap datums given specific parameters using Dexters Definition Builder
  • Load wallets using a seedphrase or CIP-30 interface using @lucid-evolution/lucid

CSWAP (Hybrid AMM Orderbook)

  • On-chain pool discovery via CSWAP Dex Pool address (ADA pairs only).
  • Fee model:
    • Pool fee from pool datum lp_fee_10k (e.g., 85 → 0.85%).
    • Batcher fee: 690,000 lovelace.
    • Per-order min deposit: 2,000,000 lovelace (returned upon fill/cancel).
    • Platform fee per 10k: 15 (applied to target min output in datum).
import { Dexter, CSwap, BlockfrostProvider, LucidProvider } from '@fluxpointstudios/dexter';

const dexter = new Dexter();
const wallet = new LucidProvider();
await wallet.loadWalletFromSeedPhrase(
  ['...seed words...'],
  { accountIndex: 0 },
  { url: 'https://cardano-mainnet.blockfrost.io/api/v0', projectId: '<BLOCKFROST_PROJECT_ID>' }
);
dexter.withWalletProvider(wallet);

const data = new BlockfrostProvider(
  { url: 'https://cardano-mainnet.blockfrost.io/api/v0', projectId: '<BLOCKFROST_PROJECT_ID>' }
);
dexter.withDataProvider(data);

const cswap = dexter.dexByName(CSwap.identifier) as CSwap;
const pools = await cswap.liquidityPools(data);
const pool = pools.find(p => p.assetA === 'lovelace') || pools[0];

const tx = await dexter.newSwapRequest()
  .forLiquidityPool(pool)
  .withSwapInToken('lovelace')
  .withSwapInAmount(2_000_000n)   // 2 ADA
  .withSlippagePercent(0.5)
  .complete();

// sign and submit as needed:
// await tx.sign(); const hash = await tx.submit();

CSWAP Pricing helpers

This is the AMM pool’s implied price. Orderbook best bid/ask could differ; we can extend to read order UTxOs at the orderbook address to synthesize a live spread if needed.

import { getAmmImpliedPrice, getOrderbookTopOfBook, Asset } from '@fluxpointstudios/dexter';

// AMM-implied (from pool reserves)
const pools = await dexter.newFetchRequest()
  .onDexs(CSwap.identifier)
  .getLiquidityPools();
const pool = pools.find(p => p.assetA === 'lovelace')!;
const { adaPerToken, tokenPerAda } = getAmmImpliedPrice(pool);

// Orderbook top-of-book (best bid/ask)
const token = Asset.fromIdentifier('<policyId><assetNameHex>');
const top = await getOrderbookTopOfBook(dexter.dataProvider!, token.identifier());
console.log({ adaPerToken, tokenPerAda, top });

Notes

  • You may need to use the flag --experimental-specifier-resolution=node when building your project to correctly import Dexter
  • All figures/parameters represented as a bigint are denominated in lovelaces
  • Optional platform fee hook: set DEXTER_PLATFORM_FEE_ADDRESS and/or DEXTER_PLATFORM_FEE_LOVELACE (lovelace bigint string) to force every swap request to include a fixed ADA payment to your treasury. Defaults are always enabled (2 ADA to Flux Point Studios) in this fork, so override these env vars if you need a different destination or amount.
  • Blockfrost Proxy Support: Dexter now supports proxy-based Blockfrost configuration via environment variables. Set BLOCKFROST_PROXY_URL and BLOCKFROST_PROXY_PROJECT_ID to use your proxy endpoint. If not set, it falls back to direct Blockfrost credentials (BLOCKFROST_URL and BLOCKFROST_PROJECT_ID). This is particularly useful for production deployments with centralized API management.

Token Decimals Resolution

Important: Asset.decimals values in @fluxpointstudios/dexter are populated from DEX API metadata and are NON-AUTHORITATIVE hints only. DEX metadata can be incorrect or change without notice.

For any safety-critical calculations (pricing, slippage, order sizing, etc.), consumers should resolve authoritative decimals via:

The decimals values provided by Dexter are suitable for:

  • Display/cosmetic purposes
  • Rough analytics and pool comparisons
  • Initial hints that can be overridden by your resolver

They should NOT be trusted for:

  • Final order sizing calculations
  • Slippage enforcement
  • Risk-critical pricing logic

Example pattern for consumers:

// Dexter provides decimals as hints
const pool = await dexter.newFetchRequest().onDexs('Minswap').getLiquidityPools();
const asset = pool[0].assetB as Asset;

// Your app should resolve authoritative decimals
const authoritativeDecimals = await yourDecimalsResolver.resolve(asset.identifier());
asset.decimals = authoritativeDecimals; // Override with trusted value

Install

NPM
npm i @fluxpointstudios/dexter
Yarn
yarn add @fluxpointstudios/dexter

Quick Start

const dexterConfig: DexterConfig = {
    shouldFetchMetadata: true,      // Whether to fetch asset metadata (Best to leave this `true` for accurate pool info)
    shouldFallbackToApi: true,      // Only use when using Blockfrost or Kupo as data providers. On failure, fallback to the DEX API to grab necessary data
    shouldSubmitOrders: false,      // Allow Dexter to submit orders from swap requests. Useful during development
    metadataMsgBranding: 'Dexter',  // Prepend branding name in Tx message
    enableSaturnClob: false,        // Enable Saturn CLOB REST adapter
    enableSplashSdk: false,         // Register SplashSdk adapter (requires Splash SDK deps)
    splashSdkNetwork: 'mainnet',    // 'mainnet' | 'staging' for Splash SDK integration
};
const requestConfig: RequestConfig = {
    timeout: 5000,  // How long outside network requests have to reply
    proxyUrl: '',   // URL to prepend to all outside URLs. Useful when dealing with CORs
    retries: 3,     // Number of times to reattempt any outside request
};

const dexter: Dexter = new Dexter(dexterConfig, requestConfig);

// Basic fetch example
dexter.newFetchRequest()
    .onAllDexs()
    .getLiquidityPools()
    .then((pools: LiquidityPool[]) => {
        console.log(pools);
    });

// Example loading wallet to be used in a swap
const lucidProvider: BaseWalletProvider = new LucidProvider();

lucidProvider
    .loadWallet(cip30Interface, {
        url: 'https://cardano-mainnet.blockfrost.io/api/v0',
        projectId: '<blockfrost-project-id>'
    })
    .then((walletProvider: BaseWalletProvider) => {
        dexter.withWalletProvider(walletProvider)
            .newFetchRequest()
            ...
    });

Saturn defaults for SaturnSwap

  • By default, Dexter registers the AMM facade provider SaturnSwap-AMM only (no API key required).
  • The CLOB/REST provider SaturnSwap is optional and disabled by default.
  • To enable the CLOB/REST provider, pass { enableSaturnClob: true } in your DexterConfig.
import { Dexter, SaturnSwapAMM } from '@fluxpointstudios/dexter';

const dexter = new Dexter({ enableSaturnClob: false }); // default
const amm = dexter.dexByName(SaturnSwapAMM.identifier);

SaturnSwap-AMM (virtual AMM facade)

For apps that prefer AMM-like math and pool discovery (like Minswap/WingRiders), you can use the Saturn AMM facade:

import { Dexter, SaturnSwapAMM } from '@fluxpointstudios/dexter';

const dexter = new Dexter();
const amm = dexter.dexByName(SaturnSwapAMM.identifier) as SaturnSwapAMM;

// Pull AMM pools via REST
const pools = await amm.liquidityPools();
console.log('AMM pools', pools.length);

// AMM math (constant product) works like other providers
const pool = pools[0];
const estimated = amm.estimatedReceive(pool, 'lovelace', 1_000_000n); // 1 ADA in lovelace

// Optional server quote/build (on-chain units)
const quote = await (amm.api as any).ammQuote({
  poolId: pool.identifier, direction: 'in', swapInAmount: 1_000_000, slippageBps: 50
});
// Build a market swap (returns tokens in same tx). Optionally include partnerAddress for fee split.
const hex = await (amm.api as any).ammBuildOrder({
  poolId: pool.identifier,
  direction: 'in',
  swapInAmount: 1_000_000,
  slippageBps: 50,
  changeAddress: '<bech32>',
  partnerAddress: '<optional-partner-bech32>' // 1 ADA partner + 1 ADA platform; if omitted, 2 ADA to platform
});

// Sign and submit locally
const tx = wallet.newTransactionFromHex(hex.unsignedCborHex);
await tx.sign();
await tx.submit();

Notes:

  • ammBuildOrder returns unsigned CBOR; sign/submit locally with your wallet.
  • Spot Market swap by default.
  • Server-enforced fee outputs: 2 ADA total; with partnerAddress, 1/1 split between partner and platform; otherwise 2 ADA to platform.
  • Pool snapshots are cached ~1–2s; re-quote if you need a fresh snapshot for minReceive checks.

SaturnSwap (Advanced REST) [Optional]

// Configure env (example). You can also set process.env at runtime.
// SATURN_API_BASE_URL=https://api.saturnswap.io
// SATURN_API_KEY=your-api-key-or-bearer-token

// Enable the CLOB provider when constructing Dexter
const dexter = new Dexter({ enableSaturnClob: true });
const wallet = new LucidProvider();
await wallet.loadWallet(cip30Interface, {
    url: 'https://cardano-mainnet.blockfrost.io/api/v0',
    projectId: '<blockfrost-project-id>'
});

dexter.withWalletProvider(wallet);

// A) High-level: quote and build-by-asset (no need to pick poolId)
const saturn = dexter.dexByName('SaturnSwap');

// By-asset quote (no Authorization required)
const quote = await (saturn as any).quoteByAsset({
  asset: '<policyId><assetNameHex>', // '' if ADA
  direction: 3,                      // 3 = MarketBuy (ADA → token), 4 = MarketSell (token → ADA)
  tokenAmountSell: 1.0,              // display units (ADA or token), not on-chain units
  tokenAmountBuy: 0,                 // 0 with slippage=null lets builder choose fills
  slippage: null                     // set a number (e.g., 0.5) only if you also set tokenAmountBuy
});
console.log('quote:', quote);

// Build from asset (Authorization required via SATURN_API_KEY)
// Returns first unsigned tx hex if buildable at this moment
const hex = await (saturn as any).createFromAssetHex({
  asset: '<policyId><assetNameHex>',
  direction: 3,
  tokenAmountSell: 1.0,   // try 1–2 ADA (very small sizes like 0.5 ADA can be rejected by min-output rules)
  tokenAmountBuy: 0,
  slippage: null,
  paymentAddress: wallet.address()
});

// Sign + submit locally (or use advanced/sign for pre-cosigned flows)
if (hex) {
  const tx = wallet.newTransactionFromHex(hex);
  await tx.sign();
  await tx.submit();
  console.log('Submitted:', tx.hash);
}

// B) Lower-level: build via specific poolId (if you want to route yourself)
// const input = {
//   paymentAddress: wallet.address(),
//   limitOrderComponents: [
//     { poolId: '...', tokenAmountSell: 1000000, tokenAmountBuy: 500000, limitOrderType: 0, version: 1 }
//   ]
// };
// const txHash = await (saturn as any).buildSignSubmitViaApi(input, wallet);
// console.log('Submitted:', txHash);

Environment variables:

  • SATURN_API_BASE_URL (e.g., https://api.saturnswap.io)
  • SATURN_API_KEY or SATURN_API_TOKEN (will be sent as Authorization: Bearer <value>)

SaturnSwap by-asset inputs at a glance

  • asset: concatenation of policyId + assetNameHex (no separator). Use empty string '' for ADA.
  • direction:
    • 3 MarketBuy (spend ADA → receive token). tokenAmountSell is ADA (display units).
    • 4 MarketSell (sell token → receive ADA). tokenAmountSell is token amount (display units).
  • tokenAmountSell / tokenAmountBuy: display units (we scale by decimals internally).
  • slippage:
    • When tokenAmountBuy = 0, set slippage = null (builder treats buy=0+slippage as a hard fail).
    • To enforce minimum output, first call quote, then set tokenAmountBuy = quote.expectedBuy and slippage = e.g., 0.5.

SaturnSwap API surface in this SDK

  • Discovery:
    • SaturnSwapApi.assets(): GET /v1/aggregator/assets
    • SaturnSwapApi.orderbook(assetA, assetB): GET /v1/aggregator/orderbook
    • SaturnSwapApi.quoteByAsset(input): POST /v1/aggregator/quote
  • Build / Sign / Submit:
    • SaturnSwapApi.createOrderTransactionSimple(...): POST /v1/aggregator/simple/create-order-transaction
    • SaturnSwapApi.createOrderTransactionFromAsset(input): POST /v1/aggregator/simple/create-from-asset
    • SaturnSwapApi.signOrderTransactionAdvanced(...): POST /v1/aggregator/advanced/sign-order-transaction
    • SaturnSwapApi.submitOrderTransactionSimple(...): POST /v1/aggregator/simple/submit-order-transaction
  • Dexter convenience (in SaturnSwap):
    • quoteByAsset(input)
    • createFromAssetHex(input) → hex
    • buildFromAssetSignSubmit(input, wallet) → txHash
    • Legacy pool-based helpers: buildSwapOrder(...), createSimpleOrderHexViaApi(...), buildSignSubmitViaApi(...)

Troubleshooting tips

  • Pool-specific depth: an asset may have bids/asks overall but your chosen pool could be empty at build time. Use quoteByAsset (auto-routes to a spendable pool).

  • Small sizes: very small ADA spends (e.g., 0.5) can fail due to min-output/fee constraints—try ≥1–2 ADA.

  • Units: always send display units (ADA or token). Do not pre-scale to on-chain units.

  • Splash SDK Adapter (SplashSdk)

    • Set enableSplashSdk: true and (optionally) splashSdkNetwork: 'mainnet' | 'staging' in your DexterConfig to register the new adapter alongside the legacy on-chain builder.
    • Pools discovered through SplashSdk expose pool.dex === 'SplashSdk'. Route swap requests to that identifier to build orders via @splashprotocol/sdk.
    • The adapter consumes @splashprotocol/sdk, @splashprotocol/core, @splashprotocol/api, and @splashprotocol/cml-builder. Ensure these packages are installed (they are bundled with this repo).
    • Existing Splash pools remain available; opt-in per pool if you need to migrate gradually.

Dexter API

All providers outlined below are modular, so you can extend the 'base' of the specific provider you want to supply, and provide it to Dexter with one of the methods below.

Using
dexter.dexByName(Minswap.identifier)
    ...

By default, Dexter will use the DEX APIs to grab information. However, you can use Blockfrost or Kupo to supply your own data.

Using
const provider: BaseDataProvider = new BlockfrostProvider(
    {
        url: 'https://cardano-mainnet.blockfrost.io/api/v0',
        projectId: '<blockfrost-project-id>',
    }
);

dexter.withDataProvider(provider)
    ...

At this time, Dexter only supplies a Mock wallet provider & a Lucid provider. Behind the scenes, the lucid provider leverages @lucid-evolution/lucid to manage your wallet & create transactions.

Blockfrost Configuration: The LucidProvider automatically resolves Blockfrost configuration from environment variables:

  • Proxy mode (preferred for production): Set BLOCKFROST_PROXY_URL and BLOCKFROST_PROXY_PROJECT_ID environment variables
  • Direct mode: Set BLOCKFROST_URL (optional, defaults to mainnet) and BLOCKFROST_PROJECT_ID environment variables

You can still pass explicit BlockfrostConfig objects, but if omitted, the provider will use the environment-based resolution.

Using
const provider: BaseWalletProvider = new LucidProvider();
const seedphrase: string[] = ['...'];
const blockfrostConfig: BlockfrostConfig = {
    url: 'https://cardano-mainnet.blockfrost.io/api/v0',
    projectId: '<blockfrost-project-id>',
};

provider.loadWalletFromSeedPhrase(seedphrase, blockfrostConfig)
    .then((walletProvider: BaseWalletProvider) => {
        dexter.withWalletProvider(walletProvider)
            ...
    });

By default, Dexter will use the Cardano Token Registry for grabbing asset metadata. You can extend the BaseMetadataProvider interface to provide your own metadata.

Using
const provider: BaseMetadataProvider = new TokenRegistryProvider();

dexter.withMetadataProvider(provider)
    ...

For available methods on the FetchRequest instance, please see those specific docs.

Using
dexter.newFetchRequest()
    ...

For available methods on the SwapRequest instance, please see those specific docs.

Using
dexter.newSwapRequest()
    ...

For available methods on the SplitSwapRequest instance, please see those specific docs.

Using
dexter.newSplitSwapRequest()
    ...

For available methods on the CancelSwapRequest instance, please see those specific docs.

Using
dexter.newCancelSwapRequest()
    ...

For available methods on the SplitCancelSwapRequest instance, please see those specific docs.

Using
dexter.newSplitCancelSwapRequest()
    ...

More Docs