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

@horizen/x402-private-vela-fixed

v0.1.1

Published

x402 private-vela-fixed scheme for facilitator, client, and resource server

Readme

@horizen/x402-private-vela-fixed

x402 payment scheme implementation for Vela private transfers. Implements the private-vela-fixed scheme for all three x402 roles: facilitator, client (buyer), and resource server (seller).

What it provides

  • Facilitator: verify and settle payments via submitRequestFor() on ProcessorEndpoint. /settle blocks until the TEE emits the matching AppEvent — a successful response is cryptographic proof that the transfer landed as specified.
  • Client: build, sign, and encrypt transfer payloads (EIP-712 + EIP-2612 + P-521 ECIES)
  • Resource server: configure PaymentRequirements with vela-nova invoiceId tracking
  • computeTransferReceiptHash: helper that recomputes vela-nova's AppEvent.eventSubType hash, for off-facilitator monitoring.

Installation

npm install @horizen/x402-private-vela-fixed @x402/core ethers

Requires @horizen/vela-common-ts for client-side P-521 encryption.


Facilitator usage

Register the scheme on an x402Facilitator instance from @x402/core:

import { x402Facilitator } from "@x402/core/facilitator";
import { registerPrivateVelaFixedScheme } from "@horizen/x402-private-vela-fixed";
import { ethers } from "ethers";

const facilitator = new x402Facilitator();

// Returns a Promise: the CAIP-2 network is derived from the RPC's chainId.
await registerPrivateVelaFixedScheme(facilitator, {
  rpcUrl: "https://rpc.vela.network",
  contractAddress: "0x<ProcessorEndpoint address>",
  signer: new ethers.Wallet(process.env.FACILITATOR_PRIVATE_KEY!),
  maxFeeValue: 0n,          // ETH in wei for service fees
  applicationId: 1n,        // vela-nova app ID
  // Optional: how long `/settle` waits for the TEE AppEvent (defaults 2s / 60s).
  appEventPollIntervalMs: 2_000,
  appEventPollTimeoutMs: 60_000,
});

The facilitator will handle POST /verify and POST /settle for the private-vela-fixed scheme. On /settle, the facilitator submits the request on-chain and then polls for the TEE's AppEvent matching the computed receipt hash before returning success; on timeout it returns errorReason: "tee_processing_timeout" while still reporting the on-chain requestId in extensions.

VelaSchemeConfig

| Field | Type | Description | |---|---|---| | rpcUrl | string | Ethereum JSON-RPC URL. The CAIP-2 network identifier (eip155:<chainId>) is derived from this at registration time. | | contractAddress | string | ProcessorEndpoint contract address | | signer | ethers.Signer | Facilitator wallet (pays gas) | | maxFeeValue | bigint | ETH in wei sent as msg.value for service fees | | applicationId | bigint | vela-nova application ID | | appEventPollIntervalMs | number? | Poll interval for the TEE AppEvent (default 2000) | | appEventPollTimeoutMs | number? | Timeout before /settle returns tee_processing_timeout (default 60000) |


Client (buyer) usage

Register the scheme on an x402Client instance to enable automatic payment on 402 responses:

import { x402Client } from "@x402/core/client";
import { registerPrivateVelaFixedClient } from "@horizen/x402-private-vela-fixed";
import { ethers } from "ethers";

const client = new x402Client("https://my-facilitator.example.com");

// Returns a Promise: the CAIP-2 network is derived from the RPC's chainId.
await registerPrivateVelaFixedClient(client, {
  signer: buyerSigner,           // ethers.Signer for EIP-712 + EIP-2612
  p521PrivateKey: buyerP521Key,  // buyer's P-521 CryptoKey (from vela-common-ts)
  teePublicKey: teeP521Key,      // TEE's P-521 CryptoKey (from ProcessorEndpoint.getPubSecp521r1())
  rpcUrl: "https://rpc.vela.network",
  contractAddress: "0x<ProcessorEndpoint address>",
});

// Automatically handles 402 responses:
const response = await client.fetch("https://seller.example.com/protected-content");

When the resource server returns a 402 Payment Required, the client:

  1. Reads the current nonce from facilitatorNonces[sender] on-chain
  2. Builds the PayloadInstructions JSON: { type: "transfer", transfer: { to, amount, invoice_id } }
  3. Encrypts the payload with the TEE's P-521 public key using ECIES (via @horizen/vela-common-ts)
  4. Signs the RequestAuthorization with EIP-712
  5. Signs an EIP-2612 permit for the ERC-20 deposit
  6. Retries the request with the payment payload in the X-PAYMENT header

VelaClientConfig

| Field | Type | Description | |---|---|---| | signer | ethers.Signer | Buyer's Ethereum signer | | p521PrivateKey | CryptoKey | Buyer's P-521 ECDH private key | | teePublicKey | CryptoKey | TEE's P-521 ECDH public key | | rpcUrl | string | Ethereum JSON-RPC URL. The CAIP-2 network identifier is derived from this. | | contractAddress | string | ProcessorEndpoint contract address | | applicationId | bigint? | vela-nova application ID (defaults to 1n) | | skipOnchainDeposit | boolean? | If true, settle is a pure private-state transfer (no on-chain deposit / no permit). The buyer must have already deposited beforehand. Default false. |


Resource server (seller) usage

Register the scheme on an x402ResourceServer to protect routes with payment requirements:

import { x402ResourceServer } from "@x402/core/server";
import { registerPrivateVelaFixedServer } from "@horizen/x402-private-vela-fixed";

const facilitatorClient = /* fetch-based facilitator client */;
const resourceServer = new x402ResourceServer(facilitatorClient);

registerPrivateVelaFixedServer(resourceServer, {
  network: "eip155:2651420",
  payTo: "0x<seller address>",
  tokenAddress: "0x<ERC-20 token address>",
  contractAddress: "0x<ProcessorEndpoint address>",
});

// Express middleware example:
app.use("/paid-content", resourceServer.middleware({
  amount: "1000000",         // token units
  extra: { invoiceId: "INV-001" },  // seller tracks this via TEE events
}));

The seller configures an invoiceId per-route in PaymentRequirements.extra. The facilitator cannot verify it (payload is encrypted), so the seller checks the invoice_id in the TEE event emitted after the TEE processes the transfer.

VelaServerConfig

| Field | Type | Description | |---|---|---| | network | string | CAIP-2 network identifier | | payTo | string | Seller's Ethereum address | | tokenAddress | string | ERC-20 token for payments | | contractAddress | string | ProcessorEndpoint contract address |


EIP-712 domain and types

Domain:

name: "Vela"
version: "0"
chainId: <chain ID>
verifyingContract: <ProcessorEndpoint address>

RequestAuthorization type hash:

RequestAuthorization(
  address sender,
  uint8 protocolVersion,
  uint64 applicationId,
  uint8 requestType,
  bytes32 payloadHash,
  address tokenAddress,
  uint256 assetAmount,
  uint256 nonce,
  uint256 deadline
)

Request type constants:

  • REQUEST_TYPE_PROCESS = 1 — private transfer
  • REQUEST_TYPE_ASSOCIATEKEY = 3 — P-521 key registration

Exported types and interfaces

// From "@horizen/x402-private-vela-fixed"

// EIP-712 typed data
interface RequestAuthorization { sender, protocolVersion, applicationId, requestType, payloadHash, tokenAddress, assetAmount, nonce, deadline }

// EIP-2612 permit
interface DepositPermit { owner, spender, value, nonce, deadline, v, r, s }

// Full x402 payment payload
interface VelaPaymentPayload { sender, requestSignature, depositPermit, requestAuthorization, payload }

// Payload sent to TEE (encrypted)
interface PayloadInstructions { type: "transfer"; transfer: TransferInstruction }
interface TransferInstruction { to, amount, invoice_id, asset }

// Config types
interface VelaSchemeConfig { ... }   // facilitator
interface VelaClientConfig { ... }   // buyer
interface VelaServerConfig { ... }   // seller

Transfer receipt hash

computeTransferReceiptHash is a TypeScript port of the hash vela-nova emits as AppEvent.eventSubType when the TEE successfully processes a transfer with a non-empty invoiceId:

keccak256(
  uint32_be(len(invoiceId)) ||
  invoiceId                 ||
  sender        (20 bytes)  ||
  tokenAddress  (20 bytes)  ||
  amount        (32 bytes, big-endian zero-padded) ||
  recipient     (20 bytes)
)

Because the hash binds invoiceId + sender + tokenAddress + amount + recipient, a matching AppEvent is cryptographic proof that the TEE executed exactly that transfer. The facilitator uses this internally in /settle; sellers can use it to subscribe to the right AppEvent independently:

import { computeTransferReceiptHash } from "@horizen/x402-private-vela-fixed";

const expected = computeTransferReceiptHash({
  invoiceId: "INV-001",
  sender: buyerAddress,
  tokenAddress,
  amount: 1_000_000n,
  recipient: sellerAddress,
});

// Subscribe via VelaClient.getAppEvents(fromBlock, toBlock, appId, requestId, expected)

Compatibility note

This scheme uses EIP-2612 permit() for gasless ERC-20 approval (sequential nonces). Coinbase's reference x402 scheme uses EIP-3009 transferWithAuthorization instead. The two are not interchangeable — the ProcessorEndpoint.submitRequestFor() contract function specifically accepts EIP-2612 permit signatures, not EIP-3009.

References