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

facinet

v2.4.6

Published

JavaScript SDK and CLI tool for x402 Facilitator Network - Make gasless USDC payments on multiple chains

Readme

Facinet SDK

JavaScript/TypeScript SDK and CLI tool for the x402 Facilitator Network
Make gasless USDC payments and execute arbitrary smart contract calls across multiple blockchain networks.

npm version License: MIT

Table of Contents


Overview

Facinet is a decentralized facilitator network that enables gasless transactions on multiple blockchain networks. Users sign transaction authorizations off-chain, and facilitators execute them on-chain while paying gas fees.

What Facinet Can Do

USDC Payments - Gasless ERC-3009 USDC transfers
Arbitrary Contract Calls - Execute any smart contract function (registry.register, transferFrom, etc.)
Multichain Support - 7 supported testnet networks
Network-Aware Selection - Automatically selects facilitators for your network
EIP-712 Meta-Transactions - Support for signed meta-transactions


Features

  • 🚀 Gasless Transactions - Users sign, facilitators pay gas
  • 🌐 Multichain - Support for 7 testnet networks
  • 🔒 Secure - EIP-712 typed data signing
  • 📦 TypeScript - Full type definitions included
  • 🎯 Network-Aware - Automatic facilitator filtering by network
  • Simple API - Easy-to-use SDK and CLI

Installation

As a Library (SDK)

npm install facinet

As a CLI Tool

npm install -g facinet

Requirements

  • Node.js: >= 18.0.0
  • npm or yarn

Quick Start

Browser (MetaMask)

import { Facinet } from 'facinet';

// Initialize for Ethereum Sepolia (network is REQUIRED)
const facinet = new Facinet({
  network: 'ethereum-sepolia', // Must specify network!
});

// Make a gasless USDC payment
const result = await facinet.pay({
  amount: '1',                              // 1 USDC
  recipient: '0xYourMerchantAddress',       // Your wallet address
});

console.log('Payment successful!', result.txHash);
console.log('Processed by:', result.facilitator.name);

Node.js (Private Key)

import { Facinet } from 'facinet';

const facinet = new Facinet({
  privateKey: process.env.PRIVATE_KEY,
  network: 'base-sepolia',
});

const result = await facinet.pay({
  amount: '5',
  recipient: '0xYourMerchantAddress',
});

console.log('Transaction:', result.txHash);

Execute Contract Call

import { Facinet } from 'facinet';

const facinet = new Facinet({
  network: 'ethereum-sepolia',
});

// Execute arbitrary contract call (e.g., 8004 registry)
const result = await facinet.executeContract({
  contractAddress: '0x...',              // Your contract address
  functionName: 'register',             // Function to call
  functionArgs: ['https://example.com'], // Function arguments
  abi: registryABI,                     // Contract ABI
});

console.log('Contract call successful!', result.txHash);

API Reference

Facinet Class

Constructor

new Facinet(config?: FacinetConfig)

Parameters:

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | apiUrl | string | No | 'https://facinet.vercel.app' | API endpoint URL | | privateKey | string | No | - | Private key for signing (Node.js only) | | network | NetworkName | Yes | - | Network to use (must be specified) | | rpcUrl | string | No | - | Custom RPC URL |

Network Options:

  • 'avalanche-fuji' (Chain ID: 43113)
  • 'ethereum-sepolia' (Chain ID: 11155111)
  • 'base-sepolia' (Chain ID: 84532)
  • 'polygon-amoy' (Chain ID: 80002)
  • 'arbitrum-sepolia' (Chain ID: 421614)
  • 'monad-testnet' (Chain ID: 10143)
  • 'optimism-sepolia' (Chain ID: 11155420)

Legacy Aliases (backwards compatible):

  • 'avalanche''avalanche-fuji'
  • 'ethereum''ethereum-sepolia'
  • 'base''base-sepolia'
  • 'polygon''polygon-amoy'
  • 'arbitrum''arbitrum-sepolia'
  • 'monad''monad-testnet'

Methods

pay(params: PaymentParams): Promise<PaymentResult>

Make a gasless USDC payment via ERC-3009.

Parameters:

interface PaymentParams {
  amount: string;                    // Amount in USDC (e.g., '1')
  recipient: `0x${string}`;         // Recipient address
  payerAddress?: `0x${string}`;     // Optional: Payer address (uses wallet if not provided)
}

Returns:

interface PaymentResult {
  success: boolean;
  txHash: string;
  facilitator: {
    id: string;
    name: string;
    wallet: string;
  };
  payment: {
    from: string;
    to: string;
    amount: string;
    network: string;
  };
}

Example:

const result = await facinet.pay({
  amount: '1',
  recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
});

console.log(result.txHash);              // Transaction hash
console.log(result.facilitator.name);    // Facilitator name
console.log(result.payment.network);     // Network used

executeContract(params: ExecuteContractParams): Promise<ExecuteContractResult>

Execute an arbitrary smart contract call. Facilitator pays gas fees.

Parameters:

interface ExecuteContractParams {
  contractAddress: `0x${string}`;    // Contract address to call
  functionName: string;              // Function name (e.g., 'register')
  functionArgs: any[];               // Function arguments array
  abi: any[];                        // Contract ABI (required)
  value?: string;                    // Optional: Native token value (for payable functions)
  signTypedData?: {                  // Optional: EIP-712 signature for meta-transactions
    domain: {
      name: string;
      version: string;
      chainId: number;
      verifyingContract: `0x${string}`;
    };
    types: Record<string, any[]>;
    message: any;
  };
}

Returns:

interface ExecuteContractResult {
  success: boolean;
  txHash: string;
  facilitator: {
    id: string;
    name: string;
    wallet: string;
  };
  contract: {
    address: string;
    functionName: string;
    network: string;
  };
  gasUsed?: string;
  gasSpent?: string;
}

Example:

// Register an agent on 8004 registry
const registryABI = [
  {
    inputs: [{ name: 'agentUrl', type: 'string' }],
    name: 'register',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];

const result = await facinet.executeContract({
  contractAddress: '0x8004RegistryAddress',  // Your 8004 registry address
  functionName: 'register',
  functionArgs: ['https://example.com/agent'],
  abi: registryABI,
});

console.log('Agent registered!', result.txHash);

getFacilitators(): Promise<Facilitator[]>

Get all active facilitators for the current network.

Returns:

interface Facilitator {
  id: string;
  name: string;
  facilitatorWallet: string;
  paymentRecipient: string;
  status: 'active' | 'inactive' | 'needs_funding';
  totalPayments: number;
  network?: string;
  chainId?: number;
}

Example:

const facilitators = await facinet.getFacilitators();

facilitators.forEach(fac => {
  console.log(`${fac.name} (${fac.id})`);
  console.log(`  Network: ${fac.network}`);
  console.log(`  Status: ${fac.status}`);
});

selectRandomFacilitator(): Promise<Facilitator>

Select a random active facilitator for the current network.

Returns: Facilitator

Example:

const facilitator = await facinet.selectRandomFacilitator();
console.log('Selected:', facilitator.name);

getChain(): ChainConfig

Get the current chain configuration.

Returns:

interface ChainConfig {
  name: string;
  displayName: string;
  chainId: number;
  rpcUrl: string;
  usdcAddress: `0x${string}`;
  usdcDecimals: number;
  gasToken: string;
  blockExplorer: string;
  erc3009DomainName: string;
  erc3009DomainVersion: string;
}

Static Methods

Facinet.getSupportedChains(): ChainConfig[]

Get all supported chain configurations.

Example:

const chains = Facinet.getSupportedChains();
chains.forEach(chain => {
  console.log(`${chain.displayName} (${chain.chainId})`);
});

Facinet.getSupportedNetworks(): string[]

Get all supported network names.

Example:

const networks = Facinet.getSupportedNetworks();
// ['avalanche-fuji', 'ethereum-sepolia', 'base-sepolia', ...]

Facinet.quickPay(params): Promise<PaymentResult>

Quick payment helper (static method).

Example:

await Facinet.quickPay({
  amount: '1',
  recipient: '0x...',
  privateKey: process.env.PRIVATE_KEY,
  network: 'base-sepolia',
});

Examples

Network-Specific Contract Addresses

When using Facinet with contracts like the 8004 registry, you can configure network-specific addresses:

import { Facinet } from 'facinet';

// Define your contract addresses per network
const CONTRACT_ADDRESSES = {
  'ethereum-sepolia': '0x...',  // 8004 registry on Ethereum Sepolia
  'base-sepolia': '0x...',      // 8004 registry on Base Sepolia
  'polygon-amoy': '0x...',      // 8004 registry on Polygon Amoy
  // ... other networks
};

// Initialize SDK for a specific network
const facinet = new Facinet({
  network: 'ethereum-sepolia',
});

// Use network-specific contract address
const registryAddress = CONTRACT_ADDRESSES[facinet.getChain().name];

// Execute contract call
const result = await facinet.executeContract({
  contractAddress: registryAddress,
  functionName: 'register',
  functionArgs: ['https://example.com/agent'],
  abi: registryABI,
});

8004 Registry Example

import { Facinet } from 'facinet';

// 8004 Registry ABI (simplified)
const REGISTRY_ABI = [
  {
    inputs: [{ name: 'agentUrl', type: 'string' }],
    name: 'register',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { name: 'tokenId', type: 'uint256' },
      { name: 'uri', type: 'string' },
    ],
    name: 'setAgentURI',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'tokenId', type: 'uint256' },
    ],
    name: 'transferFrom',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];

// Network-specific registry addresses
const REGISTRY_ADDRESSES = {
  'ethereum-sepolia': '0xYourRegistryAddressOnSepolia',
  'base-sepolia': '0xYourRegistryAddressOnBase',
  'polygon-amoy': '0xYourRegistryAddressOnPolygon',
};

async function registerAgent(network: string, agentUrl: string) {
  const facinet = new Facinet({ network });
  const registryAddress = REGISTRY_ADDRESSES[network];

  const result = await facinet.executeContract({
    contractAddress: registryAddress,
    functionName: 'register',
    functionArgs: [agentUrl],
    abi: REGISTRY_ABI,
  });

  return result.txHash;
}

// Usage
await registerAgent('ethereum-sepolia', 'https://example.com/agent');

EIP-712 Meta-Transaction Example

import { Facinet } from 'facinet';

const facinet = new Facinet({
  network: 'ethereum-sepolia',
});

// Sign EIP-712 typed data for meta-transaction
const result = await facinet.executeContract({
  contractAddress: '0x...',
  functionName: 'setAgentWallet',
  functionArgs: [tokenId, walletAddress],
  abi: registryABI,
  signTypedData: {
    domain: {
      name: 'AgentRegistry',
      version: '1',
      chainId: 11155111,
      verifyingContract: '0x...',
    },
    types: {
      SetWallet: [
        { name: 'tokenId', type: 'uint256' },
        { name: 'wallet', type: 'address' },
      ],
    },
    message: {
      tokenId: tokenId,
      wallet: walletAddress,
    },
  },
});

React Hook Example

import { useState } from 'react';
import { Facinet } from 'facinet';

function PaymentButton() {
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState<any>(null);

  const handlePayment = async () => {
    setLoading(true);
    try {
      const facinet = new Facinet({ network: 'base-sepolia' });

      const paymentResult = await facinet.pay({
        amount: '1',
        recipient: '0xYourMerchantAddress',
      });

      setResult(paymentResult);
    } catch (error: any) {
      console.error('Payment failed:', error.message);
      alert(`Payment failed: ${error.message}`);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <button onClick={handlePayment} disabled={loading}>
        {loading ? 'Processing...' : 'Pay 1 USDC'}
      </button>
      {result && (
        <div>
          <p>Transaction: {result.txHash}</p>
          <p>Facilitator: {result.facilitator.name}</p>
          <p>Network: {result.payment.network}</p>
        </div>
      )}
    </div>
  );
}

CLI Usage

Installation

npm install -g facinet

Commands

Make a Payment

# Pay on specific network (network is required)
facinet pay --amount 1 --to 0xRecipientAddress --network avalanche-fuji
facinet pay --amount 1 --to 0xRecipientAddress --network base-sepolia
facinet pay --amount 1 --to 0xRecipientAddress --network ethereum-sepolia
facinet pay --amount 1 --to 0xRecipientAddress --network polygon-amoy

List Facilitators

# List all active facilitators for configured network
facinet facilitator list

# List facilitators for specific network
facinet facilitator list --network base-sepolia

Check Facilitator Status

facinet facilitator status fac_xyz123
facinet facilitator balance fac_xyz123

Configuration

SDK Configuration

const facinet = new Facinet({
  apiUrl: 'https://facinet.vercel.app',  // Optional: Override API URL
  privateKey: '0x...',                    // Optional: For Node.js
  network: 'base-sepolia',                // REQUIRED: Network selection
  rpcUrl: 'https://...',                  // Optional: Custom RPC
});

CLI Configuration

Facinet CLI stores configuration in ~/.facinet/config.json:

{
  "privateKey": "0x...",
  "address": "0x...",
  "network": "avalanche-fuji",
  "apiUrl": "https://facinet.vercel.app"
}

TypeScript Support

Facinet is written in TypeScript and includes full type definitions:

import type {
  FacinetConfig,
  PaymentParams,
  PaymentResult,
  ExecuteContractParams,
  ExecuteContractResult,
  Facilitator,
  ChainConfig,
} from 'facinet';

const config: FacinetConfig = {
  network: 'base-sepolia',
};

const params: PaymentParams = {
  amount: '1',
  recipient: '0x...',
};

const result: PaymentResult = await facinet.pay(params);

Error Handling

try {
  const facinet = new Facinet({ network: 'base-sepolia' });
  const result = await facinet.pay({
    amount: '1',
    recipient: '0x...',
  });
} catch (error: any) {
  if (error.message.includes('No active facilitators available')) {
    console.error('No active facilitators found for this network');
  } else if (error.message.includes('invalid signature')) {
    console.error('Signature validation failed');
  } else {
    console.error('Payment failed:', error.message);
  }
}

Common Errors:

  • No active facilitators available - No facilitators registered for the selected network
  • Invalid contract address format - Contract address is not a valid Ethereum address
  • Function not found in provided ABI - Function name doesn't exist in the ABI
  • Contract execution failed - Transaction reverted on-chain

Supported Networks

| Network | Network ID | Chain ID | Gas Token | Explorer | |---------|-----------|----------|-----------|----------| | Avalanche Fuji | avalanche-fuji | 43113 | AVAX | testnet.snowtrace.io | | Ethereum Sepolia | ethereum-sepolia | 11155111 | ETH | sepolia.etherscan.io | | Base Sepolia | base-sepolia | 84532 | ETH | sepolia.basescan.org | | Polygon Amoy | polygon-amoy | 80002 | MATIC | amoy.polygonscan.com | | Arbitrum Sepolia | arbitrum-sepolia | 421614 | ETH | sepolia.arbiscan.io | | Monad Testnet | monad-testnet | 10143 | MON | testnet.monadvision.com | | Optimism Sepolia | optimism-sepolia | 11155420 | ETH | sepolia-optimism.etherscan.io |


How It Works

Payment Flow (USDC)

  1. Initialize SDK - Create Facinet instance with network selection
  2. Select Facilitator - SDK automatically selects an active facilitator for your network
  3. Sign Authorization - User signs ERC-3009 authorization (gasless!)
  4. Facilitator Executes - Selected facilitator submits transaction and pays gas
  5. Payment Complete - USDC transferred to recipient address

Contract Execution Flow

  1. Initialize SDK - Create Facinet instance with network selection
  2. Select Facilitator - SDK selects an active facilitator for your network
  3. Sign (Optional) - If using EIP-712 meta-transactions, sign typed data
  4. Facilitator Executes - Facilitator calls contract function and pays gas
  5. Transaction Confirmed - Contract call executed on-chain

Network-Aware Facilitator Selection

  • SDK filters facilitators by network and chainId
  • Only active facilitators are selected
  • Facilitators must match the selected network exactly
  • Random selection from filtered pool

Development

Build from Source

cd facinet-sdk

# Install dependencies
npm install

# Build
npm run build

# Link for local testing
npm link

# Test
facinet --help

Project Structure

facinet-sdk/
├── src/
│   ├── sdk/
│   │   ├── Facinet.ts      # Main SDK class
│   │   └── types.ts        # TypeScript types
│   ├── commands/           # CLI commands
│   ├── utils/              # Utility functions
│   └── index.ts            # CLI entry point
├── dist/                   # Compiled output
└── README.md

Changelog

v2.4.5

  • Arbitrary Contract Calls - New executeContract() method for executing any smart contract function
  • 🔧 Network-Aware Selection - Strict facilitator filtering by network/chainId
  • 📝 Enhanced Documentation - Complete API reference and examples
  • 🚀 Backend Endpoint - New /api/x402/execute-contract endpoint

v2.4.4

  • 🌐 Optimism Sepolia Support - Added Optimism Sepolia testnet
  • 🔍 Network Filtering - Backend /api/facilitator/list accepts ?network= query param
  • Strict Filtering - SDK strictly filters facilitators by network (no fallback)

v2.4.0

  • 🌐 Multichain Support - 7 supported testnet networks
  • 🎯 Network-Specific Selection - Automatic facilitator filtering by network
  • 📊 Enhanced Facilitator Info - Network and chainId in facilitator objects

License

MIT © x402 Team


Support


Made with ❤️ by the x402 Team