TL;DR: Token addresses are forgettable. Community support shouldn't be. I built Bags.fm Embeds — a free tool that turns wallet addresses into real-time badges showing lifetime earnings. It took two weekends and taught me a lot about Go concurrency.
The Problem
If you've ever shared a Solana token address, you know the drill:
9XzKDJ9wP9yqi9G5okp9UFNxFuhqyk5GNyUnnBaRBAGS
That's it. That's the tweet.
A 44-character string that tells you nothing about:
- The thousands of dollars in fees this token generated
- The creators being supported by the community
- The active, growing ecosystem around this project
The address is static, but the community is alive.
The Inspiration
GitHub badges:


They work because they make boring metadata visual and shareable.
What if crypto earnings had the same treatment? A badge that shows:
- Total lifetime fees earned
- Who's receiving those fees
- Real-time updates as the community grows
What I Built
5 embed types:
| Type | Use Case |
|---|---|
| Stats Card Widget | Full stats for websites |
| Fee Badge (Recipient) | Creator earnings display |
| Fee Badge (Project) | Total project fees |
| Mini Badge (Recipient) | Compact for GitHub/Twitter |
| Mini Badge (Project) | Compact for project pages |
Key insight: SVG badges work everywhere — GitHub, Notion, Twitter cards, Discord. No JavaScript required. Just an image URL.
See live examples here.
The Technical Stuff
Why Go?
| Aspect | Next.js + Vercel | Go + Railway |
|---|---|---|
| Cold starts | Yes | No |
| Memory | ~100MB | ~5-10MB |
| Concurrency | Good | Excellent |
| Monthly cost | $20+ | $5-20 |
For a service serving SVG badges in <50ms, Go was the clear winner.
The Background Worker Pattern
The badges show USD values → need current SOL price → can't fetch on every request.
Solution: A background goroutine that fetches SOL price every 15 seconds:
func (p *PriceService) StartWorker(ctx context.Context) {
ticker := time.NewTicker(15 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
p.fetchAndCachePrice()
}
}
}
The architecture:
HTTP Server (main goroutine)
↓ reads from
Redis (shared state)
↑ writes to
Background Worker (price goroutine)
If Jupiter's API goes down? HTTP requests keep serving cached data. The worker tries Binance, then CoinGecko. Users never notice.
5 Layers of Caching
- Browser (60s) — Repeat visitors don't hit server
- Cloudflare CDN (60s) — Global edge caching
- Redis — Different TTLs per data type
- Fallback cache — Last known values
- Hardcoded defaults — When everything fails
Result: 99% of requests served from cache in <50ms.
SVG Generation
func generateBadge(data TokenData, theme string) string {
bgColor := "#1a1a1a"
textColor := "#ffffff"
if theme == "light" {
bgColor, textColor = "#ffffff", "#1a1a1a"
}
return fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg"
width="320" height="52">
<rect fill="%s" rx="8" width="320" height="52"/>
<text fill="%s" x="16" y="32">
Lifetime Fees: $%s
</text>
</svg>`, bgColor, textColor, formatCurrency(data.FeesUSD))
}
Valid SVG that works as an <img> tag anywhere.
The Growth Flywheel
Projects share embeds
→ Visibility for platform
→ New users discover it
→ More trades, more fees
→ Creators earn more
→ More projects join
→ More embeds
Every badge is passive marketing.
Lessons Learned
1. Solve the Visibility Problem
The biggest issue wasn't technical — impact was invisible. Badges make the intangible tangible.
2. Meet People Where They Are
SVG badges work in GitHub, Twitter, Notion, Discord, and websites. One format, everywhere.
3. Design for Failure
External APIs will go down. Fallback sources + cached data + graceful degradation = users never notice.
4. Concurrency Simplifies Architecture
Background worker pattern eliminated race conditions, timeouts, and request blocking.
5. Free Tools Win
Free tools get adopted faster. The goal is ecosystem growth, not revenue.
Try It
Live: bags.sivaramp.com
Examples: bags.sivaramp.com/examples
Steps:
- Paste token address (must end in BAGS)
- Pick embed type
- Copy code
- Paste anywhere
Tech Stack
Backend: Go 1.21+ / Gin
Frontend: React / Vite / TypeScript
Cache: Redis
CDN: Cloudflare
Hosting: Railway
Data: Bags SDK, DexScreener, Jupiter
What do you think? I'd love to hear if you've built similar tools for making data visible. Drop a comment below!
If you're building in the Solana ecosystem, try out the embeds and let me know how you use them.





Top comments (0)