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

@echomirror/stellar

v0.2.0

Published

EchoMirror SDK Stellar integration — multi-wallet (Freighter, Albedo, xBull), transaction builders, and typed errors

Readme

@echomirror/stellar

Production-ready Stellar integration for the EchoMirror SDK: multi-wallet support (Freighter, xBull, Albedo), a full transaction builder (payments, trustlines, path payments, fee-bumps), a typed error taxonomy, and automatic retry with backoff for transient Horizon failures.

npm install @echomirror/stellar

Quickstart

import { StellarClient, connectWallet } from '@echomirror/stellar'

const stellar = new StellarClient({ network: 'testnet' })

// Auto-detects Freighter → xBull → Albedo, with install guidance if none exist
const { adapter, connection } = await connectWallet({ network: 'testnet' })

const tx = await stellar.buildPaymentTransaction({
  source: connection.publicKey,
  destination: 'G…',
  amount: '12.5',
  memo: 'thanks! ✨',
})

// Wallet signs (popup/extension), SDK submits with retry on transient failures
const result = await stellar.signAndSubmit(adapter, tx)
console.log('on-chain:', result.hash)

Server-side (Node) there is no wallet — sign with a Keypair:

import { Keypair, StellarClient } from '@echomirror/stellar'

const stellar = new StellarClient({ network: 'testnet' })
const tx = await stellar.buildPaymentTransaction({ source: kp.publicKey(), destination, amount: '5' })
tx.sign(kp)
await stellar.submitTransaction(tx)

Which wallet method for which use case?

| Use case | Call | Notes | | --- | --- | --- | | "Connect wallet" button, no preference | connectWallet() | Tries Freighter → xBull → Albedo; Albedo works in any browser (popup), so it is the universal fallback | | Let the user pick a wallet | detectWallets() → render list → adapter.connect() | Only returns wallets usable right now | | Force one specific wallet | getWalletAdapter('freighter' \| 'xbull' \| 'albedo') | Throws typed WalletNotFoundError (with install URL) if unavailable | | Sign a transaction you built | stellar.signAndSubmit(adapter, tx) | Or adapter.signTransaction(xdr, { networkPassphrase }) if you submit yourself | | Detect which network the user's wallet is on | adapter.connect()connection.network | Only Freighter exposes its own network selection; xBull/Albedo use the network you pass to connectWallet/the adapter constructor | | No browser (Node/server) | none — sign with Keypair, then stellar.submitTransaction(signedXdr) | connectWallet() throws a WalletNotFoundError explaining this | | Legacy EchoMirror API flows (ECHO token) | sendEcho, getBalance, … | Unchanged signatures; sendEcho now signs with any available wallet, not just Freighter |

Which transaction builder for which use case?

| Use case | Call | | --- | --- | | Send XLM or an issued asset | buildPaymentTransaction({ source, destination, amount, asset? }) | | Send XLM to a brand-new (unfunded) account | buildPaymentTransaction({ …, createDestination: true }) — becomes a createAccount op | | Let an account hold a new asset | buildTrustlineTransaction({ source, asset, limit? }) | | Gift asset B while paying in asset A (exact spend) | buildPathPaymentStrictSend({ sendAsset, sendAmount, destAsset, … }) | | Deliver an exact amount of asset B (capped spend) | buildPathPaymentStrictReceive({ sendAsset, destAsset, destAmount, … }) | | Quote a conversion before committing | findPaymentPath({ type, … }) | | Sponsor someone else's transaction fee | buildFeeBumpTransaction({ feeSource, innerTransaction }) |

Path payments quote Horizon automatically when you omit path/destMin/sendMax and protect you with a slippage bound (slippageBps, default 0.5%).

By default builders preflight: they check the destination exists, the trustline is in place, and the source's spendable XLM (balance minus base reserve and liabilities) covers the amount — so users never sign a doomed transaction. Pass preflight: false to skip the extra Horizon calls.

Error taxonomy

Every failure throws a StellarSdkError subclass with a machine-readable code and an actionable message. error.retryable tells you if resubmitting can ever succeed:

| Code | Class | Retryable | Typical cause | | --- | --- | --- | --- | | WALLET_NOT_FOUND | WalletNotFoundError | no | Extension not installed / Node environment | | WALLET_USER_REJECTED | WalletUserRejectedError | no | User dismissed the wallet popup | | WALLET_CONNECTION_FAILED | WalletConnectionError | no | Any other wallet failure | | ACCOUNT_NOT_FOUND / DESTINATION_NOT_FOUND | AccountNotFoundError | no | Account not funded on this network | | INSUFFICIENT_BALANCE | InsufficientBalanceError | no | op_underfunded, op_low_reserve, fee > balance | | TRUSTLINE_MISSING | TrustlineMissingError | no | op_no_trust, op_src_no_trust, op_no_issuer | | TRUSTLINE_LIMIT_EXCEEDED | TrustlineLimitExceededError | no | op_line_full | | PATH_NOT_FOUND | PathNotFoundError | no | No liquidity / price moved beyond slippage | | TX_BAD_SEQUENCE | BadSequenceError | no — rebuild | Stale sequence number | | TX_EXPIRED | TransactionExpiredError | no — rebuild | Time bounds passed | | TX_INSUFFICIENT_FEE | InsufficientFeeError | no — rebuild or fee-bump | Surge pricing | | TX_MALFORMED | TransactionMalformedError | no | Bad envelope / signatures / inputs | | TX_FAILED | TransactionFailedError | no | Any other operation failure (codes attached) | | NETWORK_TIMEOUT | HorizonTimeoutError | yes | Horizon 504 — identical resubmit is hash-idempotent | | RATE_LIMITED | HorizonRateLimitError | yes | Horizon 429 (honours Retry-After) | | HORIZON_UNAVAILABLE | HorizonUnavailableError | yes | 5xx / network unreachable |

submitTransaction retries only the retryable class automatically (full-jitter exponential backoff, 3 retries by default — tune via new StellarClient({ retry: { … } })). Permanent failures are never retried: resubmitting a tx_bad_seq or op_underfunded envelope cannot succeed. Resubmitting after a timeout is safe because Horizon dedupes by transaction hash and returns the original result.

Testing

npm test                    # unit tests, offline, mocked Horizon/wallets
npm run test:integration    # live Stellar testnet: Friendbot-funded accounts,
                            # every tx type, on-chain assertions, real error mapping
npm run test:e2e            # real Freighter extension in Chromium via Playwright:
                            # onboarding → testnet → connect popup → sign popup →
                            # payment asserted on-chain

The e2e run downloads the Freighter extension from the Chrome Web Store on first use (node e2e/scripts/fetch-freighter.mjs) and drives its actual UI. Selectors live in one place (e2e/helpers.ts) and are pinned to Freighter's data-testids.

In CI, the unit suite runs on every PR via js-ci.yml. The two suites that need live external infrastructure — the public testnet and the real Freighter extension — run nightly and on demand via .github/workflows/stellar-live.yml, following the same convention as sync-live-testnet.yml.

Note: modern Freighter does not inject window.freighterApi — pages must use @stellar/freighter-api (postMessage). The FreighterAdapter handles both, preferring an injected global when present (old versions, tests) and falling back to the npm package (real browsers today).