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

ows-react-native

v0.1.0

Published

The first React Native library for the Open Wallet Standard (OWS) — policy-gated autonomous agent payments on Solana using USDC, with Stripe MPP / x402 HTTP 402 support.

Readme

ows-react-native

The first React Native library for the Open Wallet Standard (OWS) — policy-gated autonomous payments on Solana using USDC, with Stripe MPP / x402 HTTP 402 support.

npm version License: MIT Built for Solana TypeScript


Why this exists

The Open Wallet Standard (OWS) defines how wallets and apps should communicate — but there is no official React Native implementation. This library fills that gap.

It's designed specifically for AI agents that need to make autonomous micro-payments (e.g. paying for API access, model inference, data feeds) without human intervention, while keeping spending under strict policy controls.

Key problems this solves:

| Problem | This library's solution | |---------|------------------------| | RN apps can't safely hold private keys | Backend vault — keys never leave the server | | Agents need to pay HTTP 402 endpoints | parseMppChallenge() + payMppChallenge() | | No spending guardrails for autonomous agents | OWS policy engine (per-tx limit, daily cap, allowlist) | | No standard RN wallet interface | Hooks-first API matching the OWS spec | | Large payments need human approval | Biometric gate via expo-local-authentication |


Features

  • OwsProvider — React context with backend URL, active wallet, policies, and tx history
  • useOwsWallet() — Create wallets, load from vault, get addresses and balances
  • usePayWithOws() — Parse + pay HTTP 402 / x402 / MPP challenges on Solana
  • usePolicy() — Set per-tx limits, daily caps, recipient allowlists, pause/resume
  • <TransactionHistory /> — Drop-in component for payment history
  • parseMppChallenge() — Parses x402 JSON, Stripe MPP headers, and X-Payment-Required headers
  • Biometric approval — Face ID / Touch ID gate for payments above your threshold
  • Full TypeScript — Complete type coverage, zero any in public API
  • New Architecture ready — Compatible with React Native's Bridgeless mode

Architecture

┌─────────────────────────────────────────┐
│         React Native App (Oscar)        │
│                                         │
│  useOwsWallet()   usePayWithOws()       │
│  usePolicy()      <TransactionHistory/> │
│                                         │
│  OwsProvider  ──────────────────────►  │
└───────────────┬─────────────────────────┘
                │  HTTP (localhost in dev, HTTPS in prod)
                ▼
┌─────────────────────────────────────────┐
│        OWS Backend (Express)            │
│                                         │
│  POST /create-wallet  ← generates keypair
│  POST /pay-mpp        ← enforces policy │
│  POST /pay-solana-usdc                  │
│  GET  /get-balance/:id                  │
│  POST /update-policy/:id                │
│                                         │
│  ┌──────────────────────────────────┐   │
│  │  .ows/ vault (keys never leave) │   │
│  └──────────────────────────────────┘   │
└───────────────┬─────────────────────────┘
                │  @solana/web3.js + @solana/spl-token
                ▼
        Solana devnet / mainnet
        USDC SPL Token transfers

Security model: Private keys are generated and stored exclusively in the backend vault (.ows/). The React Native app only ever receives public addresses and transaction signatures. The backend re-validates all OWS policies before signing any transaction — the client-side checks are a UX convenience only.


Installation

# Library
npm install ows-react-native

# Peer dependencies (if using Expo)
npx expo install expo-local-authentication expo-secure-store

Backend setup

The backend is a standalone Express server included in the package. Run it alongside your app:

# From your project root
npx ows-backend
# or clone the repo and run:
cd node_modules/ows-react-native/backend && npm install && npm run dev

The backend starts on http://localhost:3001 by default. Set PORT env var to change.


Quick Start

1. Wrap your app

import { OwsProvider } from 'ows-react-native';

export default function App() {
  return (
    // In production: use HTTPS and your deployed backend URL
    <OwsProvider backendUrl="http://localhost:3001">
      <YourApp />
    </OwsProvider>
  );
}

2. Create a wallet

import { useOwsWallet } from 'ows-react-native';

function SetupScreen() {
  const { wallet, balance, createWallet } = useOwsWallet('solana');

  return (
    <>
      <Button title="Create Wallet" onPress={() => createWallet('My Agent')} />
      {wallet && <Text>Address: {wallet.address}</Text>}
      {balance && <Text>USDC: ${balance.usdc.toFixed(2)}</Text>}
    </>
  );
}

3. Pay an x402 / MPP challenge (agent payment)

import { usePayWithOws, parseMppChallenge } from 'ows-react-native';

function AgentScreen() {
  const { payMppChallenge, isLoading } = usePayWithOws();

  const fetchProtectedResource = async () => {
    const res = await fetch('https://api.example.com/research-results');

    if (res.status === 402) {
      // Parse the 402 challenge (x402 JSON or MPP header)
      const body = await res.json();
      const headers = Object.fromEntries(res.headers.entries());
      const challenge = parseMppChallenge(body, headers);

      // Pay it — backend checks policy, signs, sends USDC on Solana
      const result = await payMppChallenge(challenge);
      console.log('Paid!', result.explorerUrl);

      // Retry the request with payment proof
      return fetch('https://api.example.com/research-results', {
        headers: { 'X-Payment-Signature': result.signature },
      });
    }

    return res;
  };
}

4. Set spend policies

import { usePolicy } from 'ows-react-native';

function PolicyScreen() {
  const { policy, setMaxPerTx, setDailyLimit, setAllowlist, pauseWallet } = usePolicy();

  // Limit the agent to $1.00 per payment, $10.00 per day
  await setMaxPerTx(1.00);
  await setDailyLimit(10.00);

  // Restrict to specific recipients (leave empty to allow all)
  await setAllowlist(['RecipientAddress1...', 'RecipientAddress2...']);

  // Emergency stop — blocks all payments immediately
  await pauseWallet();
}

5. Show transaction history

import { TransactionHistory } from 'ows-react-native';

function HistoryScreen() {
  return (
    <TransactionHistory
      limit={20}
      showExplorerLink
      onTransactionPress={(tx) => console.log(tx)}
    />
  );
}

x402 / MPP Challenge Format

This library parses all three common formats of HTTP 402 Payment Required challenges:

Format 1 — x402 JSON body (recommended)

{
  "x402Version": 1,
  "accepts": [{
    "scheme": "exact",
    "network": "solana-devnet",
    "maxAmountRequired": "0.10",
    "payTo": "RecipientPubkey...",
    "tokenMint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
    "memo": "api-access-ref-123"
  }]
}

Format 2 — Stripe MPP JSON body

{
  "scheme": "exact",
  "network": "solana-devnet",
  "amount": "0.10",
  "recipient": "RecipientPubkey...",
  "memo": "stripe-mpp-ref-456"
}

Format 3 — WWW-Authenticate MPP header

WWW-Authenticate: MPP realm="API", amount="0.10", recipient="PubKey...", network="solana-devnet"

API Reference

OwsProvider

<OwsProvider
  backendUrl="http://localhost:3001"  // Required: backend URL
  maxHistorySize={50}                  // Optional: tx history limit (default: 50)
>

useOwsWallet(chain)

const {
  wallet,          // OWSWallet | null — currently active wallet
  balance,         // WalletBalance | null — { sol, usdc, lamports, usdcRaw }
  isLoading,       // boolean
  error,           // string | null
  createWallet,    // (label?: string) => Promise<OWSWallet>
  loadWallet,      // (walletId: string) => Promise<OWSWallet>
  listWallets,     // () => Promise<OWSWallet[]>
  getAddress,      // () => string | null
  getBalance,      // () => Promise<WalletBalance>
  refreshBalance,  // () => Promise<void>
} = useOwsWallet('solana');

usePayWithOws()

const {
  isLoading,        // boolean
  error,            // string | null
  lastPayment,      // PaymentResult | null
  payMppChallenge,  // (challenge: MppChallenge) => Promise<PaymentResult>
  payDirect,        // (params: DirectPayParams) => Promise<PaymentResult>
} = usePayWithOws();

PaymentResult:

{
  signature: string;      // Solana transaction signature
  explorerUrl: string;    // Full Solana Explorer URL
  amountUsdc: number;     // Amount paid
  recipient: string;      // Recipient address
  timestamp: string;      // ISO-8601
  memo?: string;          // Memo attached to transaction
}

usePolicy()

const {
  policy,         // OWSPolicy | null
  isLoading,      // boolean
  error,          // string | null
  setMaxPerTx,    // (amount: number) => Promise<void>
  setDailyLimit,  // (amount: number) => Promise<void>
  setAllowlist,   // (addresses: string[]) => Promise<void>
  pauseWallet,    // () => Promise<void>
  resumeWallet,   // () => Promise<void>
  refreshPolicy,  // () => Promise<void>
} = usePolicy();

OWSPolicy:

{
  maxPerTx: number;                    // Max USDC per transaction
  dailyLimit: number;                  // Rolling 24h cap in USDC
  allowlist: string[];                 // Empty = allow all recipients
  paused: boolean;                     // Kill-switch
  merchantLimits?: Record<string, number>; // Per-address limits
}

parseMppChallenge(body, headers)

import { parseMppChallenge } from 'ows-react-native';

const challenge: MppChallenge = parseMppChallenge(
  responseBody,   // Record<string, unknown> | null
  responseHeaders // Record<string, string>
);
// Throws if format is unrecognised

Backend API

The included Express backend exposes these endpoints:

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /health | Server status + network info | | POST | /create-wallet | Generate new Solana keypair, store in vault | | GET | /list-wallets | List all vault wallets (no secret keys) | | GET | /get-balance/:id | SOL + USDC balance on-chain | | GET | /get-policy/:id | Current OWS policy for wallet | | POST | /update-policy/:id | Update policy fields | | POST | /pay-solana-usdc | Direct USDC transfer (policy-gated) | | POST | /pay-mpp | Pay an x402/MPP challenge (policy-gated) | | POST | /mock-402 | Dev only — returns a test 402 challenge |

Backend environment variables

# backend/.env
PORT=3001
SOLANA_NETWORK=devnet        # or mainnet-beta
VAULT_DIR=./.ows             # path to encrypted key vault

Solana Constants

| Network | USDC Mint Address | |---------|------------------| | Devnet | 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU | | Mainnet | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |


Funding Devnet Wallets (Testing)

  1. SOL (for transaction fees): faucet.solana.com
  2. USDC (devnet): spl-token-faucet.com
  3. Verify: explorer.solana.com

End-to-End Test (curl)

# 1. Start the backend
cd backend && npm run dev

# 2. Create a wallet
curl -X POST http://localhost:3001/create-wallet \
  -H "Content-Type: application/json" \
  -d '{"label":"test-agent"}'
# → { "wallet": { "id": "...", "address": "...", "chain": "solana" } }

# 3. Set policies
curl -X POST http://localhost:3001/update-policy/<WALLET_ID> \
  -H "Content-Type: application/json" \
  -d '{"maxPerTx": 0.50, "dailyLimit": 5.00}'

# 4. Get a mock 402 challenge
curl -X POST http://localhost:3001/mock-402 \
  -H "Content-Type: application/json" \
  -d '{"amount":"0.10"}'
# → HTTP 402 with x402 JSON body

# 5. Pay the challenge
curl -X POST http://localhost:3001/pay-mpp \
  -H "Content-Type: application/json" \
  -d '{
    "walletId": "<WALLET_ID>",
    "challenge": { "recipient": "<ADDRESS>", "amountFloat": 0.10 }
  }'
# → { "result": { "signature": "...", "explorerUrl": "..." } }

# 6. View on Explorer
open "https://explorer.solana.com/tx/<SIGNATURE>?cluster=devnet"

Policy enforcement tests (red-team)

# Exceed maxPerTx — should return 403
curl -X POST http://localhost:3001/pay-solana-usdc \
  -d '{"walletId":"<ID>","recipient":"<ADDR>","amountUsdc":100}'
# → 403 { "message": "Amount $100.00 exceeds maxPerTx limit of $0.50" }

# Blocked recipient (allowlist active)
curl -X POST http://localhost:3001/update-policy/<ID> \
  -d '{"allowlist":["<ALLOWED_ADDR>"]}'
curl -X POST http://localhost:3001/pay-solana-usdc \
  -d '{"walletId":"<ID>","recipient":"<OTHER_ADDR>","amountUsdc":0.01}'
# → 403 { "message": "Recipient ... is not on the allowlist" }

# Paused wallet
curl -X POST http://localhost:3001/update-policy/<ID> -d '{"paused":true}'
curl -X POST http://localhost:3001/pay-solana-usdc \
  -d '{"walletId":"<ID>","recipient":"<ADDR>","amountUsdc":0.01}'
# → 403 { "message": "Wallet is paused — no payments allowed" }

Security

⚠️ Read before deploying to production

What is safe

  • ✅ Public addresses are safe to share (they're on the blockchain)
  • ✅ Transaction signatures are safe to expose
  • ✅ Policy configs are safe to send over HTTP

What must be protected

  • 🔐 .ows/ directory — contains private keys. Never commit it. Add to .gitignore.
  • 🔐 Backend API — in production, add Authorization: Bearer <token> header authentication
  • 🔐 HTTPS — run the backend behind TLS in production (NGINX, Caddy, etc.)
  • 🔐 Vault encryption — for production, replace plain JSON files with AWS KMS, HashiCorp Vault, or a TEE/HSM

Policy enforcement

All policies are enforced server-side before any signing. The client-side pre-checks in hooks are UX convenience only — a malicious client cannot bypass them by directly calling the backend without a valid walletId and passing the same policy checks.

Private key isolation

The /create-wallet endpoint returns only { id, address, chain, label, createdAt }. The secretKey field is stripped before the response is sent. No endpoint ever returns a private key.


Roadmap

  • [ ] Native module path — TurboModules + iOS Secure Enclave for on-device signing
  • [ ] Streaming payments — x402 streaming scheme support
  • [ ] Multi-sig policy approval for spends above threshold
  • [ ] Persistent transaction history via expo-sqlite
  • [ ] Push notifications on payment confirmation
  • [ ] EVM support (chain: 'base' | 'ethereum')
  • [ ] Mainnet launch checklist and audit

Project Structure

ows-react-native/
├── src/                          # Library source (TypeScript)
│   ├── index.ts                  # Public API exports
│   ├── types.ts                  # All TypeScript types
│   ├── components/
│   │   ├── OwsProvider.tsx       # React context + provider
│   │   └── TransactionHistory.tsx
│   ├── hooks/
│   │   ├── useOwsWallet.ts       # Wallet lifecycle
│   │   ├── usePayWithOws.ts      # Payment hook
│   │   └── usePolicy.ts          # Policy management
│   └── utils/
│       ├── mppParser.ts          # x402 / MPP challenge parser
│       └── apiClient.ts          # Typed fetch wrapper
├── backend/
│   ├── server.ts                 # Express server (wallet vault + Solana)
│   └── package.json
├── example/                      # Oscar — full Expo demo app
│   ├── App.tsx
│   ├── theme.ts                  # Design tokens
│   └── screens/
│       ├── OnboardingScreen.tsx
│       ├── DashboardScreen.tsx
│       ├── PolicySetupScreen.tsx
│       ├── AgentDemoScreen.tsx
│       └── HistoryScreen.tsx
└── lib/                          # Built output (commonjs + module + types)

Contributing

Pull requests welcome. For major changes, open an issue first.

git clone https://github.com/rkmonarch/ows-react-native
cd ows-react-native
npm install
cd backend && npm install
npm run typecheck   # type-check library

Related


License

MIT © rkmonarch