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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@actalink/erc20-paymaster

v0.0.2-alpha.14

Published

ERC20 Paymaster service for Acta Account Abstraction

Readme

Actalink ERC20 Paymaster

A service for sponsoring ERC20 token payments, and scheduling token transfers using Actalink Account Abstraction infrastructure.

Overview

The @actalink/erc20-paymaster enables smart contract wallets to pay gas fees using ERC20 tokens instead of native ETH. This package provides a paymaster service that can be integrated with Account Abstraction infrastructure to allow for a more flexible user experience.

Architecture

The ERC20 Paymaster implements a modular service architecture for handling token-based gas fee payments:

PaymasterService Architecture

Key Components

  • PaymasterService: The core class that orchestrates all components and manages the paymaster lifecycle.

    • Creates and configures the Express application server
    • Sets up authentication (SIWE based E2E Auth) and API routes
    • Spawns and manages background worker threads
    • Provides access to stored user operations to extension developers

    PaymasterService Architecture

    The PaymasterService follows microservice architecture to better manage the complexity of the system. The service is divided into several components, each responsible for a specific task:

  • Express Application: Handles HTTP requests and provides API endpoints for:

    • Sponsor User Operations
    • User operation scheduling
    • User operation cancellation
    • User operation listing
    • User operation status checking
    • Nonce key retrieval
  • API Routes: Endpoints for client interaction, mounted at /api/*.

  • Worker Thread: Background process for:

    • Monitoring pending user operations
    • Processing and submitting transactions to the blockchain
    • Managing transaction status updates
  • SQLite Database: Persistent storage designed to run within microservices for:

    • Storing signed user operations and their current status
    • Transaction history and status updates

Service Lifecycle

  1. Initialize: Constructs the service with configuration options
  2. Setup Middleware & Routes: Configures Express with middleware and API routes
  3. Start HTTP Server: Begins listening for client requests
  4. Create Worker: Spawns background worker for transaction processing (if enabled)

Authentication Flow

The ERC20 Paymaster service uses Sign-In with Ethereum (SIWE) for authentication. This provides a secure, web3-native way to authenticate users using their Ethereum wallets.

SIWE Authentication Flow

Authentication Process

  1. Request Challenge

    • Client sends a POST request to /api/challenge-siwe with their wallet address
    • PaymasterService generates a unique SIWE message as challenge
    • Message includes service details, nonce, and timestamp
  2. Message Signing

    • Client receives SIWE message
    • Wallet signs the message using private key
    • Returns signature to client
  3. Authentication

    • Client makes authenticated requests by including:
      • Base64 encoded SIWE message in x-siwe-message header
      • Signature in x-siwe-signature header
    • PaymasterService verifies signature matches wallet address
    • If valid, grants access to protected resources

Usage Example

// 1. Get SIWE challenge
const challengeResponse = await fetch('/api/challenge-siwe', {
  method: 'POST',
  body: JSON.stringify({ walletAddress: account.address })
});
const { message } = await challengeResponse.json();

// 2. Sign message with wallet
const signature = await wallet.signMessage(message);

// 3. Make authenticated request
const response = await fetch('/api/protected-endpoint', {
  headers: {
    'x-siwe-message': Buffer.from(message).toString('base64'),
    'x-siwe-signature': signature
  }
});

Installation

npm install @actalink/erc20-paymaster

Quick Start

import { ERC20PaymasterService } from '@actalink/erc20-paymaster';

// Initialize the paymaster
const service = new PaymasterService({
  // Configuration options
  port: 4000,                    // Port to run the service on
  enableLuciaAuth: false,        // Enable Lucia authentication
  enableSIWEe2eAuth: true,       // Enable Sign-In with Ethereum authentication
  enableWorker: true,            // Enable background worker for transaction processing
  bodyLimit: "1024kb",           // Request body size limit
  logFormat: "dev",              // Morgan logging format
  onUserOpSuccess: (userOp: UserOperationAndProps) => {
    const { userOpHash, transactionHash } = userOp;
    console.log(`UserOperation ${userOpHash} succeeded with transaction ${transactionHash}`);
    // Add custom logic here (e.g., update database, notify user)
  }
});

// Start the service
await service.start();

API Documentation

The service exposes the following REST API endpoints:

ERC20 Paymaster Endpoints

POST /api/scheduleops

Schedules user operations for future execution. This endpoint allows you to schedule one or more user operations to be executed at a specific time.

Request Body:

[
  {
    "userOp": {
      // UserOperation object (RpcUserOperation<'0.7'>)
      "sender": "0x...",
      "nonce": "0x...",
      // ... other UserOperation fields
    },
    "userOpHash": "0x...",
    "entryPoint": "0x...",
    "executionTime": 1234567890000, // Unix timestamp in milliseconds
    "signerAddress": "0x..."
  }
  // ... more operations
]

Validation:

  • Execution time must be at least 2 minutes in the future
  • Valid merkle signature in the UserOperation
  • Valid entry point address

Response:

  • Success (200): "N UserOperations scheduled successfully!" where N is the number of operations scheduled
  • Error (404): Error message with details

Notes:

  • Operations are stored with initial status "pending"
  • A background worker processes pending operations near their execution time
  • Operations can be cancelled using the /cancelops endpoint before execution
  • No authentication required for this endpoint

POST /api/challenge-siwe

Generates a Sign-In with Ethereum (SIWE) challenge message for authentication.

Request Body:

{
  "walletAddress": "0x..." // The wallet address to authenticate
}

Response:

{
  "message": "service.acta.link wants you to sign in with your Ethereum account:\n0x...\n\nI accept the ServiceOrg Terms of Service: https://service.org/tos\n\nURI: https://service.org/login\nVersion: 1\nChain ID: 1\nNonce: ...\nIssued At: ..."
}

Status Codes:

  • 200: Challenge message generated successfully
  • 400: Invalid wallet address
  • 500: Server error

Usage:

  1. Call this endpoint to get a SIWE challenge message
  2. Sign the message using the user's wallet
  3. Use the signed message and signature in subsequent authenticated requests

POST /api/sign/v2

Sponsors a user operation with ERC20 token payment.

Request Body:

{
  "method": "pm_sponsorUserOperation",
  "params": [
    userOperation,      // UserOperation object
    entryPointAddress, // Entry point contract address
    {
      "type": "sponsor" // Sponsorship type
    }
  ]
}

Response:

{
  "result": {
    ...userOperation,
    "paymasterData": "0x...",  // Signed paymaster data
    "paymasterVerificationGasLimit": "0x...",
    "paymasterPostOpGasLimit": "0x..."
  }
}

POST /api/cancelops

Cancels pending user operations for an authenticated user. Requires SIWE (Sign-In with Ethereum) authentication.

Headers:

x-siwe-message: <base64 encoded SIWE message>
x-siwe-signature: <signature>

Response:

[
  "0x...",  // Array of cancelled userOpHashes
  "0x..."
]

Authentication: This endpoint requires SIWE authentication. To authenticate:

  1. Get a SIWE challenge message using /api/challenge-siwe
  2. Sign the message with your wallet
  3. Include the base64 encoded message and signature in the request headers

Status Codes:

  • 200: Successfully cancelled operations
  • 403: Invalid authentication or signature
  • 500: Server error

POST /api/listops

Lists all scheduled user operations for a given smart wallet address.

Request Body:

{
  "validators": ["0x..."], // Array of validator addresses
  "salt": "0x..."         // Salt for smart wallet address calculation
}

Response:

[
  {
    "userOpHash": "0x...",
    "signerAddress": "0x...",
    "userOp": {
      // UserOperation object
    },
    "entryPoint": "0x...",
    "executionTime": 1234567890000,
    "status": "pending",
    "transactionHash": ""
  }
  // ... more operations
]

Authentication: Requires user authentication. The endpoint verifies that the authenticated user owns the smart wallet.

Status Codes:

  • 200: Successfully retrieved operations
  • 403: Invalid user or address
  • 500: Server error

GET /api/listPendingOps

Lists all pending user operations for a given smart wallet address.

Request Body:

{
  "validators": ["0x..."], // Array of validator addresses
  "salt": "0x..."         // Salt for smart wallet address calculation
}

Response:

[
  {
    "userOpHash": "0x...",
    "signerAddress": "0x...",
    "userOp": {
      // UserOperation object
    },
    "entryPoint": "0x...",
    "executionTime": 1234567890000,
    "status": "pending",
    "transactionHash": ""
  }
  // ... more operations
]

Notes:

  • Only returns operations with "pending" status
  • Useful for monitoring operations that haven't been executed yet
  • Can be used in conjunction with /cancelops to manage pending operations

POST /api/getPendingNonceKeys

Retrieves a list of unique nonce keys from pending user operations for a given smart wallet address. This is useful for checking which nonce slots are currently in use by pending operations.

Request Body:

{
  "validators": ["0x..."], // Array of validator addresses
  "salt": "0x..."         // Salt for smart wallet address calculation
}

Response:

[
  "0x1234...", // Array of unique nonce keys (first 42 characters of the nonce)
  "0x5678..."
]

Notes:

  • Only considers operations with "pending" status
  • Nonce keys are extracted from the first 42 characters of each operation's nonce
  • Duplicates are automatically filtered out
  • Useful for nonce management and preventing nonce conflicts

Service Management

The PaymasterService class provides the following methods:

  • start(): Starts the HTTP server and worker thread (if enabled)
  • stop(): Stops the service and terminates the worker thread
  • getApp(): Returns the Express application instance for custom configuration

For detailed API documentation including TypeScript types and interfaces, run:

npm run docs

Then open docs/index.html in your browser.

Configuration

The PaymasterService accepts the following configuration options:

| Option | Type | Description | Default | |--------|------|-------------|---------| | port | number | Port number for the HTTP server | 4000 | | enableSIWEe2eAuth | boolean | Enable Sign-In with Ethereum authentication | true | | enableWorker | boolean | Enable background worker for transaction processing | true | | workerPath | string | Path to worker thread script | "build/workers/scheduler.js" | | bodyLimit | string | Request body size limit | "1024kb" | | logFormat | string | Morgan logging format | "dev" | | onUserOpSuccess | (userOp: UserOperationAndProps) => void | Optional callback function executed by the background worker when a scheduled UserOperation is successfully mined on-chain. The function receives a single argument of type UserOperationAndProps, which includes the original userOp object, userOpHash, the final transactionHash, the updated status, and other details. | undefined | | app | express.Application | Provide an existing Express application instance | express() |

Environment Variables

The service requires the following environment variables:

PAYMASTER_PORT=4000           # Service port (optional, defaults to 4000)
PVT_KEY=                      # Private key for the paymaster account
PROVIDER=                     # RPC provider URL
CHAIN_ID=                    # Chain ID for the network (e.g. 137 for Polygon)

Supported Networks

The paymaster service currently supports the following networks:

  • Ethereum Mainnet (Chain ID: 1)
  • Polygon (Chain ID: 137)
  • Linea (Chain ID: 59144)
  • Hedera (Chain ID: 295)

Examples

See the examples directory for integration examples.

Advanced Usage

Extending the API

The underlying Express router used by the service is also exported as paymasterRouter. You can use this to add custom routes or middleware to the service instance:

import { PaymasterService, paymasterRouter } from '@actalink/erc20-paymaster';
import express from 'express';

const service = new PaymasterService();
const app = service.getApp(); // Get the Express app instance

// Add custom middleware
app.use('/api', (req, res, next) => {
  console.log('Custom middleware for API routes');
  next();
});

// Add a custom route
app.get('/custom-status', (req, res) => {
  res.json({ custom: 'status OK' });
});

// You can also add routes directly to the exported router
paymasterRouter.get('/custom-paymaster-route', (req, res) => {
  res.json({ message: 'Custom route within paymaster router' });
});

await service.start();

Exported Utility Functions

The package also exports several utility functions that might be useful for advanced integrations or direct database interaction. Use these with caution as they might expose internal implementation details.

  • getSignerFromMerkleSignature(root: Hex, proof: Hex[], leaf: Hex): Address

    • Recovers the signer address from a Merkle signature, root, proof, and leaf.
    • Useful for verifying Merkle-based signatures off-chain.
  • getScheduledUserOpsByHash(hashes: string[]): UserOperationAndProps[]

    • Retrieves scheduled user operations from the database based on their userOpHashes.
  • getScheduledUserOps(address: Address): UserOperationAndProps[]

    • Retrieves all scheduled user operations associated with a specific smart wallet address.
  • removeUserOpByHash(hash: string): void

    • Removes a specific user operation from the scheduling database using its userOpHash.
    • Note: This directly modifies the database state.
  • getInsertQuery(): Database.Statement

    • Returns the prepared SQLite statement used for inserting new scheduled user operations.
    • Provides direct access to the database insertion mechanism.

License

MIT