Skip to content
Syntropy

Syntropy

A liquidity layer between cash and the blockchain.

Created on 8th March 2026

β€’

Syntropy

Syntropy

A liquidity layer between cash and the blockchain.

The problem Syntropy solves

Imagine this.

You just landed in a small town for a family wedding. Your phone has β‚Ή50,000 in UPI. But the local flower vendor only takes cash. The nearest ATM? 12 kilometers away, and it's been "out of service" for three days. The bank branch closes in 20 minutes and has a line out the door. Your digital wallet is full, but your hands are empty.

This isn't a rare edge case. This is daily life for millions.

  • 🏧 ATM deserts β€” India shut down 30,000+ ATMs between 2020–2024. Rural areas lost them the fastest
  • πŸ”Œ Network failures β€” UPI outages, server downtime, bank maintenance windows β€” when digital rails go down, there's no backup
  • πŸ’Έ Cash-dependent economies β€” street vendors, auto drivers, local markets, temple donations β€” cash is still the default
  • 🌍 Not just India β€” emerging markets across Southeast Asia, Africa, and Latin America face the same liquidity gap

The root cause isn't technology failure. It's infrastructure centralization. Every cash access point β€” ATMs, bank branches, payment kiosks β€” is owned by a single entity that decides where to deploy, when to refill, and who gets access.

What if cash access wasn't controlled by institutions at all?

What if the shop next door, the chai stall at the corner, the pharmacy down the street could become your ATM β€” converting your digital money to cash on the spot, trustlessly, with no middleman?

That's SYNTROPY.

Challenges we ran into

1. MetaMask Transactions Failing Silently on Polygon Amoy

The first time I tried creating an on-chain escrow from the frontend, MetaMask would pop up, I'd confirm, and then… nothing. No error, no revert, just a hung transaction. Turns out Polygon Amoy's RPC endpoints don't support

eth_maxPriorityFeePerGas

, which ethers.js v6 calls by default for EIP-1559 transactions. MetaMask would silently fail trying to estimate gas.

Fix: I forced legacy (Type 0) transactions by manually setting

gasPrice

instead of

maxFeePerGas

+

maxPriorityFeePerGas

. I also added a 30 gwei floor to prevent underpriced transactions. This one took hours to debug because the error surfaced deep inside MetaMask's internal RPC layer, not in my code.

2. Leaflet + Next.js SSR Crash

Leaflet requires

window

and

document

to exist β€” but Next.js renders on the server first where neither exists. Every time I imported Leaflet normally, the build would crash with

ReferenceError: window is not defined

.

Fix: I wrapped every react-leaflet component (

MapContainer

,

TileLayer

,

Marker

,

Circle

,

Popup

) in

next/dynamic

with

{ ssr: false }

. For custom icons, I added

typeof window === "undefined"

guards and lazy-required Leaflet inside those functions. It's not the cleanest pattern, but it works reliably.

3. Wallet Connection Flow β€” Race Conditions

The auth flow involves 4 sequential async steps: connect MetaMask β†’ fetch nonce from backend β†’

personal_sign

the nonce β†’ POST signature to verify. During testing, I kept hitting a bug where the nonce stored in the DB didn't match the one being signed. The issue was that rapid button clicks would fire multiple nonce requests, and the second one would overwrite the first in the database before the first signature was verified.

Fix: I added an upsert (

ON CONFLICT DO UPDATE

) for wallet sessions so the latest nonce always wins, and restructured the frontend

login()

function to be a single sequential

useCallback

chain that can't be double-invoked.

4. Database Unreachable During Demo

During a live demo run, Supabase had a brief connectivity hiccup and the entire node discovery flow broke β€” the map showed zero nodes. For a hackathon demo, that's a death sentence.

Fix: I added a

MOCK_NODES

fallback array with 5 seed nodes around Kolkata. When the PostgreSQL query fails, the backend gracefully degrades to serving mock data with a

source: "mock"

flag so the frontend still works. Same thing for

getAllNodes

β€” if the DB is down, mock data keeps the demo alive.

5. Environment Variables Silently Missing in Production

After deploying the frontend to Vercel, every single API call returned a network error. The app looked perfect but was completely non-functional. Spent 30 minutes before I realized

NEXT_PUBLIC_BACKEND_URL

wasn't set in Vercel's env config, so all fetch calls were hitting

localhost:5000

on Vercel's servers.

Fix: I added runtime warnings that fire in the browser console when

NEXT_PUBLIC_BACKEND_URL

or

NEXT_PUBLIC_ROUTING_ENGINE_URL

aren't set and the hostname isn't

localhost

. It's a small thing, but it would've saved me a lot of time.

6. Solidity Contract Size Limit

The

Escrow.sol

contract kept hitting the 24KB Spurious Dragon bytecode limit when I tried combining escrow + reputation + insurance logic into one contract. The deployment would just fail with a cryptic "exceeds contract size limit" error.

Fix: I split the system into 4 separate contracts (

Escrow

,

NodeRegistry

,

InsurancePool

,

Reputation

) with cross-contract references. I also set Hardhat's optimizer to

runs: 1

(minimize bytecode at the cost of slightly higher gas per call) β€” a worthwhile tradeoff since deployment cost on a testnet matters more than per-call savings.

Tracks Applied (1)

Web3

SYNTROPY isn't a Web2 app with a token bolted on. Every on-chain component exists because removing the trusted middleman...Read More

Discussion

Builders also viewed

See more projects on Devfolio