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.tsregistry 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 walletStep-by-step Railway setup
1. Create the service
- Railway → New Service → "Deploy from GitHub repo"
- Repo:
irealrahul/privacy-layer1 - Root Directory:
airdrop-indexer(so Railway only builds this subdir) - Service name:
airdrop-indexer
2. Attach Postgres plugin
- Click "+ New" → "Database" → Postgres
- Plugin name:
airdrop-postgres - Wait for it to provision (~30s)
- The plugin automatically exposes
DATABASE_URLto 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=0x6A5e80493281237f678F5f5c64Db42572c20458c4. Initialize the database schema
After first deploy succeeds:
# 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
- Service settings → Networking → "Generate domain" or "Custom domain"
- Add CNAME at Cloudflare (DNS-only, grey cloud):
airdrop-api.sanect.com→<service>.up.railway.app - 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>:
- Set on airdrop-app Railway service:
INDEXER_URL=https://airdrop-api.sanect.com - Redeploy airdrop-app —
/api/points/[address]proxies the real data /dashboardshows real points; mock generator stays as a fallback whenINDEXER_URLis empty
Operator runbook (during Phase 1 prep)
| Day | Action |
|---|---|
| Day 7 | Indexer-v2 spec finalized; rewrite begins |
| Day 21 | Service deployed, indexing from block 1 |
| Day 30 | First sanity check — top 100 wallets reasonable? |
| Day 90 | Phase 1 indexing cutoff approaches |
| Day 173 | Operator runs CSV export from indexer Postgres → upload to IPFS + airdrop.sanect.com/snapshots/phase1.csv |
| Day 174-179 | Public appeal window |
| Day 179 | Operator batch-loads final allocations into AirdropDistributor via setAllocations() calls (~500/tx) |
| Day 180 | Phase 1 claim opens |
Cost estimate
| Component | Free tier | Paid (after limits) |
|---|---|---|
| Railway service | $5/mo Pro | included |
| Postgres plugin | included up to 1GB | $10/mo for 5GB |
| Bandwidth | 100GB/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— rewritequalifies()to return points-per-event instead of yes/nosql/schema.sql— addwallet_phase_points (address, phase, product_id, points, last_indexed_block)table; replace the binarysanect_engagementtablesrc/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=Nsrc/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).