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

@mirageprivacy/sdk

v0.4.3

Published

SDK for private transfers on Mirage

Readme

@mirageprivacy/sdk

TypeScript SDK for private transfers on the Mirage protocol. It requests API-authored pricing, deploys the exact quoted escrow, obtains a quote-bound execution approval, submits the encrypted Nomad Signal, and polls recipient transfers.

Install

npm install @mirageprivacy/sdk viem

viem is a peer dependency.

Quick start

import { networks, prepareTransfer, getTokenMetadata, getTokenBalance } from "@mirageprivacy/sdk";
import { createPublicClient, createWalletClient, http, custom, parseUnits, formatUnits } from "viem";
import { mainnet } from "viem/chains";

const publicClient = createPublicClient({ chain: mainnet, transport: http() });
const walletClient = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) });

const TOKEN = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // USDC
const RECIPIENT = "0x...";

const token = await getTokenMetadata(TOKEN, publicClient);
const balance = await getTokenBalance(TOKEN, walletClient.account.address, publicClient);
const amount = parseUnits("100", token.decimals);

// 1. Preview fees
const prepared = await prepareTransfer({
  tokenAddress: TOKEN,
  recipientAddress: RECIPIENT,
  amount,
  walletClient,
  publicClient,
  network: networks.ethereum,
});

console.log(
  `Service fee: ${formatUnits(prepared.fees.serviceFee.amount, token.decimals)} ${token.symbol}`,
);

// 2. Execute transfer
for await (const event of prepared.execute()) {
  switch (event.step) {
    case "fees":    console.log("Fees calculated");                          break;
    case "approve": console.log(`Approved: ${event.hash}`);                  break;
    case "deploy":  console.log(`Escrow: ${event.escrowAddress}`);           break;
    case "signal":  console.log(`Signal sent: ${event.response}`);           break;
    case "complete": console.log(`Delivered ${event.transfers.length} row(s)`); break;
  }
}

Staged execution

Consumers that expose separate approval and deployment controls can use the same prepared transfer without reimplementing protocol logic:

const prepared = await prepareTransfer({
  transfers,
  senderAddress: walletClient.account.address,
  publicClient,
  network: networks.ethereum,
});

// Approve button. The generator yields immediately after each token approval.
const approvals = prepared.approve(walletClient);
let checkpoint;
while (true) {
  const next = await approvals.next();
  if (next.done) {
    checkpoint = next.value;
    break;
  }
  console.log(next.value.hash);
}

// Deploy button. Persist `deployed.secrets` immediately.
const deployed = await prepared.deploy(walletClient, checkpoint);

// Automatically submit and monitor after deployment.
for await (const event of prepared.complete(walletClient, deployed.secrets)) {
  console.log(event.step);
}

ApprovalCheckpoint and TransferSecrets are serializable stage boundaries. The latter includes the blinding scalar, quote commitment, and opaque sealed pricing authorization. Persist it immediately: a reload must submit the same authorization that produced the deployed constructor.

Networks

Built-in configs for ethereum, sepolia, and tempo:

import { networks, createNetworkConfig } from "@mirageprivacy/sdk";

// Use a built-in config directly
const network = networks.ethereum;

// Or customize transport and attestation policy.
const custom = createNetworkConfig("ethereum", {
  rpcUrl: "https://my-rpc.example.com",
  attestation: { maxAgeSecs: 180 },
});

Attestation and Signal submission are routed through the API server's nomad proxy at {apiServer}/nomad/{chainId}, so NetworkConfig has no nomadUrl field and nodes are never contacted directly. Pointing at a local nomad node now requires an API server with the proxy configured.

Transfer lifecycle

prepareTransfer returns a PreparedTransfer with the API quote in .fees and an async .execute() pipeline:

  1. fees - Public API service fee and exact funding requirements
  2. approve - One exact approval per ERC-20 funding asset
  3. deploy - Exact API-quoted escrow deployment
  4. compliance - Execution approval bound to the deployment and quote
  5. signal - Minimal encrypted Signal envelope submission to Nomad
  6. complete - Transfer event observed on-chain

One ERC-20 row deploys EscrowERC20, one native row deploys EscrowNative, and two or more rows deploy EscrowBatch.

Cancellation

Pass an AbortSignal to cancel mid-transfer:

const controller = new AbortController();

const prepared = await prepareTransfer({
  // ...params,
  abortSignal: controller.signal,
});

// Cancel from UI, network change, etc.
controller.abort();

If aborted before any transactions are sent, TransferAbortedError is thrown with no escrowAddress. If aborted after escrow deployment, the error includes escrowAddress for manual recovery.

Resuming a transfer

If a transfer fails after the escrow is deployed (e.g. account change, abort, timeout), you can resume from where it left off:

const prepared = await prepareTransfer({
  // ...same params,
  resume: savedTransferSecrets,
});

for await (const event of prepared.execute()) {
  // picks up from compliance/signal step
}

Pricing

All amounts are bigint in raw token units. Use viem's parseUnits/formatUnits at the boundary.

const prepared = await prepareTransfer({ /* ... */ });
const { fees } = prepared;

fees.serviceFee;      // one public { asset, amount } quote
fees.approvalGasEstimate; // optional sum of exact ERC-20 approval gas units
fees.deploymentGasEstimate; // optional API-simulated escrow deployment gas units
fees.totalWalletGasEstimate; // optional approvals + deployment gas units
fees.rewardAsset;     // escrow reward denomination
fees.rewardAmount;    // complete reward pot; internal split remains private
fees.depositByAsset;  // exact principal + reward funding by asset
fees.msgValue;        // exact native amount supplied during deployment

Multiply totalWalletGasEstimate by the current chain gas price to estimate the wallet's native-token cost before private execution. It includes the exact quoted ERC-20 approvals and escrow deployment; node execution is already funded by the service fee.

The SDK does not calculate or publish a platform/node split. Pricing formulas, gas profiles, floors, ceilings, and execution limits are owned and signed by the API.

Token utilities

import {
  getTokenMetadata,
  getTokenBalance,
  getTokenAllowance,
  isNativeToken,
  NATIVE_TOKEN_ADDRESS,
} from "@mirageprivacy/sdk";

const meta = await getTokenMetadata(tokenAddress, publicClient);
// { address, name, symbol, decimals }

const balance = await getTokenBalance(tokenAddress, owner, publicClient);
const allowance = await getTokenAllowance(tokenAddress, owner, spender, publicClient);

// Native ETH is represented by the zero address
isNativeToken(NATIVE_TOKEN_ADDRESS); // true

Service utilities

import { fetchNetworkKey, fetchApiHealth, fetchTransferLimit } from "@mirageprivacy/sdk";

// SGX attestation status, fetched through the API's nomad proxy at
// {apiServer}/nomad/{chainId}. Nodes are not addressed directly.
const key = await fetchNetworkKey("https://api.mirageprivacy.com", 1);
// { publicKey, attested, debug, chainId, mrenclave?, mrsigner?, verification? }

// Tune the verification policy for a known hardened enclave release.
const hardenedKey = await fetchNetworkKey("https://api.mirageprivacy.com", 1, {
  verify: {
    allowedTcbStatus: [
      "UpToDate",
      "SWHardeningNeeded",
      "ConfigurationAndSWHardeningNeeded",
    ],
    allowedAdvisoryIds: ["INTEL-SA-00289", "INTEL-SA-00615"],
    minimumIsvSvn: 2,
  },
});

// Service health and transfer limits
const health = await fetchApiHealth("https://api.mirageprivacy.com");
// { status, version?, maxTransferUsd? }

// Per-network transfer limit (USD)
const limit = await fetchTransferLimit("https://api.mirageprivacy.com", 1);
// "10000" | null | undefined

Error handling

All SDK errors extend MirageError, which has a machine-readable code property:

import {
  MirageError,
  ApiError,
  ContractError,
  TransferAbortedError,
  TransferTimeoutError,
  WhitelistRequiredError,
} from "@mirageprivacy/sdk";

try {
  for await (const event of prepared.execute()) { /* ... */ }
} catch (e) {
  if (e instanceof TransferAbortedError) {
    // e.escrowAddress - set if deploy completed before abort
  } else if (e instanceof TransferTimeoutError) {
    // Transfer event not observed within pollTimeout (default 2 min)
  } else if (e instanceof WhitelistRequiredError) {
    // e.amountUsd and e.thresholdUsd are present when supplied by the API
  } else if (e instanceof MirageError && e.code === "ACCOUNT_CHANGED") {
    // Wallet account switched mid-transfer
    // e.meta.expectedAccount, e.meta.actualAccount, e.meta.escrowAddress
  } else if (e instanceof ApiError) {
    // e.statusCode, e.body
  } else if (e instanceof ContractError) {
    // e.txHash
  }
}

What this SDK does not do

  • Wallet connection (use AppKit, MetaMask, etc.)
  • ENS resolution
  • Gas/price subscriptions (fetches on-demand; caller manages polling)
  • Framework-specific state (React hooks, Svelte stores, etc.)
  • Display formatting (use viem's formatUnits)

License

UNLICENSED