Skip to content

Repository files navigation

stellar-inspect

Find the Stellar Asset Contracts (SACs) referenced by a Stellar transaction or by Soroban authorization entries, and learn which asset each one wraps.

A signing UI receives CCW67TSZ… calling transfer. This library turns that into "USDC issued by GA5ZSEJY…".

npm install stellar-inspect @stellar/stellar-sdk

The package is published as ES modules. require("stellar-inspect") also works, since Node 22.12+ can load ES modules from CommonJS.

Usage

import { Networks } from "@stellar/stellar-sdk";
import { findSacs } from "stellar-inspect";

const sacs = await findSacs(txEnvelopeXdr, { networkPassphrase: Networks.PUBLIC });
// [
//   { contractId: "CAS3J7GY…", asset: { type: "native" } },
//   { contractId: "CCW67TSZ…", asset: { type: "credit_alphanum4", code: "USDC", issuer: "GA5ZSEJY…" } },
// ]

findSacs accepts a Transaction, a FeeBumpTransaction, an xdr.TransactionEnvelope, an xdr.SorobanAuthorizationEntry, base64 XDR of either, or an array of any of those. Anything else throws a TypeError rather than being reported as a transaction without SACs.

networkPassphrase is the passphrase of the network the input belongs to, the same value you pass to TransactionBuilder. Any network works, including futurenet and local standalone networks, as long as the resolver can serve it.

It looks at:

  • the contract invoked by each InvokeHostFunction operation,
  • every Address-typed argument, recursively through vectors and maps,
  • the full authorization tree of each auth entry, including sub-invocations,
  • the Soroban footprint (contracts whose data is read or written),
  • createContract operations: the deployer address, constructor arguments, and for SAC deployments the asset itself, taken from the transaction without a lookup.

Contract addresses are deduplicated and resolved in parallel. Anything that is not a SAC is dropped.

Errors

findSacs rejects, without partial results, when the input is unsupported or when any single lookup fails: a network error, a timeout, an abort, or an unexpected response from the resolver. A contract that does not exist or is not a SAC is not a failure; it is simply left out. Pass a signal to abort pending lookups, for example AbortSignal.timeout(5000) to bound the whole call:

await findSacs(tx, { networkPassphrase: Networks.PUBLIC, signal: AbortSignal.timeout(5000) });

Resolvers

Resolution is pluggable through the Resolver interface: resolve(networkPassphrase, contractId, { signal }) returns the asset, null when the contract is not a SAC, and rejects when the lookup fails.

A resolver serves one network, like an RPC node does. findSacs verifies every result against the passphrase you pass, so a resolver pointed at the wrong network yields no SACs rather than wrong ones.

import { findSacs, httpResolver, rpcResolver, staticResolver } from "stellar-inspect";

// Default for mainnet and testnet: the public sac-resolver service (edge-cached, no RPC load on your side).
// Other networks have no default; pass a resolver.
await findSacs(tx, { networkPassphrase: Networks.PUBLIC });

// The same service addressed explicitly (`/mainnet` or `/testnet`), or your own deployment. One URL per
// network; the contract id is appended to it. Each request times out after 10 s; tune with `timeout`.
await findSacs(tx, { networkPassphrase: Networks.PUBLIC, resolver: httpResolver("https://sac-resolver.lightsail.network/mainnet", { timeout: 3000 }) });

// Read contract instances directly from a Soroban RPC node (a URL or an existing rpc.Server).
await findSacs(tx, { networkPassphrase: Networks.PUBLIC, resolver: rpcResolver("https://rpc.lightsail.network", { timeout: 5000 }) });

// Fully offline, from assets you already know (for example the user's trustlines).
await findSacs(tx, { networkPassphrase: Networks.PUBLIC, resolver: staticResolver(knownSacs) });

Trust

A SAC address is sha256(networkId, asset), so findSacs re-derives the address of every asset a resolver returns, drops anything that does not match, and rebuilds what remains from the verified code and issuer alone. A misconfigured, compromised, or tampered-with resolver, including the public sac-resolver service, can only cause a SAC to be missed; it can never make an address show up as the wrong asset, and nothing it returns reaches you unverified. The RPC resolver applies the same check to the contract instance it reads, so a contract cannot misreport which asset it represents.

Lower-level pieces

import { extractContracts, parseSacInstance } from "stellar-inspect";

// Pure, synchronous: every contract address in the input plus assets being deployed as SACs.
const { contractIds, deployedAssets } = extractContracts(txEnvelopeXdr);

// Parse a contract instance ledger entry yourself.
const asset = parseSacInstance(Networks.PUBLIC, contractId, ledgerEntryData); // AssetInfo | null

Related

About

Find the Stellar Asset Contracts (SACs) touched by a Stellar transaction or Soroban authorization entries.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages