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

@signet-sh/sdk

v0.4.5

Published

TypeScript SDK for Signet Orders - create, sign, and verify orders compatible with signet-types

Readme

@signet-sh/sdk

npm version CI License

TypeScript SDK for creating and signing Signet orders compatible with the Rust signet-types crate.

Installation

pnpm add @signet-sh/sdk viem

Quick Start

import { UnsignedOrder, orderHash, MAINNET } from "@signet-sh/sdk";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

// Create a wallet client
const account = privateKeyToAccount("0x...");
const client = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
});

// Build and sign an order
const signedOrder = await UnsignedOrder.new()
  .withInput("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 1000000n) // 1 USDC
  .withOutput(
    "0x0000000000000000000000000000000000000000", // Native token
    500000000000000000n, // 0.5 ETH
    account.address,
    1 // Mainnet
  )
  .withDeadline(BigInt(Math.floor(Date.now() / 1000) + 3600)) // 1 hour
  .withChain({
    chainId: MAINNET.rollupChainId,
    orderContract: MAINNET.rollupOrders,
  })
  .sign(client);

// Compute the order hash
const hash = orderHash(signedOrder);

Usage

Computing Order Hash from Existing Data

import { orderHash } from "@signet-sh/sdk";
import type { SignedOrder } from "@signet-sh/sdk";

const order: SignedOrder = {
  permit: {
    permit: {
      permitted: [{ token: "0x...", amount: 1000000n }],
      nonce: 12345n,
      deadline: 1700000000n,
    },
    owner: "0x...",
    signature: "0x...",
  },
  outputs: [
    {
      token: "0x...",
      amount: 500000n,
      recipient: "0x...",
      chainId: 1,
    },
  ],
};

const hash = orderHash(order);

Submitting Orders to the Transaction Pool

After signing an order, submit it to the Signet transaction pool for fillers to discover and execute:

import { createTxCacheClient } from "@signet-sh/sdk/client";

// Create a client pointing to the Signet tx-cache
const txCache = createTxCacheClient("https://tx.signet.sh");

// Submit a signed order
const { id } = await txCache.submitOrder(signedOrder);
console.log(`Order submitted with ID: ${id}`);

Preflight Checks

Before submitting an order, verify it can actually execute on-chain:

import { checkOrderFeasibility } from "@signet-sh/sdk";
import type { FeasibilityIssue } from "@signet-sh/sdk";

const result = await checkOrderFeasibility(publicClient, signedOrder);

if (!result.feasible) {
  for (const issue of result.issues) {
    console.warn(`${issue.type}: ${issue.message}`);
  }
}

checkOrderFeasibility runs all checks and returns a FeasibilityResult with a list of issues:

| Issue Type | Description | | ------------------------ | ------------------------------------------------------ | | insufficient_balance | Owner lacks enough tokens for one or more inputs | | insufficient_allowance | Owner has not approved Permit2 for the required amount | | nonce_used | The Permit2 nonce has already been consumed | | deadline_expired | The order deadline is in the past |

Each FeasibilityIssue includes token, required, and available fields where applicable.

Individual Checks

For more targeted checks, use the lower-level functions directly:

import { hasPermit2Approval, isNonceUsed, randomNonce } from "@signet-sh/sdk";

// Check Permit2 allowance for specific tokens
const approved = await hasPermit2Approval(publicClient, ownerAddress, [
  { token: usdcAddress, amount: 1000000n },
]);

// Check if a nonce has been consumed
const used = await isNonceUsed(publicClient, ownerAddress, nonce);

// Generate a random 256-bit nonce
const nonce = randomNonce();

On-Chain Operations

The SDK exports ABIs and constants for on-chain operations. Use viem directly for contract interactions:

Permit2 Approvals

import { PERMIT2_ADDRESS } from "@signet-sh/sdk";
import { erc20Abi, maxUint256 } from "viem";

// Approve Permit2 to spend your tokens (one-time per token)
await walletClient.writeContract({
  address: tokenAddress,
  abi: erc20Abi,
  functionName: "approve",
  args: [PERMIT2_ADDRESS, maxUint256],
});

// Check current allowance
const allowance = await publicClient.readContract({
  address: tokenAddress,
  abi: erc20Abi,
  functionName: "allowance",
  args: [ownerAddress, PERMIT2_ADDRESS],
});

For tokens like USDT that require resetting allowance to zero before setting a new value, use ensurePermit2Approval:

import { ensurePermit2Approval } from "@signet-sh/sdk";

// Handles USDT-style tokens automatically
const { approved, txHash } = await ensurePermit2Approval(
  walletClient,
  publicClient,
  {
    token: usdtAddress,
    owner: account.address,
    amount: 1000000n,
  }
);

Bridging with Passage

import { passageAbi, MAINNET } from "@signet-sh/sdk";

// Bridge native ETH to Signet
await walletClient.writeContract({
  address: MAINNET.hostPassage,
  abi: passageAbi,
  functionName: "enter",
  args: [recipientAddress],
  value: 1000000000000000000n, // 1 ETH
});

// Bridge ERC20 tokens to Signet (requires prior token approval to Passage)
await walletClient.writeContract({
  address: MAINNET.hostPassage,
  abi: passageAbi,
  functionName: "enterToken",
  args: [MAINNET.rollupChainId, recipientAddress, tokenAddress, amount],
});

WETH Wrapping

import { wethAbi, getTokenAddress, MAINNET } from "@signet-sh/sdk";

const wethAddress = getTokenAddress("WETH", MAINNET.hostChainId, MAINNET)!;

// Wrap ETH → WETH
await walletClient.writeContract({
  address: wethAddress,
  abi: wethAbi,
  functionName: "deposit",
  value: 1000000000000000000n, // 1 ETH
});

// Unwrap WETH → ETH
await walletClient.writeContract({
  address: wethAddress,
  abi: wethAbi,
  functionName: "withdraw",
  args: [1000000000000000000n], // 1 WETH
});

Reading Output Witness

import { rollupOrdersAbi, MAINNET } from "@signet-sh/sdk";

const { witnessHash, witnessTypeString } = await publicClient.readContract({
  address: MAINNET.rollupOrders,
  abi: rollupOrdersAbi,
  functionName: "outputWitness",
  args: [outputs],
});

Chain Configurations

import { MAINNET, PARMIGIANA } from "@signet-sh/sdk";

// Mainnet configuration
console.log(MAINNET.hostChainId); // 1n
console.log(MAINNET.rollupChainId); // 519n

// Parmigiana testnet
console.log(PARMIGIANA.hostChainId); // 3151908n
console.log(PARMIGIANA.rollupChainId); // 88888n

Subpath Imports

// Import specific modules for smaller bundle sizes
import { MAINNET, PARMIGIANA } from "@signet-sh/sdk/constants";
import { UnsignedOrder } from "@signet-sh/sdk/signing";
import type { SignedOrder } from "@signet-sh/sdk/types";
import { rollupOrdersAbi, passageAbi, wethAbi } from "@signet-sh/sdk/abi";
import { createTxCacheClient } from "@signet-sh/sdk/client";
import { ensurePermit2Approval } from "@signet-sh/sdk/permit2";

Bundles

⚠️ SECURITY WARNING: Bundles are private information and MUST be kept safe.

Never log, share, or transmit bundles over unencrypted connections. Leaked bundles expose your trading strategy and can be front-run.

Bundles are atomic transaction packages for block builders:

import {
  SignetEthBundleBuilder,
  createTxCacheClient,
  type Hex,
} from "@signet-sh/sdk";

// Build a bundle from signed transactions
const signedTx: Hex = "0x02f8..."; // Your signed transaction

const bundle = SignetEthBundleBuilder.new()
  .withTx(signedTx)
  .withBlockNumber(12345678n)
  .build();

// Submit to tx-cache
const txCache = createTxCacheClient("https://tx.signet.sh");
const response = await txCache.submitBundle(bundle);
console.log("Bundle ID:", response.id);

Bundle with host transactions and timing constraints:

const bundle = SignetEthBundleBuilder.new()
  .withTxs([tx1, tx2]) // Rollup transactions
  .withHostTx(hostTx) // Host chain transaction
  .withBlockNumber(12345678n)
  .withMinTimestamp(Math.floor(Date.now() / 1000))
  .withMaxTimestamp(Math.floor(Date.now() / 1000) + 120)
  .build();

Simulate a bundle before submission:

import { SignetCallBundleBuilder, serializeCallBundle } from "@signet-sh/sdk";

const callBundle = SignetCallBundleBuilder.new()
  .withTx(signedTx)
  .withBlockNumber(12345679n)
  .withStateBlockNumber("latest")
  .build();

const serialized = serializeCallBundle(callBundle);
// Use with signet_callBundle RPC method

Parsing RollupOrders Events

Use viem's parseEventLogs with the SDK's ABI and event types for typed event indexing:

import { createPublicClient, http, parseEventLogs } from "viem";
import {
  rollupOrdersAbi,
  MAINNET,
  type OrderEvent,
  type FilledEvent,
  type SweepEvent,
} from "@signet-sh/sdk";

const client = createPublicClient({ chain, transport: http(rpcUrl) });

// Fetch logs from a block range
const logs = await client.getLogs({
  address: MAINNET.rollupOrders,
  fromBlock: 100n,
  toBlock: 200n,
});

// Parse all RollupOrders events with full type safety
const events = parseEventLogs({ abi: rollupOrdersAbi, logs });

for (const event of events) {
  switch (event.eventName) {
    case "Order": {
      const { deadline, inputs, outputs } = event.args as OrderEvent;
      console.log(`Order with ${inputs.length} inputs, deadline ${deadline}`);
      break;
    }
    case "Filled": {
      const { outputs } = event.args as FilledEvent;
      console.log(`Filled ${outputs.length} outputs`);
      break;
    }
    case "Sweep": {
      const { recipient, token, amount } = event.args as SweepEvent;
      console.log(`Sweep ${amount} of ${token} to ${recipient}`);
      break;
    }
  }
}

RPC Patterns

The SDK exports ABIs and token utilities. Use viem directly for RPC operations.

Fetching Passage Events

import { createPublicClient, http, getContract } from "viem";
import {
  passageAbi,
  PARMIGIANA,
  getTokenAddress,
  resolveTokenSymbol,
} from "@signet-sh/sdk";

const client = createPublicClient({ chain, transport: http(rpcUrl) });

const passage = getContract({
  address: PARMIGIANA.hostPassage,
  abi: passageAbi,
  client,
});

// Get Enter events (native ETH entries)
const enterLogs = await passage.getEvents.Enter({
  rollupChainId: PARMIGIANA.rollupChainId,
  rollupRecipient: userAddress,
});

// Get EnterToken events (ERC20 entries)
const enterTokenLogs = await passage.getEvents.EnterToken({
  rollupChainId: PARMIGIANA.rollupChainId,
  rollupRecipient: userAddress,
});

// Resolve token symbols from logs
for (const log of enterTokenLogs) {
  const symbol = resolveTokenSymbol(
    log.args.token,
    PARMIGIANA.hostChainId,
    PARMIGIANA
  );
  console.log(`Entered ${log.args.amount} of ${symbol}`);
}

Fetching Token Balances

import { createPublicClient, http, erc20Abi, formatUnits } from "viem";
import { PARMIGIANA, getTokenAddress, getTokenDecimals } from "@signet-sh/sdk";

const client = createPublicClient({ chain, transport: http(rpcUrl) });

// Fetch native ETH balance
const ethBalance = await client.getBalance({ address: userAddress });

// Fetch ERC20 balance
const wethAddress = getTokenAddress("WETH", PARMIGIANA.hostChainId, PARMIGIANA);
const wethBalance = await client.readContract({
  address: wethAddress,
  abi: erc20Abi,
  functionName: "balanceOf",
  args: [userAddress],
});

// Format with correct decimals (uses testnet override if available)
const decimals = getTokenDecimals("WETH", PARMIGIANA);
const formatted = formatUnits(wethBalance, decimals);

Batch Balance Fetching

import { getTokenAddress, getTokenDecimals } from "@signet-sh/sdk";

const tokens = ["WETH", "WBTC", "USDC"] as const;

const balances = await Promise.all(
  tokens.map(async (symbol) => {
    const address = getTokenAddress(symbol, chainId, config);
    if (!address) return { symbol, balance: 0n };

    const balance = await client.readContract({
      address,
      abi: erc20Abi,
      functionName: "balanceOf",
      args: [userAddress],
    });

    return { symbol, balance, decimals: getTokenDecimals(symbol, config) };
  })
);

API Reference

Types

  • SignedOrder - A signed order ready for submission
  • SignedFill - A signed fill for filling orders
  • SignetEthBundle - Bundle for signet_sendBundle
  • SignetCallBundle - Bundle for signet_callBundle (simulation)
  • Permit2Batch - Permit2 batch transfer data
  • Output - Order output specification
  • TokenPermissions - Token permission for Permit2
  • OrderEvent - Parsed args from an Order event
  • FilledEvent - Parsed args from a Filled event
  • SweepEvent - Parsed args from a Sweep event
  • FeasibilityResult - Result of a preflight feasibility check
  • FeasibilityIssue - A single issue preventing order execution
  • FeasibilityIssueType - Issue category: "insufficient_balance" | "insufficient_allowance" | "nonce_used" | "deadline_expired"
  • Flow - Entry mechanism type: "passage" or "orders"
  • TokenSymbol - Supported token symbols

Functions

  • orderHash(order) - Compute the order hash
  • orderHashPreImage(order) - Get the pre-image used for hashing
  • normalizeSignature(sig) - Normalize ECDSA signature S-value
  • serializeEthBundle(bundle) - Serialize bundle for JSON-RPC
  • serializeCallBundle(bundle) - Serialize call bundle for JSON-RPC
  • createTxCacheClient(url) - Create a tx-cache client for bundle submission
  • ensurePermit2Approval(walletClient, publicClient, params) - Smart Permit2 approval with USDT handling
  • checkOrderFeasibility(client, order) - Check if an order can execute on-chain
  • hasPermit2Approval(client, owner, tokens) - Check Permit2 allowance for tokens
  • isNonceUsed(client, owner, nonce) - Check if a Permit2 nonce has been consumed
  • randomNonce() - Generate a random 256-bit Permit2 nonce
  • getTokenDecimals(symbol, config?) - Get token decimals with chain-specific overrides
  • needsWethWrap(symbol, direction, flow) - Check if ETH needs wrapping for operation

Classes

  • UnsignedOrder - Builder for creating unsigned orders
  • UnsignedFill - Builder for creating unsigned fills
  • SignetEthBundleBuilder - Builder for creating eth bundles
  • SignetCallBundleBuilder - Builder for creating call bundles

Client

  • createTxCacheClient(baseUrl) - Create a client for submitting orders to the transaction pool
    • submitOrder(order) - Submit a signed order

Constants

  • PERMIT2_ADDRESS - Canonical Permit2 contract address
  • MAINNET - Mainnet chain configuration
  • PARMIGIANA - Parmigiana testnet configuration

ABIs

  • passageAbi - Passage bridge contract ABI
  • wethAbi - WETH contract ABI
  • rollupOrdersAbi - Rollup orders contract ABI
  • permit2Abi - Permit2 contract ABI

Compatibility

This SDK produces signed orders that are byte-for-byte compatible with the Rust signet-types crate. The order hash computation matches exactly, ensuring interoperability between TypeScript and Rust implementations.

Development

pnpm install    # Install dependencies
pnpm build      # Build the package
pnpm test       # Run tests in watch mode
pnpm test:run   # Run tests once
pnpm lint       # Run ESLint
pnpm format     # Format with Prettier
pnpm typecheck  # Type check

License

MIT OR Apache-2.0