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

evm-randomness

v0.4.0

Published

Minimal, typed SDK skeleton for HyperEVM VRF

Readme

HyperEVM VRF SDK

TypeScript SDK to request and fulfill on-chain VRF using DRAND beacons.

TL;DR (Quick Start)

  • Install: pnpm add evm-randomness
  • Minimal usage:
import { HyperEVMVRF } from "evm-randomness";
const vrf = new HyperEVMVRF({ account: { privateKey: process.env.PRIVATE_KEY! }, chainId: 999 });
const { requestId } = await vrf.requestRandomness({ deadline: BigInt(Math.floor(Date.now()/1000)+120) });
await vrf.fulfillWithWait(requestId);

No private key input (generate ephemeral wallet)

import { createEphemeralWallet } from "evm-randomness";

const { vrf, address } = await createEphemeralWallet({
  chainId: 999,
  minBalanceWei: 1_000_000_000_000_000n, // 0.001 HYPE
});
console.log("Send gas to:", address);

const deadline = BigInt(Math.floor(Date.now()/1000)+120);
const { requestId } = await vrf.requestRandomness({ deadline });
await vrf.fulfillWithWait(requestId);

Why this SDK

  • Request + Fulfill DRAND-powered VRF on HyperEVM
  • Typed API, ESM/CJS builds
  • Chain-aware defaults (rpc, VRF address, DRAND beacon), configurable
  • Policy control (strict/window/none) and wait-until-published helpers

Installation

pnpm add hyperevm-vrf-sdk
# or
npm i hyperevm-vrf-sdk
# or
yarn add hyperevm-vrf-sdk

Quickstart

import { HyperEVMVRF } from "evm-randomness";

const vrf = new HyperEVMVRF({
  account: { privateKey: process.env.WALLET_PRIVATE_KEY! },
  // optional overrides shown below
});

const result = await vrf.fulfill(1234n);
console.log(`Fulfilled request ${result.requestId} with round ${result.round}`);
console.log(`Transaction hash: ${result.txHash}`);

This will:

  • Read request metadata from the VRF contract
  • Compute the required drand round from the request deadline and minRound
  • Wait until the round is available (if needed) and fetch its signature
  • Submit fulfillRandomness on-chain
  • Return fulfillment details including transaction hash

Configuration (Schema)

new HyperEVMVRF(config) accepts:

interface HyperevmVrfConfig {
  rpcUrl?: string;                  // default resolved from chain (or HyperEVM)
  vrfAddress?: string;              // default resolved from chain (or HyperEVM)
  chainId?: number;                 // default: 999 (HyperEVM)
  account: { privateKey: string };  // required
  policy?: { mode: "strict" | "window"; window?: number } | undefined; // default: { mode: "window", window: 10000 }
  drand?: { baseUrl?: string; fetchTimeoutMs?: number; beacon?: string }; // defaults: api.drand.sh/v2, 8000ms, evmnet
  gas?: { maxFeePerGasGwei?: number; maxPriorityFeePerGasGwei?: number };
}

Defaults are exported from defaultConfig and defaultVRFABI. Chain info available via CHAINS.

Address/Chain Resolution

  • If you pass chainId, the SDK will resolve reasonable defaults (rpcUrl, drand beacon, and optionally a known vrfAddress).
  • You can override any field explicitly in config.

Policy Enforcement

The SDK enforces VRF request policies to ensure randomness quality and security:

  • strict mode: Only allows fulfillment when the target round is exactly the latest published round
  • window mode: Allows fulfillment when the target round is within a specified window of the latest round
  • No policy: Explicitly disable policy enforcement by setting policy: undefined

Default Behavior: When no policy is specified, the SDK uses a very generous window of 10000 rounds to ensure requests can be fulfilled even if they've been waiting for a long time. This provides maximum usability while still having some reasonable upper bound.

Note: With DRAND's 30-second round interval, a window of 10000 rounds allows requests that are up to ~83 hours (3.5 days) old to be fulfilled. This ensures excellent user experience for most scenarios.

Boundary Case Handling

The SDK includes comprehensive boundary case handling for robust operation:

  • Deadline == Genesis: Handles cases where request deadline exactly matches or precedes genesis time
  • Divisible Deltas: Correctly processes time deltas that are exactly divisible by DRAND period
  • Window Boundaries: Enforces policy limits at exact window boundaries (0, 1, 2, etc.)
  • Future Rounds: Rejects attempts to fulfill with rounds that haven't been published yet
// Strict policy - only fulfill with latest round
const vrf = new HyperEVMVRF({
  account: { privateKey: process.env.PRIVATE_KEY! },
  policy: { mode: "strict" }
});

// Window policy - allow up to 3 rounds behind latest
const vrf = new HyperEVMVRF({
  account: { privateKey: process.env.PRIVATE_KEY! },
  policy: { mode: "window", window: 3 }
});

// No policy enforcement - allow any round difference
const vrf = new HyperEVMVRF({
  account: { privateKey: process.env.PRIVATE_KEY! },
  policy: undefined
});

// Default policy (very generous window=10000) when no policy specified
const vrf = new HyperEVMVRF({
  account: { privateKey: process.env.PRIVATE_KEY! }
  // Uses default: { mode: "window", window: 10000 }
});

Policy violations throw VrfPolicyViolationError with detailed context about the violation.

Gas Configuration

The SDK supports custom gas settings for VRF fulfillment transactions:

const vrf = new HyperEVMVRF({
  account: { privateKey: process.env.PRIVATE_KEY! },
  gas: { 
    maxFeePerGasGwei: 50,        // Maximum fee per gas in Gwei
    maxPriorityFeePerGasGwei: 2  // Maximum priority fee per gas in Gwei
  }
});

Gas Settings:

  • maxFeePerGasGwei: Maximum total fee per gas (base fee + priority fee) in Gwei
  • maxPriorityFeePerGasGwei: Maximum priority fee per gas in Gwei (tip for miners/validators)

Note: Values are specified in Gwei for convenience and automatically converted to Wei for transaction submission.

Environment

  • Node.js >= 18
  • Set WALLET_PRIVATE_KEY (or pass directly) for the signer

Example .env (never commit private keys):

WALLET_PRIVATE_KEY=0xabc123...

Load it in scripts/tests with dotenv if needed.

API (Surface)

  • class HyperEVMVRF

    • constructor(config: HyperevmVrfConfig)
    • requestRandomness({ deadline, consumer?, salt? }): Promise<{ requestId, txHash }>
    • fulfill(requestId: bigint): Promise<FulfillResult>
    • fulfillWithWait(requestId: bigint, opts?): Promise<FulfillResult>
    • requestAndFulfill({ deadline, consumer?, salt?, wait? }): Promise<{ requestId, round, signature, requestTxHash, fulfillTxHash }>
  • helper

    • createEphemeralWallet(options): Promise<{ vrf, address }> – in-memory account + optional funding wait

Error Handling

The SDK provides comprehensive typed error handling with specific error classes for different failure scenarios:

Error Classes

  • HyperEVMVrfError - Base error class for all SDK errors
  • ConfigurationError - Invalid configuration parameters
  • VrfRequestError - Base class for VRF request-related errors
    • VrfRequestAlreadyFulfilledError - Request has already been fulfilled
    • VrfTargetRoundNotPublishedError - Target DRAND round not yet available
    • VrfPolicyViolationError - Policy enforcement violations
  • DrandError - DRAND network or signature errors
    • DrandRoundMismatchError - Round mismatch between expected and received
    • DrandSignatureError - Invalid signature format
  • NetworkError - Network communication errors
    • HttpError - HTTP status code errors
    • JsonParseError - JSON parsing failures
  • ContractError - Smart contract interaction errors
  • TransactionError - Transaction mining failures

Error Properties

All errors include:

  • message: Human-readable error description
  • code: Error category identifier
  • details: Additional context information
  • name: Error class name for type checking

Example Error Handling

import { HyperEVMVRF, ConfigurationError, VrfRequestAlreadyFulfilledError } from "hyperevm-vrf-sdk";

try {
  const vrf = new HyperEVMVRF({
    account: { privateKey: "invalid_key" }
  });
} catch (error) {
  if (error instanceof ConfigurationError) {
    console.log(`Configuration error in field: ${error.field}`);
    console.log(`Details:`, error.details);
  }
}

try {
  await vrf.fulfill(requestId);
} catch (error) {
  if (error instanceof VrfRequestAlreadyFulfilledError) {
    console.log(`Request ${error.requestId} already fulfilled`);
  } else if (error instanceof VrfTargetRoundNotPublishedError) {
    console.log(`Waiting ${error.secondsLeft}s for round ${error.targetRound}`);
  } else if (error instanceof VrfPolicyViolationError) {
    console.log(`Policy violation: ${error.policyMode} mode requires round difference <= ${error.policyWindow}`);
    console.log(`Current: ${error.currentRound}, Target: ${error.targetRound}, Difference: ${error.roundDifference}`);
  }
}

Error Codes

import { ERROR_CODES } from "evm-randomness";

// Available error codes:
// ERROR_CODES.VRF_REQUEST_ERROR
// ERROR_CODES.DRAND_ERROR  
// ERROR_CODES.NETWORK_ERROR
// ERROR_CODES.CONFIGURATION_ERROR
// ERROR_CODES.CONTRACT_ERROR
// ERROR_CODES.TRANSACTION_ERROR

Return Types

The fulfill method returns a FulfillResult object:

interface FulfillResult {
  requestId: bigint;           // The fulfilled request ID
  round: bigint;               // The DRAND round used
  signature: [bigint, bigint]; // BLS signature components
  txHash: `0x${string}`;      // Transaction hash
}

Usage Examples

  • Minimal request + fulfill:
import "dotenv/config";
import { HyperEVMVRF } from "hyperevm-vrf-sdk";

async function main() {
  const vrf = new HyperEVMVRF({ account: { privateKey: process.env.PRIVATE_KEY! }, chainId: 999, policy: undefined });
  const deadline = BigInt(Math.floor(Date.now()/1000)+120);
  const { requestId } = await vrf.requestRandomness({ deadline });
  const res = await vrf.fulfillWithWait(requestId);
  console.log(res);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
  • Custom endpoints and gas:
const vrf = new HyperEVMVRF({
  rpcUrl: "https://rpc.hyperliquid.xyz/evm",
  vrfAddress: "0xCcf1703933D957c10CCD9062689AC376Df33e8E1",
  chainId: 999,
  account: { privateKey: process.env.WALLET_PRIVATE_KEY! },
  drand: { baseUrl: "https://api.drand.sh/v2", fetchTimeoutMs: 8000, beacon: "evmnet" },
  gas: { maxFeePerGasGwei: 50, maxPriorityFeePerGasGwei: 2 },
});

How it works (high level)

  • Reads the VRF request from the contract
  • Queries DRAND beacon for info to map deadline -> round
  • Ensures the target round is published, fetches its BLS signature
  • Calls fulfillRandomness(id, round, signature) on the VRF contract

Scripts

  • pnpm build – build library with types
  • pnpm dev – watch build
  • pnpm lint – eslint check
  • pnpm test – run unit tests (vitest)

Scope / Notes

  • This SDK performs DRAND round selection (max(minRound, roundFromDeadline)) and signature retrieval.
  • Default policy is permissive (window=10000). Set policy: undefined to disable or strict/window to enforce.
  • For consumer contracts like your Lottery V2, you typically don’t need requestRandomness() because the consumer requests it during its flow; you only need fulfill*.

License

MIT