Skip to content

airdrop-indexer — Railway deploy guide ​

Last refreshed 2026-06-26. Companion to airdrop-indexer/README.md.

TL;DR — should you deploy this tonight? ​

Short answer: no.

The codebase at airdrop-indexer/ is built for the pre-2026-06-25 design: binary per-product qualifies() → unlock-% model. After the points system rewrite (PR #364), this is no longer the spec. The current canonical design is in docs/airdrop-points-system-design.md and uses USD-value × time × caps with a 13-product matrix.

Deploying as-is gives you:

  • A Postgres schema that doesn't match the points-per-time model
  • A products.ts registry that returns yes/no per product (no scoring)
  • A Merkle tree generator that's correct, but for a pool we don't use (we chose registered-claim — see docs/airdrop-distributor-spec.md)

Recommendation: keep the service un-deployed for now. Marketing kicks off with on-demand eligibility via airdrop-app (Alchemy). The indexer rewrite happens during Week 1-3 of mainnet engineering, then this guide becomes immediately useful.

The setup steps below are correct for the rewritten version too — only the schema initialization step will need adjustment when the new spec lands.

Service architecture (post-rewrite) ​

                   ┌──────────────────────────┐
                   │  Alchemy / sanect RPC    │
                   │  (read events + state)   │
                   └─────────────┬────────────┘
                                 │
                                 ▼
┌────────────────────────────────────────────────┐
│  airdrop-indexer (Railway)                     │
│                                                │
│  ┌──────────┐    ┌────────────┐   ┌─────────┐  │
│  │ Event    │───▶│ Per-phase  │──▶│ Daily   │  │
│  │ scanner  │    │ points     │   │ held-   │  │
│  │ (13      │    │ aggregator │   │ balance │  │
│  │ products)│    │            │   │ sweep   │  │
│  └──────────┘    └─────┬──────┘   └────┬────┘  │
│                        │               │       │
│                        ▼               ▼       │
│              ┌──────────────────────┐          │
│              │  Postgres (wallet_   │          │
│              │   phase_points,      │          │
│              │   held_balances)     │          │
│              └──────────┬───────────┘          │
│                         │                      │
│              ┌──────────▼───────────┐          │
│              │  HTTP API            │          │
│              │  /api/points/:addr   │          │
│              │  /api/leaderboard    │          │
│              └──────────────────────┘          │
└────────────────────────────────────────────────┘
                         │
                         ▼
                  airdrop-app calls
                  /api/points/:addr per
                  connected wallet

Step-by-step Railway setup ​

1. Create the service ​

  1. Railway → New Service → "Deploy from GitHub repo"
  2. Repo: irealrahul/privacy-layer1
  3. Root Directory: airdrop-indexer (so Railway only builds this subdir)
  4. Service name: airdrop-indexer

2. Attach Postgres plugin ​

  1. Click "+ New" → "Database" → Postgres
  2. Plugin name: airdrop-postgres
  3. Wait for it to provision (~30s)
  4. The plugin automatically exposes DATABASE_URL to the airdrop-indexer service

3. Set environment variables ​

In airdrop-indexer service → Variables:

# Sanect chain access — Railway internal IPv6 saves bandwidth
SANECT_RPC_URL=http://snapshot-publisher.railway.internal:8080
SANECT_CHAIN_ID=7628
SANECT_START_BLOCK=1

# Ethereum archive (reuse the Alchemy key from airdrop-app)
ETH_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/<YOUR_KEY>
SNAPSHOT_CUTOFF_TS=2026-06-21T23:59:59Z

# Contract addresses (from CLAUDE.md mainnet table)
DEX_FACTORY_ADDRESS=0xDF03522CEc5bd2a609863De38da2eEf1267EE0c8
DEX_ROUTER_ADDRESS=0xBf08497825CFcF04037efD852C9bBd0C9C64d175
WSNCT_ADDRESS=0x4f35BD61FEd919aA9933C6333e388b3B7E1108Dc
MASTERCHEF_ADDRESS=0xa8264E2c8EC09b0251A789860388f70e0Ef563A7
SHIELDED_POOL_ADDRESS=0x5532Dc2B743D45Fd0121E382696521ED62DD3ecf
BRIDGE_ADAPTER_ADDRESS=0x4BAfd466B805471065a3F909E6b7acE28Dc68D75
SANECT_REGISTRY_ADDRESS=0xdC7bB6f4d1c217bd5729a719AFf55de4Cf3EaE23
BASE_REGISTRAR_ADDRESS=0xd45357B67C82311eD3e36DE279599CD84CE15AcA
SEALED_BID_AUCTION_ADDRESS=0xdAD7EEF74725F0ee71Ef8851A37329D33764Bac0
MARKETPLACE_ADDRESS=0x0b6E3C4bF83097636294c076443feF7b6AC7a34D
REVERSE_REGISTRAR_ADDRESS=0x6A5e80493281237f678F5f5c64Db42572c20458c

4. Initialize the database schema ​

After first deploy succeeds:

bash
# Via Railway CLI from your laptop
railway run --service airdrop-indexer "psql \$DATABASE_URL < sql/schema.sql"

Note: the schema in the current codebase is OLD-design. When the rewrite lands it will be re-run with a new schema; data loss is acceptable since the indexer can re-scan from block 1.

5. Configure the custom domain ​

  1. Service settings → Networking → "Generate domain" or "Custom domain"
  2. Add CNAME at Cloudflare (DNS-only, grey cloud): airdrop-api.sanect.com → <service>.up.railway.app
  3. Wait for cert provisioning (~1 min)

6. Wire the airdrop-app to the indexer ​

Once the indexer responds at https://airdrop-api.sanect.com/api/points/<addr>:

  1. Set on airdrop-app Railway service: INDEXER_URL=https://airdrop-api.sanect.com
  2. Redeploy airdrop-app — /api/points/[address] proxies the real data
  3. /dashboard shows real points; mock generator stays as a fallback when INDEXER_URL is empty

Operator runbook (during Phase 1 prep) ​

DayAction
Day 7Indexer-v2 spec finalized; rewrite begins
Day 21Service deployed, indexing from block 1
Day 30First sanity check — top 100 wallets reasonable?
Day 90Phase 1 indexing cutoff approaches
Day 173Operator runs CSV export from indexer Postgres → upload to IPFS + airdrop.sanect.com/snapshots/phase1.csv
Day 174-179Public appeal window
Day 179Operator batch-loads final allocations into AirdropDistributor via setAllocations() calls (~500/tx)
Day 180Phase 1 claim opens

Cost estimate ​

ComponentFree tierPaid (after limits)
Railway service$5/mo Proincluded
Postgres pluginincluded up to 1GB$10/mo for 5GB
Bandwidth100GB/mo free$0.10/GB
Total monthly~$5-15~$30-50 at scale

Rewrite scope (engineering reference) ​

When the indexer rewrite happens, the changes needed are in docs/airdrop-points-system-design.md. Key files:

  • src/sanect/products.ts — rewrite qualifies() to return points-per-event instead of yes/no
  • sql/schema.sql — add wallet_phase_points (address, phase, product_id, points, last_indexed_block) table; replace the binary sanect_engagement table
  • src/sanect/indexer.ts — add per-product event handlers that compute USD-value × time × caps
  • New file src/sanect/sweepBalances.ts — daily cron for held-balance snapshots (items #2, #3, #7, #9, #10)
  • src/api.ts — new endpoints /api/points/:address?phase=N, /api/leaderboard?phase=N
  • src/merkle.ts — DELETE (we use registered-claim now, not Merkle)

Build effort: 2-3 focused weeks. External audit not required (off-chain data; operator trust model is fine for indexer logic).