Skip to main content
Learn how to build a ranked leaderboard of the best tracked wallets, sorted by PnL, win rate, trades or volume, across any chain. One call gets the data, then you sort and search it on the client so the UI feels instant. This guide walks it end to end in TypeScript, with no CSS.

CabalSpy/Leaderboard-Demo

The full styled demo is open source. This guide rebuilds its core logic from scratch.

What you will build

A leaderboard that loads the ranked wallets for a chain, wallet type and period, then lets the user re sort and search without another request. The key idea, fetch once, sort and filter in memory.

The endpoint

One call returns the whole ranked list.

GET /v1/wallet/leaderboard

Ranked wallets for a chain, wallet type and period, each with a profile, period stats and win rate.

Step 1, type the row

The response nests data under profile, period_stats and a win rate object. It is easier to work with if you flatten each row into one flat object as it comes in.
src/types.ts

Step 2, fetch and flatten

Give it a chain, a wallet type and a period. Flatten each raw row into the clean Entry shape as you map over the list, so the rest of the app never touches the nested response.
src/leaderboard.ts

Step 3, sort and search in memory

This is what makes the leaderboard feel fast. Once you have the rows, sorting and searching never hit the network, they run on the array you already have.
src/view.ts

Step 4, render the ranked table

Rendering is a map over the sorted rows. The rank is just the index plus one.
src/render.ts

Step 5, wire it together

Hold the loaded rows and the current view options in a little state object. Loading a new chain, type or period refetches. Changing the sort or the search only re renders, no request.
src/main.ts

Wallet types per chain

Not every chain has every wallet type, so match your type control to the chain. Solana has KOL, Smart Money and Whale. BNB and Base have KOL and Smart Money. ETH has KOL only. Passing a type a chain does not support returns an error, so hide the ones that do not apply.
src/chain-types.ts

Lessons worth knowing

Two things make this clean. Flatten on the way in. The response nests fields under profile and period_stats. Flatten each row once in the fetch, and every other function works with a simple flat object. Fetch once, view many times. Sorting and search run on the array you already loaded, so they are instant. Only a new chain, type or period needs another request. This is the difference between a snappy leaderboard and one that reloads on every click.

Reference

GET /v1/wallet/leaderboard

Ranked wallets for a chain, wallet type and period.