mppx-xpr-network

May 13, 2026 · View on GitHub

XPR Network payment method for the Machine Payments Protocol (MPP).

Zero gas fees. Sub-second finality. Human-readable accounts. Built for machine-to-machine payments.

Installation

npm install mppx-xpr-network mppx

Server Usage

import { Mppx } from 'mppx/server'
import { xpr } from 'mppx-xpr-network'

const mppx = Mppx.create({
  methods: [
    xpr.charge({ recipient: 'charliebot' }),
  ],
  secretKey: process.env.MPP_SECRET_KEY,
})

// In your route handler (Next.js, Express, etc.)
export async function GET(request: Request) {
  const result = await mppx.xpr.charge({
    amount: '1.0000 XPR',
  })(request)

  if (result.status === 402) return result.challenge

  return result.withReceipt(
    Response.json({ joke: 'Why did the AI cross the blockchain?' })
  )
}

The server automatically:

  • Returns 402 with WWW-Authenticate: Payment challenge header
  • Parses Authorization: Payment credentials from retry requests
  • Verifies the on-chain transfer via Hyperion
  • Attaches Payment-Receipt header to successful responses
  • Rejects replay attacks (duplicate transaction hashes)

Client Usage

import { Mppx } from 'mppx/client'
import { xprClient } from 'mppx-xpr-network'

const mppx = Mppx.create({
  methods: [
    xprClient({
      signTransaction: async (actions) => {
        // Use WebAuth SDK, @nicknguyen/proton-web-sdk, or any EOSIO wallet
        const result = await session.transact({ actions }, { broadcast: true })
        return { transactionId: result.processed.id }
      },
    }),
  ],
})

Payment Flow

Client                          Server                      XPR Network
  |                               |                              |
  |  GET /api/resource            |                              |
  |------------------------------>|                              |
  |                               |                              |
  |  402 + WWW-Authenticate:      |                              |
  |  Payment (challenge)          |                              |
  |<------------------------------|                              |
  |                               |                              |
  |  eosio.token::transfer        |                              |
  |  to=charliebot, memo=uuid     |                              |
  |------------------------------------------------------------->|
  |                               |                              |
  |  GET /api/resource            |                              |
  |  Authorization: Payment       |                              |
  |  (credential with txHash)     |                              |
  |------------------------------>|                              |
  |                               |  verify tx via Hyperion      |
  |                               |----------------------------->|
  |                               |                              |
  |  200 OK + Payment-Receipt     |                              |
  |  (content + receipt header)   |                              |
  |<------------------------------|                              |

Sessions (Streaming Payments)

Sessions use the XPR Network vest contract for time-locked streaming payments. The client deposits XPR into a vest stream; the server verifies on-chain and streams content. Either party can stop the session early — remaining funds are refunded.

import { Mppx } from 'mppx/server'
import { xpr } from 'mppx-xpr-network'

const mppx = Mppx.create({
  methods: [
    xpr.session({
      recipient: 'myservice',
      rpc: 'https://api.protonnz.com',
    }),
  ],
  secretKey: process.env.MPP_SECRET_KEY,
})

// Streaming endpoint
export async function GET(request: Request) {
  const result = await mppx.xpr.session({
    maxAmount: '10.0000 XPR',
    duration: 300, // 5 minutes
  })(request)

  if (result.status === 402) return result.challenge

  // Stream content to the client
  const stream = new ReadableStream({ ... })
  return result.withReceipt(new Response(stream))
}

Session Flow

Client                          Server                      XPR Network (vest)
  |                               |                              |
  |  GET /api/stream              |                              |
  |------------------------------>|                              |
  |                               |                              |
  |  402 + WWW-Authenticate:      |                              |
  |  Payment (session challenge)  |                              |
  |  {vestName, maxAmount, dur}   |                              |
  |<------------------------------|                              |
  |                               |                              |
  |  1. transfer XPR to vest      |                              |
  |     memo="deposit"            |                              |
  |  2. startvest(vestName, ...)  |                              |
  |------------------------------------------------------------->|
  |                               |                              |
  |  GET /api/stream              |                              |
  |  Authorization: Payment       |                              |
  |  {vestName}                   |                              |
  |------------------------------>|                              |
  |                               |  verify vest table           |
  |                               |----------------------------->|
  |                               |                              |
  |  200 OK (streaming content)   |                              |
  |  + Payment-Receipt            |                              |
  |<------------------------------|                              |
  |                               |                              |
  |           ... streaming ...   |  claimvest (periodic)        |
  |                               |----------------------------->|
  |                               |                              |
  |  stopvest (session end)       |                              |
  |------------------------------------------------------------->|
  |                               |  final claimvest             |
  |                               |----------------------------->|

Vest Contract Actions

ActionWhoWhat
eosio.token::transfer to vestClientDeposit XPR (memo: "deposit")
vest::startvestClientStart streaming session
vest::claimvestServerWithdraw accrued tokens
vest::stopvestEitherEnd session early, refund remainder

Sessions vs Charges

xpr.charge()xpr.session()
Use caseOne-time paymentStreaming / metered
On-chain actions1 (transfer)2+ (deposit + startvest + claims)
RefundableNoYes (stopvest refunds remainder)
VerificationHyperion tx lookupVest table query
Gas fees$0$0

XPR Sessions vs Tempo Sessions

XPR NetworkTempo (EVM)
Gas to open session$0~$0.001 (EVM tx)
Gas to close session$0~$0.001 (EVM tx)
Gas per claim$0~$0.001 per claim
Finality< 500ms~1s
AccountsHuman-readableHex addresses
Native streamingvest contractCustom contract

XPR Network's zero gas fees mean sessions cost exactly nothing to open, claim, and close — the only cost is the actual payment amount.

Configuration

Charge

xpr.charge({
  recipient: 'charliebot',        // XPR account to receive payments
  hyperion: 'https://proton.eosusa.io',  // Hyperion API for verification
  rpc: 'https://api.protonnz.com',       // XPR Network RPC
  amount: '1.0000 XPR',                  // Default amount
  memo: 'custom-memo',                   // Default memo
})

Session

xpr.session({
  recipient: 'myservice',                // XPR account to receive payments
  rpc: 'https://api.protonnz.com',       // RPC for vest table queries
})

Why XPR Network for Machine Payments?

FeatureXPR NetworkEthereumSolanaTempo
Gas fees$0 (zero)$0.50-$50+$0.001-$0.05~$0.001
Finality< 500ms~12 min~400ms~1s
Account namesHuman-readable (charliebot)Hex (0x7a58...)Base58 (Gh9Z...)Hex
Identity/KYCBuilt-inNoneNoneNone
Wallet authBiometric (WebAuth)Seed phraseSeed phraseSeed phrase
Account creationFreeFree (EOA)~$0.002Free
Smart contractsYes (C++)Yes (Solidity)Yes (Rust)Yes (Solidity)

XPR Network advantages for agents and machines:

  • Zero gas fees — the payment amount is exactly what the recipient gets
  • Sub-second finality — no waiting for block confirmations
  • Human-readable accounts — pay charliebot not 0x7a58c3F2...
  • Built-in identity — on-chain KYC verification for compliance
  • WebAuth wallet — biometric authentication, no seed phrases needed
  • Free account creation — onboard users at zero cost
  • On-chain agent registry — trust scores, escrow jobs, A2A protocol

MCP tool calls

MPP also works over MCP/JSON-RPC, not just HTTP. That makes mppx-xpr-network useful for agent-to-agent tools: an agent calls a paid tool, the MCP server returns a payment challenge, the agent pays on XPR Network, then retries with the credential and receives a receipt.

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio'
import { Mppx, Transport } from 'mppx/server'
import { xpr } from 'mppx-xpr-network'

const mppx = Mppx.create({
  methods: [xpr.charge({ recipient: 'charliebot' })],
  transport: Transport.mcpSdk(),
  secretKey: process.env.MPP_SECRET_KEY,
})

const server = new McpServer({ name: 'paid-xpr-tools', version: '1.0.0' })

server.registerTool(
  'market-snapshot',
  { description: 'Fetch a paid SimpleDEX market snapshot' },
  async (_args, extra) => {
    const result = await mppx.xpr.charge({
      amount: '1.0000 XPR',
      description: 'SimpleDEX market snapshot',
    })(extra)

    if (result.status === 402) throw result.challenge

    return result.withReceipt({
      content: [{ type: 'text', text: 'paid market data goes here' }],
    })
  },
)

await server.connect(new StdioServerTransport())

MCP encoding follows the MPP transport model:

MPP conceptMCP / JSON-RPC encoding
ChallengeJSON-RPC error -32042 with challenge data
Credentialtool call _meta["org.paymentauth/credential"]
Receipttool result _meta["org.paymentauth/receipt"]

Discovery

Expose paid XPR endpoints through standard MPP discovery by serving an OpenAPI 3.1 document at /openapi.json with x-payment-info.offers[] entries.

{
  "openapi": "3.1.0",
  "info": { "title": "Paid XPR API", "version": "1.0.0" },
  "x-service-info": {
    "categories": ["ai", "payments", "xpr-network"],
    "docs": { "llms": "https://example.com/llms.txt" }
  },
  "paths": {
    "/api/resource": {
      "get": {
        "x-payment-info": {
          "offers": [{
            "amount": "10000",
            "currency": "XPR",
            "description": "1 XPR for paid resource",
            "intent": "charge",
            "method": "xpr",
            "network": "xpr-network",
            "recipient": "charliebot"
          }]
        },
        "responses": {
          "200": { "description": "Paid response" },
          "402": { "description": "Payment Required" }
        }
      }
    }
  }
}

The live playground exposes this at x402.charliebot.dev/openapi.json, so agents and registries can discover prices before calling an endpoint. Runtime 402 challenges remain authoritative.

Spec Compliance

This package implements the Payment Authentication IETF draft:

  • WWW-Authenticate: Payment — 402 challenge header
  • Authorization: Payment — credential header with base64-encoded proof
  • Payment-Receipt — receipt header with base64-encoded settlement proof
  • HMAC-bound challenge IDs via mppx secretKey
  • Replay protection via transaction hash deduplication

License

MIT