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

stellar-soroban-snap-connector

v0.1.0

Published

Dapp connector for the Stellar Soroban MetaMask Snap: typed SEP-43 client, Freighter-compatible facade, and Stellar Wallets Kit module.

Downloads

165

Readme

stellar-soroban-snap-connector

Dapp connector for the Stellar Soroban MetaMask Snap: a typed SEP-0043 client, a drop-in @stellar/freighter-api-compatible facade, and a Stellar Wallets Kit module. Zero runtime dependencies.

Independent software, not affiliated with or endorsed by the Stellar Development Foundation.

Install

npm install stellar-soroban-snap-connector

1. Typed client (StellarSnap)

import { StellarSnap, StellarSnapError } from 'stellar-soroban-snap-connector';

const snap = new StellarSnap(); // auto-detects MetaMask via EIP-6963

if (await snap.isAvailable()) {
  const { address } = await snap.connect(); // installs the snap + requests access

  const { signedTxXdr, signerAddress } = await snap.signTransaction(xdr, {
    networkPassphrase: 'Test SDF Network ; September 2015',
  });

  const { signedAuthEntry } = await snap.signAuthEntry(entryXdr); // Soroban auth
  const { signedMessage } = await snap.signMessage('hello'); // SEP-53
}

Errors throw StellarSnapError { message, code } with SEP-0043 codes (-1 internal, -2 external service, -3 invalid request, -4 user rejected). Every typed method validates the response shape at runtime before returning it. connect()/isInstalled() verify that the snap version MetaMask actually installed matches the pinned release, and every call that reaches the snap (including through the Freighter facade and the Wallets Kit module) re-checks that on first use, refusing a mismatched install with -3 before the snap is contacted, so a dapp that never calls connect() gets the same guarantee.

During snap development pass { snapId: 'local:http://localhost:8080' }. The version option must be an exact x.y.z semver (no range, no prerelease suffix) and snapId an npm:/local: ID; anything else is rejected at construction, because a range or arbitrary ID would silently defeat the audited-release pin. An npm: ID other than the published one is accepted but logs a console warning.

The signing option bags follow SEP-0043 (networkPassphrase, address, and for signTransaction also submit). submitUrl is declared for shape compatibility but not supported: the snap submits only to its own allowlisted endpoints, and passing a submitUrl is refused with -3 before MetaMask is contacted rather than silently ignored. See the connector API reference for the full method list.

Multiple accounts

The wallet can hold several SEP-0005 accounts (m/44'/148'/x'), added by the user from the snap home page. A connected dapp can enumerate them, switch the wallet-global active account (dialog-confirmed), and target any revealed account per request via the SEP-43 address option:

const { accounts, activeIndex } = await snap.getAccounts();
// [{ index: 0, address: 'G...' }, { index: 1, address: 'G...' }]

await snap.setActiveAccount(1); // user confirms; every site sees the switch

// Or select a signing account per request, without switching:
await snap.signTransaction(xdr, { address: accounts[1].address });

Requesting an address the wallet does not hold returns -3.

Which transactions the wallet will sign

The snap decodes every transaction from its XDR and refuses to sign anything it cannot render in full, rather than showing a warning over raw XDR. A transaction containing an operation type it has no renderer for is rejected with -3 before any dialog is shown, and the message names the offending types.

Supported today: payment, createAccount, changeTrust, pathPaymentStrictSend, pathPaymentStrictReceive, manageData, setOptions, accountMerge, invokeHostFunction, extendFootprintTtl, restoreFootprint.

Not yet supported, so signing is refused: the DEX operations (manageBuyOffer, manageSellOffer, createPassiveSellOffer), the liquidity-pool operations, the claimable-balance operations, clawback and clawbackClaimableBalance, setTrustLineFlags, bumpSequence, and the sponsorship operations.

The same fail-closed rule rejects undecodable host functions, contract-call arguments too large or deeply nested to display, embedded authorization entries that cannot be rendered, and Soroban transactions whose footprint is missing (unprepared) or too large to show. If you build Soroban envelopes yourself, simulate or prepare them before requesting a signature so they carry a footprint.

Reading balances

getBalances() returns the account's classic Horizon balances plus any Soroban tokens the user tracks, in one array. Branch on type, never on the asset string:

const { balances } = await snap.getBalances();

const xlm = balances.find((line) => line.type === 'native')?.balance ?? '0';
const tokens = balances.filter((line) => line.type === 'soroban');
// tokens[0].contractId identifies the contract; tokens[0].asset is display only

asset is a display string: XLM for the native asset, CODE:ISSUER for a classic asset, and SYMBOL:CONTRACT_ID for a tracked token. The last two are the same shape, and a token's symbol is reported by its contract, so a contract the user was persuaded to track can call itself USDC. Code that splits asset on : and shows the first field will display exactly that. type and contractId are what make the distinction available without parsing.

tokensUnavailable: true means the wallet's global token-read budget was exhausted and the token rows are missing, not that the account holds none of them. The classic balances are complete either way.

Signing with submit, and recovering from an ambiguous failure

signTransaction(xdr, { submit: true }) signs and broadcasts in one approval. Broadcasting can fail after the user has approved and the envelope has been signed, and one of those failures is genuinely ambiguous: the wallet aborts the Horizon submit at 10 seconds, but Horizon's synchronous endpoint waits for ledger close, so under congestion a transaction that actually lands can still surface as an error.

Do not blind-retry that error. The signed envelope is preserved on it so you can poll instead:

try {
  const { hash } = await snap.signTransaction(xdr, { submit: true });
  return hash;
} catch (error) {
  if (error instanceof StellarSnapError && error.data?.signedTxXdr) {
    // The user DID sign, and the transaction may already be on the ledger.
    // Compute its hash from the returned envelope and poll Horizon before
    // doing anything else. Re-signing or re-submitting blindly risks a
    // duplicate.
    return pollForTransaction(error.data.signedTxXdr);
  }
  throw error;
}

On the Freighter facade the same data is at error.recovery, deliberately not merged into the result fields: the common const { signedTxXdr } = await api.signTransaction(...); if (signedTxXdr) submit(...) pattern must not silently submit an envelope from a call the dapp believes failed. Reaching into error.recovery is an opt-in.

2. Freighter-compatible facade

Same method names and { ...result, error? } convention as @stellar/freighter-api — a near drop-in swap:

import { createFreighterApi } from 'stellar-soroban-snap-connector';

const freighter = createFreighterApi();
const { address, error } = await freighter.requestAccess();
const { signedTxXdr } = await freighter.signTransaction(xdr);

Includes a polling WatchWalletChanges helper.

On failure the result fields stay empty, exactly like @stellar/freighter-api. When a post-approval submission fails after the user already signed, the signed envelope and status are preserved under error.recovery ({ signedTxXdr, signerAddress, hash, status }) — an explicit opt-in, so code that checks if (signedTxXdr) never submits an envelope from a call that reported an error.

3. Stellar Wallets Kit module

import {
  StellarWalletsKit,
  defaultModules,
} from '@creit.tech/stellar-wallets-kit';
import { StellarSnapKitModule } from 'stellar-soroban-snap-connector';

const kit = new StellarWalletsKit({
  modules: [...defaultModules(), new StellarSnapKitModule()],
  // ...your kit config
});

"MetaMask (Stellar Snap)" then appears in the kit's wallet picker with no further integration work.

Repository

Source and documentation: github.com/SentinelFi/stellar-metamask-snap (Apache-2.0).