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

@khalander/forge-sdk

v0.1.3

Published

Minimal on-chain payment-logging SDK for the ForgePay showcase contract (Sepolia).

Readme

@khalander/forge-sdk - wallet-native payments for ForgePay (Sepolia)

The complete payments stack for apps that settle on-chain: wallet authentication, user-signed charging, and verifiable on-chain receipts - one SDK, one integration.

1. Install

bun add @khalander/forge-sdk thirdweb

(thirdweb ships the wallet-adapter layer - connectors and TypeScript types. Every payment, auth, and settlement decision is made by ForgePay.)

2. Env

# free at https://thirdweb.com/create-api-key
NEXT_PUBLIC_THIRDWEB_CLIENT_ID=YOUR_THIRDWEB_CLIENT_ID
# your own wallet: every user payment goes straight here. Set once per deployment.
NEXT_PUBLIC_DEV_ADDRESS=0xYOUR_DEV_ADDRESS

3. Authenticate + charge with ForgePay (your money path)

ForgePay authentication is wallet-native: there are no passwords and no sessions to hijack - the wallet is the credential. ForgePayProvider establishes the session, ConnectButton onboards the user (Phantom-first), and useActiveAccount is the logged-in identity for everything downstream:

"use client";

// Everything flows through the SDK: one runtime, one wallet store.
import {
  ConnectButton,
  FORGE_PAY_WALLETS as wallets,
  createThirdwebClient,
  prepareTransaction,
  sepolia,
  sendTransaction,
  toWei,
  useActiveAccount,
} from "@khalander/forge-sdk/react";

const client = createThirdwebClient({
  clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID!,
});

export function PayButton() {
  // Hardcoded per deployment via env - every user payment goes straight here.
  const devAddress = process.env.NEXT_PUBLIC_DEV_ADDRESS! as `0x${string}`;
  // The authenticated user. Null = signed out.
  const account = useActiveAccount();

  async function pay() {
    if (!account) throw new Error("Connect a wallet first");
    const tx = prepareTransaction({
      to: devAddress,
      value: toWei("0.01"),
      chain: sepolia,
      client,
    });
    const { transactionHash } = await sendTransaction({ transaction: tx, account });
    return transactionHash; // <-- save as payTxHash (see §4)
  }

  return (
    <>
      <ConnectButton client={client} chain={sepolia} wallets={wallets} />
      <button onClick={pay}>Subscribe - 0.01 ETH</button>
      {account && <p>Paying as {account.address}</p>}
    </>
  );
}

4. Create the DB table (Drizzle + Postgres assumed)

export const payments = pgTable("payments", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  userAddress: text("user_address").notNull(),
  devAddress: text("dev_address").notNull(),
  amountWei: text("amount_wei").notNull(),
  plan: text("plan").notNull().default("monthly"),
  status: text("status").notNull().default("paid"), // paid | failed
  payTxHash: text("pay_tx_hash"),
  logTxHash: text("log_tx_hash"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});

5. Settle with @khalander/forge-sdk (the on-chain receipt)

Wrap the app once, then settle after the payment succeeds. Settlement is a two-phase commit, both phases signed by the user: (1) value transfer to the dev, (2) ForgePay.logPayment receipt - amount, parties, and plan sealed on-chain, with the protocol counters (totalTxs, totalDevs) advancing:

"use client";

import { ForgePayProvider, useLogPayment } from "@khalander/forge-sdk/react";

export function SubscribeCard() {
  // Same env dev address the payment went to.
  const devAddress = process.env.NEXT_PUBLIC_DEV_ADDRESS! as `0x${string}`;
  return (
    <ForgePayProvider thirdwebClientId={process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID!}>
      <SubscribeInner devAddress={devAddress} />
    </ForgePayProvider>
  );
}

function SubscribeInner({ devAddress }: { devAddress: `0x${string}` }) {
  const { log, isPending, address } = useLogPayment();

  async function subscribe(payTxHash: `0x${string}`) {
    if (!address) throw new Error("Connect a wallet first");
    // 1. record the real payment in YOUR db
    const row = await db.insert(payments).values({
      userAddress: address,
      devAddress,
      amountWei: toWei("0.01").toString(),
      plan: "monthly",
      status: "paid",
      payTxHash,
    });
    // 2. settle it on-chain through ForgePay
    const { txHash } = await log({
      devAddress,
      userAddress: address,
      amountWei: BigInt(toWei("0.01").toString()),
      plan: "monthly",
    });
    // 3. save the receipt back to the row, show status from YOUR table
    await db.update(payments).set({ logTxHash: txHash }).where(eq(payments.id, row.id));
  }
}

Vanilla (non-React) alternative:

import { createForgePay } from "@khalander/forge-sdk";

const forgePay = createForgePay({
  thirdwebClientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID!,
});
const { txHash } = await forgePay.logPayment(
  { devAddress, userAddress, amountWei, plan: "monthly" },
  account,
);

Verify any settlement on Etherscan: https://sepolia.etherscan.io/tx/<logTxHash> shows the PaymentLogged(dev, user, amount, plan) event, and the contract's totalTxs / totalDevs counters grow with usage.

6. Security architecture

ForgePay's threat model starts from a simple observation: the most secure code is code that cannot be attacked because the attack surface does not exist. Both headline guarantees below are structural - they hold for every deployment, with no configuration to get wrong.

6.1 Re-entrancy: secured by construction

Classic re-entrancy (the DAO, SpankChain, dForce pattern) needs three ingredients: (a) the contract calls out to an untrusted address, (b) state updates happen after that call, (c) value is at stake. ForgePay removes all three:

  1. Zero external calls. logPayment invokes no other contract and performs no .call{value} to users. The only cross-boundary effect is an event emission, which cannot re-enter. There is no fallback function to hijack, no token hook (ERC777, ERC1155Receiver) to trigger, no callback path of any kind.
  2. Check-Effects-Interactions ordering throughout. The two state effects - totalTxs += 1 and the first-seen totalDevs bump - execute before the event emission, so even a hypothetical future extension that adds callbacks inherits the safe ordering.
  3. No value custody, ever. The contract is not payable outside pure logging, holds no balances, and owns no allowances. A re-entrancy exploit with nothing to drain is a non-issue - the maximum extractable value of this contract is exactly zero.
  4. Minimal privileged surface. The only authority in the system is owner, fixed immutably at deploy time (constructor), with no transfer/renounce ceremony to mismanage and no admin functions touching funds - because there are no funds to administer.

Net effect: the entire re-entrancy family (single-function, cross-function, cross-contract, read-only) is inapplicable to ForgePay - not mitigated, but structurally absent.

6.2 Timestamp dependence: secured by elimination

Timestamp attacks (miner/validator time games, deadline bypasses, vesting and lottery manipulation) need time-gated logic: expiries, lockups, windows, or randomness derived from block.timestamp. ForgePay's money path contains none of these:

  1. No deadlines, expiries, or lockups. Settlement is atomic per transaction: pay, then log. There is no window in which timing changes the outcome, so no validator can profit by shifting a timestamp.
  2. No on-chain randomness or sequencing from block data. Ordering, pricing, and plan selection all happen off-chain in the integrator's app; the chain only records the result. Nothing to grind, nothing to front-run for advantage.
  3. block.timestamp appears exactly once - as the informational startedAt field in the showcase-only flagSubscription, which moves no funds, gates no access, and feeds no decision. A skewed timestamp there changes a displayed number and nothing else.

6.3 Transparency and least privilege

  • Verified source, public counters. The deployed bytecode exactly matches the published source (Etherscan Exact Match); totalTxs and totalDevs let anyone audit platform usage without trusting our servers.
  • User-signed everything. Both settlement phases require the subscriber's wallet signature. The SDK holds no keys, runs no relayer, and cannot move a single wei on anyone's behalf.
  • Scope honesty: demo-grade deployment, Sepolia testnet only - no audit claims, no mainnet.