Node.js theorytheory 0/50 · 0%
Web3 integration · hard

49. Building a relayer/signer service

A backend that submits transactions on behalf of users.

A relayer service holds a hot wallet's key (ideally in a secrets manager, not plain env vars) and submits transactions on behalf of users — common for gasless UX, batching, or automated on-chain actions. It must track nonces itself, since sending multiple transactions in quick succession from the same address risks nonce collisions if you just query "next nonce" from the node each time.

js
class NonceManager {
  constructor(startNonce) { this.nonce = startNonce; }
  next() { return this.nonce++; } // reserve synchronously before any await
}

async function relayTransaction(nonceManager, buildAndSignTx, sendRawTx) {
  const nonce = nonceManager.next();
  const signedTx = await buildAndSignTx(nonce);
  return sendRawTx(signedTx);
}

Production relayers also need retry/replacement logic for stuck transactions (bumping gas price and resubmitting with the same nonce), and careful handling of failures so a rejected transaction doesn't permanently "burn" a nonce and stall every later transaction.

Check your understanding

  1. 1. Why can't a relayer safely just query 'next nonce' from the node before every send?

  2. 2. What happens if a transaction with a given nonce gets stuck or rejected without being replaced?

  3. 3. Where should a relayer's private key ideally be stored?