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

@hazbase/relayer

v0.0.18

Published

The relayer SDK that provides gasless execution (meta-transactions) for the hazBase stack.

Downloads

51

Readme

@hazbase/relayer

npm version License

Overview

@hazbase/relayer is the relayer SDK that provides gasless execution (meta-transactions) for the hazBase stack.
It is designed to be used together with @hazbase/kit’s ContractBuilder: build calldata & gas estimates with the builder, then sign and relay the request with this package.

  • Recommended composition: ContractBuilder (call assembly) + @hazbase/relayer (EIP‑712 signing & relay) + @hazbase/auth (JWT/client‑key/audit logging)
  • Typical use cases: frictionless first‑time UX, sponsored transactions for KYC‑verified users, better mobile UX
  • Security: one‑time Auth Code + ForwardRequest.nonce for replay protection, strict EIP‑712 domain separation

Flow (architecture)

  1. Build calldata & (optionally) gas with ContractBuilder (encode, estimate)
  2. Build ForwardRequest (buildForwardRequest)
  3. Produce EIP‑712 signature (signForwardRequest)
  4. Obtain one‑time Auth Code (generateRelayerAuthCode)
  5. Send to relayer (sendRelayerTransaction) → returns tx hash

Prefer forwardCall to do all steps in one go (ABI/method/args → relay). When you also use ContractBuilder, you can pre‑compute data/gas more precisely.


Requirements

  • Node.js >= 18 (ESM recommended)
  • TypeScript >= 5
  • ethers v6
  • @hazbase/auth (API endpoint & client key configuration, audit logging)
  • @hazbase/kit (ContractBuilder)

Installation

npm i @hazbase/relayer @hazbase/kit @hazbase/auth ethers
# or
pnpm add @hazbase/relayer @hazbase/kit @hazbase/auth ethers

Pre‑setup (@hazbase/auth)

// All comments in English per project rule
import { setClientKey } from '@hazbase/auth';

setClientKey(process.env.HAZBASE_CLIENT_KEY!);               // required for validation & logging

Quick start (with ContractBuilder)

A) Builder + forwardCall (fastest path)

// Use ContractBuilder to centralize address/abi, then relay with forwardCall
import { ethers } from 'ethers';
import { signInWithWallet } from '@hazbase/auth';
import { ContractBuilder } from '@hazbase/kit';
import { forwardCall } from '@hazbase/relayer';
import erc20Abi from './abi/MyToken.json' assert { type: 'json' };

const CHAIN_ID = "11155111"; // repolia testnet
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL!);
const signer   = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);

/** 1) Build a contract helper (address/abi binding) */
const tokenAddress = '0xToken...';

const signer = await provider.getSigner();

const { accessToken } = await signInWithWallet({ signer });

const contract = ContractBuilder
  .create({ address: tokenAddress, abi: erc20Abi, chainId: CHAIN_ID, signer })
  .withRelayer({ accessToken })
  .build();

const decimals = await contract.decimals();
const amountWei = ethers.parseUnits(amount.toString(), decimals);
await contract.transfer(toAddress, amountWei);

B) Builder + low‑level control (explicit data / gas)

// Use ContractBuilder to encode calldata and estimate gas, then sign & relay
import { ethers } from 'ethers';
import { ContractBuilder } from '@hazbase/kit';
import {
  buildForwardRequest, makeDomain, signForwardRequest,
  generateRelayerAuthCode, sendRelayerTransaction
} from '@hazbase/relayer';
import erc20Abi from './abi/MyToken.json' assert { type: 'json' };

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL!);
const signer   = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const chainId  = Number((await provider.getNetwork()).chainId);

/** 1) Prepare builder */
const tokenAddress = '0xToken...';
const builder = ContractBuilder
  .create({ address: tokenAddress, abi: erc20Abi, chainId: CHAIN_ID, signer })
  .build();

/** 2) Encode calldata via builder */
const to = '0xRecipient...';
const amount = 1_000n;
const data = builder.encode('transfer', [to, amount]); // or builder.interface.encodeFunctionData(...)

/** 3) Build ForwardRequest */
const req = await buildForwardRequest({
  signer,
  tokenAddress,             // maps to request.to
  chainId
});

/** 4) (Optional) refine gas with builder */
const gasEst = await builder.estimate('transfer', [to, amount]); // typical helper name
req.gas = BigInt(Math.ceil(Number(gasEst) * 1.2));               // add safety margin

/** 5) Inject calldata and sign typed data */
req.data = data;
const domain    = makeDomain(chainId);
const signature = await signForwardRequest({ signer, domain, request: req });

/** 6) Obtain one-time Auth Code and send */
const code   = await generateRelayerAuthCode({
  accessToken: 'JWT-from-signInWithWallet',
  chainId, contractAddress: tokenAddress
});
const txHash = await sendRelayerTransaction({ chainId, code, request: req, signature });
console.log('relayed tx =', txHash);

The exact API names on ContractBuilder (e.g., encode, estimate) can vary by version. Please consult the type definitions (.d.ts) or your editor’s IntelliSense.


Function reference

forwardCall(params: ForwardCallParams) => Promise<string>

Purpose: from ABI/method/args, do ForwardRequest build → sign → get Auth Code → send, and return the tx hash.
Params

  • signer: ethers.JsonRpcSigner
  • chainId: number
  • accessToken: string — JWT obtained from @hazbase/auth.signInWithWallet
  • contractAddress: string
  • abi: ethers.InterfaceAbi
  • method: string
  • args: unknown[]

Returns: stringtransaction hash (relayer response)


generateRelayerAuthCode({ accessToken, chainId, contractAddress, type? }) => Promise<string>

Purpose: fetch a one‑time code required to execute via relayer.
Details

  • Internally calls ensureClientKeyActive(70)POST /api/app/code/generate-code
  • On success, logs createRequestTransaction(functionId=70, status='succeeded')

Params

  • accessToken: string
  • chainId: number
  • contractAddress: string
  • type?: string (default 'relayer_auth')

Returns: string — one‑time code


buildForwardRequest({ signer, tokenAddress, chainId }) => Promise<ForwardRequest>

Purpose: build a Minimal‑Forwarder compatible ForwardRequest (from,to,value,gas,nonce,data).
Details

  • Reads nonce from the chain’s Forwarder (getForwarderAddress(chainId))
  • Defaults: value = 0, gas is a coarse estimate (override with builder’s estimate if available)

Params

  • signer: ethers.JsonRpcSigner
  • tokenAddress: string (mapped to request to)
  • chainId: number

Returns: ForwardRequest


makeDomain(chainId: number) => Domain

Purpose: build the EIP‑712 domain (name, version, chainId, verifyingContract).
Note: verifyingContract is the chain’s Forwarder address.


signForwardRequest({ signer, domain, request }) => Promise<string>

Purpose: sign the ForwardRequest using EIP‑712 and return the signature.


sendRelayerTransaction({ chainId, code, request, signature }) => Promise<string>

Purpose: send the signed ForwardRequest to the relayer API and get the tx hash.

Returns: string — transaction hash (or relayer’s canonical identifier)


Types (primary)

export interface ForwardRequest {
  from:  string;
  to:    string;
  value: bigint;   // usually 0n
  gas:   bigint;   // estimation; override with builder's estimate
  nonce: bigint;   // forwarder.getNonce(from)
  data:  string;   // calldata from ContractBuilder.encode(...)
}

export interface Domain {
  name: string;
  version: string;
  chainId: number;
  verifyingContract: string;
}

export interface ForwardCallParams {
  signer:  ethers.JsonRpcSigner;
  chainId: number;
  accessToken: string;
  contractAddress: string;
  abi: ethers.InterfaceAbi;
  method: string;
  args: unknown[];
}

Utilities / constants (excerpt)

  • getRelayerUrl(chainId): string — resolve relayer API base URL
  • getForwarderAddress(chainId): string — resolve Minimal‑Forwarder address
  • bigintReplacerJSON.stringify replacer to stringify bigint

The chain → forwarder mapping depends on your environment. See your utils.ts / constants.ts.


Best practices

  • Normalize with the builder: centralize calldata generation & gas estimation in ContractBuilder.
  • Domain separation: always derive domain via makeDomain(); pin chainId and forwarder per env.
  • Re‑issue Auth Code on retry: it’s one‑time; also re‑read nonce on each retry.

Troubleshooting

  • Client key not set — call @hazbase/auth.setClientKey() first.
  • Relayer error — check result in the response (expired Auth Code / forwarder mismatch / encoding issues).
  • invalid signature — mismatch between domain and request. Ensure from matches the signer’s address.
  • nonce too low/high — refresh forwarder.getNonce(from) and update request.nonce.
  • execution reverted — check roles/paused state on target contract, arguments, and balances.

License

Apache-2.0