Latest — Explore subgraphs is live

Your indexer.
Our decoders.

You write the loop, we hand you decoded events with cursors and reorgs already handled. Deploy it to Railway, Fly, or your own box. No node to run.

Read the docs
sBTCbridge + token
PoX-5staking
BNSnames
SIP-010fungible tokens
SIP-009nfts
Velaramm swaps
Arkadikovaults
ALEXdefi
Zestlending
StackingDAOliquid stacking
sBTCbridge + token
PoX-5staking
BNSnames
SIP-010fungible tokens
SIP-009nfts
Velaramm swaps
Arkadikovaults
ALEXdefi
Zestlending
StackingDAOliquid stacking
sBTCbridge + token
PoX-5staking
BNSnames
SIP-010fungible tokens
SIP-009nfts
Velaramm swaps
Arkadikovaults
ALEXdefi
Zestlending
StackingDAOliquid stacking
sBTCbridge + token
PoX-5staking
BNSnames
SIP-010fungible tokens
SIP-009nfts
Velaramm swaps
Arkadikovaults
ALEXdefi
Zestlending
StackingDAOliquid stacking

Say it once. Send it anywhere.

One typed filter describes the chain event. Each surface gets its own explicit projection — including the conversions and disagreements a spread would hide.

Read about filters
the filter
import { on } from "@secondlayer/stacks/filters";

const whales = on.ftTransfer({
  assetIdentifier:
    "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token::sbtc-token",
  minAmount: 100_000_000n, // ≥ 1 BTC
});
indexsame rows, one cursor
sl.index.events.list(whales.toIndexParams({ limit: 50 }));
streamshold the tip, reorg-aware
sl.streams.events.list(whales.toStreamsParams());
subscriptions100_000_000n → "100000000" — converted here, not at JSON.stringify
sl.subscriptions.create({ triggers: [whales.toChainTrigger()], url });
member gating compile error, not an empty result
on.sbtcDeposit({}).toIndexParams();
// Property 'toIndexParams' does not exist. Subscriptions only.

Principals are validated at construction: a contract id where an asset identifier belongs throws naming the field, instead of quietly matching zero rows.

The ABI already knows.

Give a source its contract's ABI and event.input is typed per function — names checked, integers as bigint, wrong fields caught before deploy.

Read about typed handlers
marketplace.tsmarketplace.abi.ts
import { marketplaceAbi } from "./marketplace.abi";

sources: {
  sale: {
    type: "contract_call",
    contractId: MARKETPLACE,
    functionName: "purchase-asset",
    abi: marketplaceAbi,
  },
},
handlers: {
  sale: (event, ctx) => ctx.insert("sales", {
    collection: event.input.collection,
    token_id:   event.input.tokenId,
    amount:     event.input.amount,
  }),
},
Property 'amount' does not exist on type '{ collection: string; tokenId: bigint; price: bigint }'. Did you mean 'price'? ts(2551)
marketplace.abi.ts
// marketplace.abi.ts — generated from the deployed contract
export const marketplaceAbi = {
  functions: [{
    name: "purchase-asset",
    access: "public",
    args: [
      { name: "collection", type: "principal" },
      { name: "token-id",   type: "uint128" },
      { name: "price",      type: "uint128" },
    ],
    /* outputs … */
  }],
} as const;

kebab-case on chain, camelCase in your handler: token-idtokenId. A uint128 arrives as bigint — never a truncated number.

Production is not a test environment.

Run real chain events through your local handler code before you deploy. The first run records a cassette, so every run after is free and offline.

Read about testing
bns-names.test.tsbns-names.ts
import { buildEvent, createTestContext }
  from "@secondlayer/subgraphs/testing";

test("registers a name from a nested tuple", async () => {
  const ctx = createTestContext(bns.schema, {
    block: { height: 167_484 },
  });
  await bns.handlers.bns!(
    buildEvent(bns.sources.bns, {
      topic: "name-register", data,
    }),
    ctx,
  );
  expect(await ctx.rows("names")).toMatchInlineSnapshot();
});
$ sl subgraphs test subgraphs/bns-names.ts --from 167484 --to 167600
✓ 41 events matched · 41 rows written · 1.2s
cassette: cassettes/bns-names.json
$ sl subgraphs test subgraphs/bns-names.ts --offline
✓ replayed cassette · 0 network calls
# and the bug this gate exists for:
✗ events matched, handlers wrote 0 rows
a field-mapping bug — this subgraph would ship empty

Stop rebuilding the chain indexer.
Start shipping your own.

Open data, open SDKs, honest infrastructure — decoded sBTC, PoX, and Clarity calls included.