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

@synfin/client

v0.1.7

Published

Synfin partner client: get a best-execution quote, plan a route, execute it from your own Canton wallet, and track it to a terminal state. Talks to the hosted Synfin API; holds no keys and drives no ledger.

Readme

@synfin/client

The Synfin partner client. Get a best-execution quote across Canton venues, plan a route, execute it from your own wallet, and track it to a terminal state. Synfin holds no keys and drives no ledger: the quote and the plan come from the hosted API, and your wallet signs and sends.

  • Docs: https://synfin.xyz/docs
  • SDK guide: https://synfin.xyz/docs/sdk

Status: v0. The API surface may evolve with design-partner feedback before a 1.0. Pin an exact version and watch the changelog.

What is live today (the honest state)

  • Quote and plan are live against the hosted API. Quotes aggregate the Canton venues; Tradecraft is the executable venue today (others appear in the quote for comparison but are not yet plan-buildable).
  • Execution is non-custodial from your own wallet. You bring a WalletAdapter on the taker's party; Synfin holds no keys.
  • Fees are DISCLOSED, not yet COLLECTED on-chain. Your plan's clientFees shows the service fee and your integrator split so you can display them, but on-ledger fee collection is gated behind a flag that is off today (FEE_COLLECTION_ENABLED = false). When it turns on, the fee rides the swap as an atomic CIP-0112 batch leg — and it is wallet- dependent: a wallet whose Canton participant lacks the batching package degrades the swap to fee-less honestly (never a broken swap, never a silent charge). Loop's participant is pending that package upload; other wallets that have it collect atomically. Until the flag flips, every swap is fee-less regardless.
  • Guaranteed minimums are enforced on-ledger. minReceive rides the deposit memo; a bad quote aborts and your funds never leave the wallet.

Install

npm install @synfin/client

Requires Node 18+ (built-in fetch) or any modern browser. Zero runtime dependencies.

The five-step journey

1. Get a key

Create a free API key at portal.synfin.xyz. It carries your rate limit and your fee configuration. Then:

import { createClient } from '@synfin/client';

const synfin = createClient({ apiKey: process.env.SYNFIN_API_KEY! });

2. Quote, with your fee

A keyed quote returns each venue's net, ranked best-first, plus your disclosed clientFees — the 15 bps Synfin service fee and your integrator split (your feeBps, capped, split 80/20 with Synfin). These are DISCLOSED for you to show; on-ledger collection is flag-gated off today (see "What is live today").

const quote = await synfin.getQuote({
  from: 'CC',
  to: 'USDCx',
  amount: '100',
  feeBps: 30, // your integrator fee (0 to the cap)
  feeRecipient: 'yourparty::1220...', // where your fee settles
});

const best = quote.venues.find((v) => v.available);
console.log(best?.venueId, best?.net, best?.clientFees?.userReceives);

3. Plan

Ask the API for a one-call execution plan for the venue you chose. The server computes the memo floor (minReceive) and the fee amounts; you never do. Pass a stable idempotencyKey so retries return the same plan.

const plan = await synfin.createPlan({
  from: 'CC',
  to: 'USDCx',
  amount: '100',
  venueId: best!.venueId,
  takerParty: 'yourparty::1220...',
  idempotencyKey: crypto.randomUUID(),
  feeBps: 30,
  feeRecipient: 'yourparty::1220...',
});

4. Execute, from your own wallet

You provide a WalletAdapter that acts on the taker's own party (a test harness implements it for tests; a Loop-class wallet implements it in production). executePlan drives it through the plan steps.

import { executePlan, type WalletAdapter } from '@synfin/client';

const wallet: WalletAdapter = {
  sendDeposit: (step) => myWallet.createTransferOffer(step),
  lockFeeEscrow: () => Promise.resolve(null),
  withdrawDeposit: (id) => myWallet.withdraw(id),
  depositActive: (id) => myWallet.offerActive(id),
  observePayout: ({ instrument, sinceIso }) =>
    myWallet.received(instrument, sinceIso),
};

const handle = await executePlan(plan, {
  wallet,
  hooks: { onStatus: (s) => console.log(s.status, s.note) },
});

5. Track

Observe the taker's own ledger view and derive the partner status until it is terminal (COMPLETED, SLIPPAGE_FAILED, REFUNDED, or ABORTED).

import { isPartnerTerminal, track } from '@synfin/client';

let state = await track(handle, { wallet });
while (!isPartnerTerminal(state.status)) {
  await new Promise((r) => setTimeout(r, 3000));
  state = await track(handle, { wallet });
}
console.log('final:', state.status, state.payoutAmount);

Errors

Non-2xx API responses throw a SynfinApiError with the HTTP status and the machine-readable code:

import { SynfinApiError } from '@synfin/client';

try {
  await synfin.getQuote({ from: 'CC', to: 'USDCx', amount: '100' });
} catch (err) {
  if (err instanceof SynfinApiError) console.error(err.status, err.code, err.message);
}

What this package is not

It talks only to the hosted Synfin API and your wallet. It contains no venue adapter code, no fee-schema logic, and no server internals: pricing and the memo floor are computed server-side and disclosed to you, never recomputed here.

License

MIT. Copyright Cayvox Labs.