Skip to main content

Cloudflare Workers Deployment

Complete guide for deploying Loyalteez Cloudflare Workers to production with mainnet configurations.


Overview

Loyalteez infrastructure runs on 4 Cloudflare Workers that handle API requests, wallet creation, reward processing, and gasless transactions. This guide covers deployment, configuration, and troubleshooting.

Core Workers:

WorkerDomainPurpose
Event Handlerapi.loyalteez.appManual events, Stripe minting, SDK serving
Pregenerationregister.loyalteez.appEmail/OAuth wallet creation via Privy
Reward ProcessorScheduled (Cron)Background reward distribution
Gas Relayerrelayer.loyalteez.appGasless transaction execution

Social Engagement Workers:

WorkerDomainPurpose
Twitter Loyalty Routerx-{brand}.loyalteez.appTwitter/X engagement rewards (multi-tenant)
Farcaster Single-Brandfarcaster-bot.loyalteez.appLoyalteez's own Farcaster tracking
Farcaster Multi-Tenantfc.loyalteez.appShared Farcaster tracking for all brands

Prerequisites

# Install Wrangler CLI
npm install -g wrangler

# Authenticate with Cloudflare
wrangler login

# Verify authentication
wrangler whoami

Mainnet Configuration

All workers are configured for Soneium Mainnet (Chain ID: 1868).

Network Settings

CHAIN_ID=1868
NETWORK_NAME=soneium
RPC_URL=https://rpc.soneium.org
BLOCK_EXPLORER=https://soneium.blockscout.com/

Contract Addresses (Mainnet - Deployed 2024)

# LTZ Token (EIP-2612 Permit enabled)
LOYALTEEZ_ADDRESS=0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9

# Perk NFT (v6 - no salt requirement)
PERK_NFT_ADDRESS=0x6ae30d6Dcf3e75456B6582b057f1Bf98A90F2CA0

# Points Sale (V3 with Permit)
POINTS_SALE_ADDRESS=0x5269B83F6A4E31bEdFDf5329DC052FBb661e3c72

# USDC (Mainnet)
USDC_ADDRESS=0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369

Worker 1: Event Handler

Handles main API endpoints and SDK serving.

Routes

  • api.loyalteez.app/*
  • reward.loyalteez.app/*

Endpoints

# Health Check
GET https://api.loyalteez.app/health

# Manual Event Submission
POST https://api.loyalteez.app/loyalteez-api/manual-event

# Stripe Minting
POST https://api.loyalteez.app/loyalteez-api/stripe-mint

# SDK Serving
GET https://api.loyalteez.app/sdk.js

Configuration (wrangler.event-handler.toml)

name = "loyalteez-event-handler"
main = "src/event-handler/index.js"
compatibility_date = "2024-10-01"
compatibility_flags = ["nodejs_compat"]

[vars]
ENVIRONMENT = "production"
CHAIN_ID = "1868"
NETWORK_NAME = "soneium"
RPC_URL = "https://rpc.soneium.org"
BLOCK_EXPLORER = "https://soneium.blockscout.com/"
LOYALTEEZ_ADDRESS = "0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9"
PERK_NFT_ADDRESS = "0x6ae30d6Dcf3e75456B6582b057f1Bf98A90F2CA0"
POINTS_SALE_ADDRESS = "0x5269B83F6A4E31bEdFDf5329DC052FBb661e3c72"
PRIVY_APP_ID = "your_privy_app_id"
SUPABASE_URL = "https://your-project.supabase.co"

[[routes]]
pattern = "api.loyalteez.app/*"
zone_name = "loyalteez.app"

[[routes]]
pattern = "reward.loyalteez.app/*"
zone_name = "loyalteez.app"

Secrets (Encrypted)

# Set via wrangler secret put
wrangler secret put PRIVY_SECRET --config wrangler.event-handler.toml
wrangler secret put SUPABASE_SECRET_KEY --config wrangler.event-handler.toml
wrangler secret put WALLET_PRIVATE_KEY --config wrangler.event-handler.toml
wrangler secret put STRIPE_SECRET_KEY --config wrangler.event-handler.toml
wrangler secret put RESEND_API_KEY --config wrangler.event-handler.toml

Deploy

cd cloudflare-automation
npx wrangler deploy --config wrangler.event-handler.toml

Health Check Fix (Jan 2025)

Issue: Worker only responded to /loyalteez-api/health, not root /health.

Solution: Add root-level health check before API routing:

// src/event-handler/index.js
if (path === '/health' && request.method === 'GET') {
return successResponse({
status: 'healthy',
service: 'event-handler',
timestamp: new Date().toISOString(),
hostname: url.hostname,
routes: ['api.loyalteez.app/*', 'reward.loyalteez.app/*']
}, request);
}

Worker 2: Pregeneration

Creates Privy wallets for email addresses and OAuth users.

Route

  • register.loyalteez.app/*

Endpoints

# Health Check
GET https://register.loyalteez.app/health

# Wallet Pregeneration
POST https://register.loyalteez.app/loyalteez-api/pregenerate-user

Configuration (wrangler.pregeneration.toml)

name = "loyalteez-pregeneration"
main = "src/pregeneration/index.js"
compatibility_date = "2024-10-01"
compatibility_flags = ["nodejs_compat"]

[vars]
ENVIRONMENT = "production"
PRIVY_APP_ID = "your_privy_app_id"
SUPABASE_PUBLISH_KEY = "sb_publishable_..."

[[routes]]
pattern = "register.loyalteez.app/*"
zone_name = "loyalteez.app"

Secrets

wrangler secret put PRIVY_APP_SECRET --config wrangler.pregeneration.toml
wrangler secret put SUPABASE_URL --config wrangler.pregeneration.toml
wrangler secret put SUPABASE_SECRET_KEY --config wrangler.pregeneration.toml

Deploy

npx wrangler deploy --config wrangler.pregeneration.toml

Worker 3: Reward Processor

Background worker that processes pending rewards every minute.

Type

Scheduled Worker (Cron: */1 * * * *)

Endpoints

# Health Check (via workers.dev URL)
GET https://loyalteez-reward-processor.taylor-cox1199.workers.dev/health

# Manual Processing
POST https://loyalteez-reward-processor.taylor-cox1199.workers.dev/process-pending

# Queue Status
GET https://loyalteez-reward-processor.taylor-cox1199.workers.dev/queue

Configuration (wrangler.reward-processor.toml)

name = "loyalteez-reward-processor"
main = "src/reward-processor/index.js"
compatibility_date = "2023-11-01"

[vars]
ENVIRONMENT = "production"
CHAIN_ID = "1868"
RPC_URL = "https://rpc.soneium.org"
LOYALTEEZ_ADDRESS = "0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9"
PRIVY_APP_ID = "your_privy_app_id"

# Scheduled trigger - every 1 minute
[triggers]
crons = ["*/1 * * * *"]

# Note: No custom route - handled by Event Handler at reward.loyalteez.app

Secrets

wrangler secret put PRIVY_SECRET --config wrangler.reward-processor.toml
wrangler secret put SUPABASE_SECRET_KEY --config wrangler.reward-processor.toml
wrangler secret put WALLET_PRIVATE_KEY --config wrangler.reward-processor.toml
wrangler secret put RESEND_API_KEY --config wrangler.reward-processor.toml

Deploy

npx wrangler deploy --config wrangler.reward-processor.toml

Worker 4: Gas Relayer

Executes gasless transactions on behalf of users.

Route

  • relayer.loyalteez.app/*

Endpoints

# Health Check
GET https://relayer.loyalteez.app/health

# Relay Transaction (requires Privy auth)
POST https://relayer.loyalteez.app/relay

Configuration (wrangler-gas-relayer.toml)

name = "loyalteez-gas-relayer"
main = "src/gas-relayer/worker.js"
compatibility_date = "2024-10-01"

[vars]
VITE_RPC_URL = "https://rpc.soneium.org"
VITE_PRIVY_APP_ID = "your_privy_app_id"
DEV_WALLET_ADDRESS = "0x92627010DF82F6aA16eeCb45C4bD2207140C52A7"
VITE_LOYALTEEZ_ADDRESS = "0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9"
VITE_PERK_NFT_ADDRESS = "0x6ae30d6Dcf3e75456B6582b057f1Bf98A90F2CA0"
VITE_POINTS_SALE_ADDRESS = "0x5269B83F6A4E31bEdFDf5329DC052FBb661e3c72"

[[kv_namespaces]]
binding = "RELAYER_KV"
id = "your_kv_namespace_id"

[env.production]
name = "loyalteez-gas-relayer"
routes = [
{ pattern = "relayer.loyalteez.app/*", zone_name = "loyalteez.app" }
]

Secrets

wrangler secret put DEV_WALLET_PRIVATE_KEY --config wrangler-gas-relayer.toml
wrangler secret put PRIVY_SECRET --config wrangler-gas-relayer.toml
wrangler secret put VITE_SUPABASE_SECRET_KEY --config wrangler-gas-relayer.toml
wrangler secret put VITE_SUPABASE_PUBLISH_KEY --config wrangler-gas-relayer.toml

Deploy

npx wrangler deploy --config wrangler-gas-relayer.toml --env production

Automated Deployment

Use the provided deployment script to deploy all workers at once.

deploy-workers.sh

#!/bin/bash
# Deploy all workers to production

echo "🚀 Deploying all Cloudflare Workers..."

# 1. Event Handler
echo "📦 [1/4] Deploying Event Handler..."
npx wrangler deploy --config wrangler.event-handler.toml

# 2. Pregeneration
echo "📦 [2/4] Deploying Pregeneration..."
npx wrangler deploy --config wrangler.pregeneration.toml

# 3. Reward Processor
echo "📦 [3/4] Deploying Reward Processor..."
npx wrangler deploy --config wrangler.reward-processor.toml

# 4. Gas Relayer
echo "📦 [4/4] Deploying Gas Relayer..."
npx wrangler deploy --config wrangler-gas-relayer.toml --env production

echo "✅ All workers deployed successfully!"

Usage

cd cloudflare-automation
chmod +x deploy-workers.sh
./deploy-workers.sh

Health Check Verification

After deployment, verify all workers are healthy:

# Event Handler
curl https://api.loyalteez.app/health
# Expected: {"status":"healthy","service":"event-handler",...}

# Pregeneration
curl https://register.loyalteez.app/health
# Expected: {"status":"healthy","service":"oauth-pregeneration",...}

# Gas Relayer
curl https://relayer.loyalteez.app/health
# Expected: {"status":"healthy","service":"gas-relayer",...}

# Reward Processor (workers.dev URL)
curl https://loyalteez-reward-processor.taylor-cox1199.workers.dev/health
# Expected: {"status":"healthy","worker":"reward-processor",...}

Monitoring & Logs

Tail Logs in Real-Time

# Event Handler
wrangler tail loyalteez-event-handler

# Pregeneration
wrangler tail loyalteez-pregeneration

# Reward Processor
wrangler tail loyalteez-reward-processor

# Gas Relayer
wrangler tail loyalteez-gas-relayer

View Deployment History

# List deployments
wrangler deployments list --name loyalteez-event-handler

# View specific deployment
wrangler deployments view <deployment-id>

Check Worker Status

# Worker details
wrangler status --name loyalteez-event-handler

# KV namespace list
wrangler kv:namespace list

# KV namespace keys
wrangler kv:key list --namespace-id <namespace-id>

Troubleshooting

404 on Health Endpoints

Symptoms:

  • curl https://api.loyalteez.app/health returns 404
  • Worker shows as deployed in dashboard

Causes:

  1. Worker only has /loyalteez-api/health, not root /health
  2. DNS propagation delay (5-30 minutes)
  3. Route not configured

Solutions:

  1. Add root-level health check (Fixed in Jan 2025):
// Before API routing
if (path === '/health' && request.method === 'GET') {
return successResponse({
status: 'healthy',
service: 'event-handler',
timestamp: new Date().toISOString()
}, request);
}
  1. Wait for DNS propagation:
# Check DNS
nslookup api.loyalteez.app

# Test via workers.dev URL while waiting
curl https://loyalteez-event-handler.taylor-cox1199.workers.dev/health
  1. Verify routes in dashboard:
  • Cloudflare Dashboard → Workers & Pages → loyalteez-event-handler
  • Check "Domains & Routes" tab

Route Conflicts

Error: "A route with the same pattern already exists"

Solution: Remove duplicate route from wrangler.toml:

# BAD: Both workers define reward.loyalteez.app/*
# In event-handler:
[[routes]]
pattern = "reward.loyalteez.app/*"
# In reward-processor:
[[routes]]
pattern = "reward.loyalteez.app/*" # ❌ CONFLICT!

# GOOD: Only Event Handler serves reward.loyalteez.app
# reward-processor has no custom route (runs on schedule)

Secrets Not Working

Symptoms:

  • Worker deploys but fails at runtime
  • Authentication errors in logs

Solution:

# List existing secrets
wrangler secret list --config wrangler.event-handler.toml

# Set missing secrets
wrangler secret put PRIVY_SECRET --config wrangler.event-handler.toml

# Redeploy after setting secrets
npx wrangler deploy --config wrangler.event-handler.toml

Cron Not Firing

Symptoms:

  • Reward Processor not running
  • No scheduled executions in logs

Solution:

  1. Verify cron trigger in wrangler.toml:
[triggers]
crons = ["*/1 * * * *"] # Every minute
  1. Check scheduled logs:
wrangler tail loyalteez-reward-processor --format pretty
  1. Manually trigger:
curl -X POST https://loyalteez-reward-processor.taylor-cox1199.workers.dev/process-pending

Security Best Practices

1. Never Commit Secrets

# ✅ Good - stored encrypted
wrangler secret put PRIVY_SECRET

# ❌ Bad - visible in code
[vars]
PRIVY_SECRET = "actual_secret_value"

2. Use Environment-Specific Configs

# Production
[env.production]
name = "loyalteez-gas-relayer"
routes = [
{ pattern = "relayer.loyalteez.app/*", zone_name = "loyalteez.app" }
]

# Development
[env.development]
name = "loyalteez-gas-relayer-dev"
# No custom routes - use workers.dev URL

3. Restrict CORS Origins

// Development
const corsHeaders = {
'Access-Control-Allow-Origin': '*'
};

// Production
const corsHeaders = {
'Access-Control-Allow-Origin': 'https://partners.loyalteez.app'
};

4. Rate Limiting

Use KV namespaces for rate limiting:

const rateLimit = async (userAddress, limit = 35) => {
const key = `rate_limit_${userAddress}_${Math.floor(Date.now() / 3600000)}`;
const count = await env.RELAYER_KV.get(key) || 0;

if (count >= limit) {
throw new Error('Rate limit exceeded');
}

await env.RELAYER_KV.put(key, count + 1, { expirationTtl: 3600 });
};

Performance Optimization

1. Use KV for Caching

// Cache DNS lookups
const cachedDNS = await env.DNS_CACHE.get(`dns_${domain}`);
if (cachedDNS) {
return JSON.parse(cachedDNS);
}

const dnsRecord = await fetchDNSTxtRecord(domain);
await env.DNS_CACHE.put(`dns_${domain}`, JSON.stringify(dnsRecord), {
expirationTtl: 3600 // 1 hour
});

2. Batch Database Queries

// BAD: N+1 queries
for (const event of events) {
await database.getEvent(event.id);
}

// GOOD: Single batch query
const eventIds = events.map(e => e.id);
const eventData = await database.batchGetEvents(eventIds);

3. Use Durable Objects Sparingly

Durable Objects are powerful but expensive. Use KV for:

  • Simple key-value storage
  • Rate limiting
  • Caching

Use Durable Objects for:

  • Real-time collaboration
  • Strong consistency requirements
  • Coordination between workers

Rollback Strategy

Rollback to Previous Version

# List deployments
wrangler deployments list --name loyalteez-event-handler

# Rollback to specific version
wrangler rollback --name loyalteez-event-handler --version <version-id>

# Verify rollback
curl https://api.loyalteez.app/health

Gradual Rollout

# Deploy to 10% of traffic first
[env.canary]
name = "loyalteez-event-handler-canary"
routes = [
{ pattern = "api.loyalteez.app/*", zone_name = "loyalteez.app", percentage = 10 }
]

Social Engagement Workers

In addition to the core infrastructure, Loyalteez provides social engagement workers that track brand mentions and reward users automatically.

Twitter Loyalty Bot

Polls Twitter API for brand mentions and rewards users.

PropertyValue
Domainx-{brand}.loyalteez.app
APITwitter API v2
Eventstweet_mention, tweet_reply, tweet_like, tweet_retweet
DocumentationTwitter Integration

Quick Deploy:

cd x-loyalty-bot
npx wrangler kv:namespace create TWITTER_BOT_KV
npx wrangler secret put TWITTER_BEARER_TOKEN
npx wrangler secret put SUPABASE_PUBLISH_KEY
npx wrangler deploy

Farcaster Loyalty Bot

Polls Neynar API for Farcaster engagements and rewards users.

PropertyValue
Single-Brand Domainfarcaster-bot.loyalteez.app
Multi-Tenant Domainfc.loyalteez.app
APINeynar API
Eventsfarcaster_mention, farcaster_reply, farcaster_like, farcaster_recast, farcaster_follow
DocumentationFarcaster Integration

Quick Deploy (Single-Brand):

cd farcaster-loyalty-bot
npx wrangler kv namespace create FARCASTER_BOT_KV
npx wrangler secret put NEYNAR_API_KEY
npx wrangler secret put SUPABASE_PUBLISH_KEY
npx wrangler deploy

Quick Deploy (Multi-Tenant Router):

cd farcaster-loyalty-bot
npx wrangler kv namespace create FARCASTER_ROUTER_KV
npx wrangler secret put NEYNAR_API_KEY -c wrangler.router.toml
npx wrangler deploy -c wrangler.router.toml

Common Configuration

Both social bots follow the same patterns:

# wrangler.toml template for social bots
name = "{platform}-loyalty-bot"
main = "src/index.js"
compatibility_date = "2024-11-23"
compatibility_flags = ["nodejs_compat"]

[[kv_namespaces]]
binding = "{PLATFORM}_BOT_KV"
id = "your_kv_id"

# Service binding for direct worker communication
[[services]]
binding = "EVENT_HANDLER"
service = "loyalteez-event-handler"

# Poll every 15 minutes
[triggers]
crons = ["*/15 * * * *"]

[vars]
BRAND_ID = "0xYourBrandWallet"
LOYALTEEZ_API_URL = "https://api.loyalteez.app"

User Identification Pattern

Both bots use synthetic emails for deterministic wallet creation:

// Twitter
const email = `twitter_${userId}@loyalteez.app`;

// Farcaster
const email = `farcaster_${fid}@loyalteez.app`;


Changelog

January 2025

  • ✅ Added root-level /health endpoint to Event Handler
  • ✅ Verified mainnet configurations (Chain ID: 1868)
  • ✅ Fixed route conflicts (reward.loyalteez.app only on Event Handler)
  • ✅ Updated contract addresses for 2024 mainnet deployment
  • ✅ Created automated deployment script
  • ✅ Documented health check verification

December 2024

  • Initial Cloudflare Workers documentation

Need Help? Join our Discord or email [email protected]