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

@prism-ing/swap-router

v1.0.4

Published

HTTP client SDK for the Prism aggregator service — quotes, signing, and execution for EVM, Solana, and cross-chain swaps.

Readme

@prism-ing/swap-router

HTTP client SDK for the Prism meta-DEX aggregator service.

Install

pnpm add @prism-ing/swap-router @prism-ing/wallet

Aggregator Service

@prism-ing/swap-router is an HTTP client for the Prism aggregator service. You need a running instance.

Self-host: Deploy apps/aggregator-service to Fly.io — see the aggregator README for required environment variables.

Set baseUrl in your config to your instance URL (must include /v1).

Third-party API terms Anyone operating an aggregator-service instance integrates with Jupiter, KyberSwap, OKX DEX, swaps.xyz, and Fynd. You must comply with each provider's terms of service. Notably, Jupiter's SDK License Agreement requires that end-user interfaces display "Powered by Jupiter" attribution.

Solvers

The aggregator races all available solvers on every quote. The best price wins.

| Provider value | Route type | Notes | |----------------|-----------|-------| | kyberswap | Same-chain EVM | High liquidity on Base, Arbitrum, Polygon | | jupiter | Same-chain Solana | Best for Solana routes | | okx-dex | Same-chain EVM or Solana | Alternative across both ecosystems | | swaps-xyz | Same-chain or cross-chain bridge | Handles bridge routes as fallback | | onebalance | Cross-chain fast-path | Requires accounts field with smart account | | fynd | Same-chain EVM | Optional server-side dependency | | bf-evm | Same-chain EVM | Bellman-Ford optimizer, optional dep | | bf-solana | Same-chain Solana | Bellman-Ford optimizer, optional dep |

Pin to a specific solver for testing with the provider field in QuoteRequest.

Quickstart

import { createSwapRouter } from '@prism-ing/swap-router';

const router = createSwapRouter({
  baseUrl: 'https://your-aggregator.fly.dev/v1',
  // Optional: provide a signer to enable execution (not just quoting)
  signer: mySigner,
  evmRpcUrls: { 8453: 'https://mainnet.base.org' },
  solanaRpcUrl: 'https://api.mainnet-beta.solana.com',
});

// Get the best swap quote from all solvers
const quoteResult = await router.quote({
  fromAsset: 'USDC:base',
  toAsset: 'WETH:base',
  amount: '1000000', // 1 USDC (6 decimals)
  sender: '0xYourAddress',
});

if (!quoteResult.ok) {
  console.error(quoteResult.error.code);
  process.exit(1);
}

const quote = quoteResult.value;
console.log(
  `${quote.provider} → ${quote.netAmountOut} (impact: ${quote.priceImpactBps}bps)`,
);

// Execute the swap
const execResult = await router.execute(quote);
if (execResult.ok) {
  console.log('Status:', execResult.value.status);
  // For cross-chain: poll until COMPLETED
  if (execResult.value.status === 'pending') {
    const status = await router.getStatus(execResult.value.quoteId!);
    console.log(status.ok ? status.value.status : status.error.code);
  }
}

Signer Interface

Any PrismSigner from @prism-ing/wallet satisfies SwapSigner — pass wallet.signer directly:

import { createProductionWallet } from '@prism-ing/wallet';

const walletResult = await createProductionWallet({ signer: { walletName: 'my-agent' } });
const wallet = walletResult.value;

const router = createSwapRouter({
  baseUrl: '...',
  signer: wallet.signer, // PrismSigner → SwapSigner, no cast needed
});

signEvmTxHash is optional in the interface but required at runtime for EVM swap execution. All built-in PrismSigner backends implement it.

Asset Formats

| Format | Example | Notes | | --------------------- | -------------------- | ------------------------------------- | | Symbol + chain | "USDC:base" | Resolved to address by the aggregator | | Address + chainId | "0xA0b86991c...:1" | Specific token address on chain | | Solana symbol | "SOL:solana" | Native SOL | | OneBalance aggregated | "ob:usdc" | Cross-chain, any chain |

Custom tokens: For tokens not in the aggregator's registry, use "0xContractAddress:chainId" (e.g. "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48:1" for USDC on mainnet). Symbol + chain resolution only works for tokens the aggregator knows about.

Route Types

| Route | When | Execution | | ------------------- | ----------------------------- | ----------------------------------------------- | | same-chain-evm | Both assets on same EVM chain | Sign + broadcast via EVM RPC | | same-chain-solana | Both assets on Solana | Sign + broadcast via Solana RPC | | cross-chain | Assets on different chains | Sign OneBalance operations → poll getStatus() |

Cross-Chain with OneBalance

For cross-chain swaps, pass smart account info in the accounts field. MultiAccount from @prism-ing/onebalance is assignable to SwapAccount directly:

import type { MultiAccount } from '@prism-ing/onebalance';

const accounts: MultiAccount[] = [
  {
    type: 'kernel-v3.1-ecdsa',
    signerAddress: wallet.evmAddress,
    accountAddress: wallet.smartAccountAddress,
  },
];

const quote = await router.quote({
  fromAsset: 'USDC:base',
  toAsset: 'USDT:arbitrum',
  amount: '1000000',
  sender: wallet.evmAddress,
  accounts,
});

See @prism-ing/onebalance for the full three-package bootstrap example.

Full Cross-Chain Polling Loop

For production use, poll until COMPLETED rather than relying on the auto-poll in execute():

import { createSwapRouter } from '@prism-ing/swap-router';

const router = createSwapRouter({
  baseUrl: 'https://your-aggregator.fly.dev/v1',
  signer: wallet.signer,
  evmRpcUrls: { 8453: process.env.BASE_RPC_URL },
});

// Quote
const quoteResult = await router.quote({
  fromAsset: 'USDC:base',
  toAsset:   'USDC:arbitrum',
  amount:    '5000000',
  sender:    wallet.evmAddress,
  accounts:  [{ type: 'kernel-v3.1-ecdsa', accountAddress: wallet.smartAccountAddress, signerAddress: wallet.evmAddress }],
});
if (!quoteResult.ok) throw new Error(quoteResult.error.code);

// Execute (submit only — don't auto-poll)
const execResult = await router.execute(quoteResult.value);
if (!execResult.ok) throw new Error(execResult.error.code);

const exec = execResult.value;
if (exec.status === 'confirmed') {
  console.log('Same-chain confirmed:', exec.txHash);
  process.exit(0);
}

// Cross-chain — poll manually
const quoteId = exec.quoteId ?? '';
const INTERVAL = 5_000;
const TIMEOUT  = 5 * 60_000;
const start    = Date.now();

while (Date.now() - start < TIMEOUT) {
  await new Promise((r) => setTimeout(r, INTERVAL));
  const s = await router.getStatus(quoteId);
  if (!s.ok) throw new Error(s.error.code);
  console.log(`[${Math.round((Date.now() - start) / 1000)}s]`, s.value.status);
  if (s.value.status === 'COMPLETED') {
    console.log('Done! Tx:', s.value.txHash);
    break;
  }
  if (s.value.status === 'FAILED') throw new Error('Cross-chain swap failed');
}

Session Keys + Swaps

Pass a session key signer instead of the root signer for policy-scoped execution:

import { createSessionKeyManager } from '@prism-ing/wallet';
import { createSwapRouter } from '@prism-ing/swap-router';

// Create a session scoped to USDC only, 1-hour expiry, 10 USDC max per op
const manager = createSessionKeyManager(wallet.signer);
const session = manager.createSessionKey({
  expiresAt:       Math.floor(Date.now() / 1000) + 3600,
  allowedAssets:   ['ob:usdc'],
  maxAmountPerOp:  '10000000', // 10 USDC
  allowedChains:   [8453, 42161],
});

// The session signer satisfies SwapSigner directly
const router = createSwapRouter({
  baseUrl:     'https://your-aggregator.fly.dev/v1',
  signer:      session.signer,  // policy-scoped — can only sign USDC ops under 10 USDC
  evmRpcUrls:  { 8453: process.env.BASE_RPC_URL },
});

// If the agent tries to sign a 20 USDC swap, session.signer returns SESSION_KEY_POLICY_VIOLATION
const result = await router.execute(quote);

// Revoke when the agent task is done
manager.revokeSessionKey(session.sessionId);

Error Handling

All methods return Result<T, SwapRouterError>. Error codes:

| Code | Meaning | | ------------------ | ------------------------------------ | | QUOTE_EXPIRED | Quote TTL elapsed before execution | | SIGNER_REQUIRED | Execution called without a signer | | EVM_RPC_REQUIRED | No RPC URL for the swap's chain | | SIGNING_FAILED | Signer returned an error | | BROADCAST_FAILED | Transaction rejected by the network | | VALIDATION_ERROR | Unexpected aggregator response shape | | NETWORK_ERROR | HTTP or connection failure | | TIMEOUT | Request exceeded timeoutMs | | QUOTE_NOT_FOUND | Status polled for unknown quoteId |

const result = await router.execute(quote);
if (!result.ok) {
  switch (result.error.code) {
    case 'QUOTE_EXPIRED':
      // Re-fetch a fresh quote
      break;
    case 'SIGNER_REQUIRED':
      // Provide a signer in SwapRouterConfig
      break;
    case 'NETWORK_ERROR':
      // Retry — transient
      break;
  }
}

Configuration

interface SwapRouterConfig {
  baseUrl: string; // Aggregator URL (include /v1)
  timeoutMs?: number; // Default: 10_000
  signer?: SwapSigner; // Required for execution
  evmRpcUrls?: Partial<Record<number, string>>; // Per-chain RPC endpoints
  solanaRpcUrl?: string; // Solana RPC endpoint
  approvalConfirmationTimeoutMs?: number; // ERC-20 approval wait. Default: 30_000
}

Info Endpoints

const tokens = await router.getTokens(); // Token[]
const chains = await router.getChains(); // Chain[]

Use these to discover what assets and chains are available on the connected aggregator.

License

MIT