MadeOnSolMade on Sol
Pricing
Try freeGet real-time feedsSign in
MadeOnSolMade on Sol

Solana and Robinhood Chain intelligence — KOL wallet tracking, deployer intelligence, all-DEX trade streams, and a developer API. Discover, compare, and build.

Product

  • Solana Data API
  • Robinhood Chain API
  • Robinhood Chain x402
  • MCP Servers & SDKs
  • x402 for AI Agents
  • Pricing
  • Enterprise / For Businesses

Solutions

  • Trading Bots
  • Wallet Tracking
  • Copy Trading
  • Token Scanners
  • Trading Terminals
  • DEX Data
  • Smart Money
  • Token Risk
  • AI Agents
  • View all solutions

Developers

  • API Docs
  • WebSocket Streaming
  • Changelog
  • API Status
  • Latency Benchmarks
  • Data Integrity
  • Data Dictionary
  • Methodology

Resources

  • All Tools
  • Compare Tools
  • Best Trading Bots
  • Best DEXs
  • Best Wallets
  • Best Analytics
  • Best DeFi
  • Best Snipers
  • Blog
  • Blog Archive
  • Signal Scorecard
  • Tool Uptime
  • Community
  • Submit a Tool
  • Advertise

Company

  • About
  • Contact
  • Security
  • Privacy
  • Terms
  • DPA
  • Disclaimer

© 2026 MadeOnSol

MadeOnSol — eenmanszaak, Hulshout, Belgium · KBO/BTW BE 1039.535.538 (art. 56bis, no VAT charged)

Runs on our own dedicated EU servers — self-hosted data stack, own Robinhood Chain node · security

Follow us on XPowered by:constant·k — Private Solana RPC Services
SolutionsPortfolio Trackers

Solutions · Portfolio trackers

Wallet activity and context for Solana portfolio apps

MadeOnSol is the wallet activity layer for Solana portfolio apps: swaps and SOL transfers of watched wallets as structured events, on-chain holdings with the balance change no trade explains, cost-basis PnL on the launchpad trades it captures, and KOL, bot and deployer labels for counterparties and tokens, while valuation, accounting and the interface stay in your app.

Try it on a wallet Read the wallet API
Solana KOL wallets trackedActive in the live KOL feed · copy-trade rules follow these
1,142
Alpha wallets scoredSolana early buyers · bot-confidence ranked
1.70M
Deployers profiledElite → cold · of 1.2M+ indexed
98K
Counterparties labelled per batch call
100

An address and a balance do not tell a user what happened. Turn each transaction into a row they can read: what was bought or sold, how much SOL moved, and whether the token or the wallet on the other side carries a label worth a second look.

Free API key · plans from €43

A Free key reads GET /token/{mint}. The Wallet Tracker, its webhook, positions, PnL and counterparty labels from Pro; holdings and the wallet_tracker:events channel on Ultra. Showing the data to your own users needs Business.

What one activity row needs

Four questions a user asks about a transaction, and the call that answers each.

  • What happened

    wallet_tracker:eventPro

    event_typeactionsol_amounttoken_amount

  • Which token

    GET /token/{mint}Free

    namesymbolprice_usddeployer.tier

  • Who was on the other side

    POST /wallet/batch/classifyPro

    is_kolkol_nameis_sniperbot_confidence

  • Is the balance explained

    GET /wallet/{address}/holdingsUltra

    amounttrade_derived_amounttransfer_delta

The problem

A portfolio screen has to explain activity, not list it. That means parsing every venue's swaps into a side and an amount, telling a trade from a transfer, keeping rows in chain order when a stream reconnects, spotting tokens that arrived without a trade, and knowing which counterparties and tokens deserve a warning. Each of those is its own indexer before the first row renders.

Show me

One wallet, two transactions, two activity rows

The events are what MadeOnSol delivers; the context calls fill in what the event does not carry; the last step is your code. Field names are the real keys, values are illustrative and the bodies are abridged.

  1. 1 · Two events arrive

    A buy and a SOL transfer on a watched wallet. The swap carries market cap change and volume for its token; the transfer names the other side but not the direction.

    wallet_tracker:events frames (abridged)
    [
      {
        "id": "wallet_tracker:event:4vJ9...Qm2x",
        "seq": 512034,
        "channel": "wallet_tracker:events",
        "event": "wallet_tracker:event",
        "data": {
          "subscriber": "3f1c...9a07",
          "wallet_address": "7xKX...3bPq",
          "label": "Main wallet",
          "event_type": "swap",
          "action": "buy",
          "token_mint": "8vdc...pump",
          "sol_amount": 1.25,
          "token_amount": 482113.52,
          "counterparty": null,
          "tx_signature": "4vJ9...Qm2x",
          "block_time": 1790330520,
          "slot": 449812044,
          "replayed": false,
          "ts": "2026-09-25T10:02:00.000Z",
          "mc_change_pct": { "5m": 12.4, "15m": 30.1 },
          "volume_usd": { "5m": 8420.5, "15m": 21980 },
          "mev_volume_pct": { "5m": 4.2, "15m": 3.8 },
          "history_age_seconds": 1140
        },
        "ts": 1790330520000
      },
      {
        "id": "wallet_tracker:event:2mLq...Xw9e",
        "seq": 512041,
        "channel": "wallet_tracker:events",
        "event": "wallet_tracker:event",
        "data": {
          "subscriber": "3f1c...9a07",
          "wallet_address": "7xKX...3bPq",
          "label": "Main wallet",
          "event_type": "transfer",
          "action": null,
          "token_mint": null,
          "sol_amount": 12,
          "token_amount": null,
          "counterparty": "9WzD...AWWM",
          "tx_signature": "2mLq...Xw9e",
          "block_time": 1790330581,
          "slot": 449812197,
          "replayed": false,
          "ts": "2026-09-25T10:03:01.000Z"
        },
        "ts": 1790330581000
      }
    ]
  2. 2 · Label the other side

    One batch call labels the transfer's counterparty, and up to 100 more in the same request.

    POST /api/v1/wallet/batch/classify
    POST /api/v1/wallet/batch/classify
    Authorization: Bearer msk_...
    Content-Type: application/json
    
    { "wallets": ["9WzD...AWWM"] }
    
    // 200
    {
      "wallets": [
        {
          "address": "9WzD...AWWM",
          "is_sniper": true,
          "is_bundler": false,
          "is_dumper": false,
          "is_kol": false,
          "kol_name": null,
          "bot_confidence": "high",
          "dump_cluster": null
        }
      ],
      "count": 1,
      "as_of": "2026-09-25T10:03:02.410Z"
    }
  3. 3 · Name the token

    The event has the mint only. GET /token/{mint} (Free) adds the name, symbol, price and the deployer's tier.

    GET /api/v1/token/{mint} (abridged)
    // GET /api/v1/token/8vdc...pump
    {
      "token": {
        "mint": "8vdc...pump",
        "name": "New Token",
        "symbol": "NEW",
        "image_url": "https://.../new.png",
        "price_usd": 0.0000912,
        "market_cap": 91200,
        "liquidity_usd": 18400,
        "price_is_stale": false,
        "is_blacklisted": false,
        "deployer": {
          "wallet": "8kP4...Qe2x",
          "tier": "good",
          "bonding_rate": 0.41,
          "total_deployed": 34,
          "total_bonded": 14
        }
      },
      "as_of": "2026-09-25T10:02:01.120Z"
    }
  4. 4 · Build the rows

    Your code: one row per wallet per transaction, sorted by slot, recovered events marked, labels as badges.

    Your code: event to activity row
    import type { WalletTrackerTrade } from "madeonsol-x402";
    
    type Token = { symbol: string | null; deployer: { tier: string } | null };
    type Party = { is_kol: boolean; kol_name: string | null; is_sniper: boolean; bot_confidence: string | null };
    
    function toActivityRow(e: WalletTrackerTrade, token?: Token, party?: Party) {
      const badges: string[] = [];
      if (token?.deployer) badges.push(`deployer: ${token.deployer.tier}`);
      if (party?.is_kol) badges.push(`KOL: ${party.kol_name}`);
      if (party?.is_sniper) badges.push("sniper wallet");
      return {
        key: `${e.tx_signature}:${e.wallet_address}`, // frame id is tx_signature only
        order: e.slot,                                  // chain order, not arrival
        recovered: e.replayed,                          // mark, never re-notify
        title: e.event_type === "swap"
          ? `${e.action === "buy" ? "Bought" : "Sold"} ${token?.symbol ?? "a token"}`
          : "SOL transfer",
        detail: e.event_type === "swap"
          ? `${e.sol_amount} SOL`
          : `${e.sol_amount} SOL with ${e.counterparty ?? "unknown"}`,
        badges,
      };
    }
    
    // → { key: "4vJ9...Qm2x:7xKX...3bPq", title: "Bought NEW", detail: "1.25 SOL", badges: ["deployer: good"] }
    // → { key: "2mLq...Xw9e:7xKX...3bPq", title: "SOL transfer", detail: "12 SOL with 9WzD...AWWM", badges: ["sniper wallet"] }

one row per wallet per transaction

The tracker stores one event per transaction, watchlist and wallet, but the WebSocket frame id is keyed on tx_signature alone. A transfer between two of a user's own watched wallets is two events with the same id, so dedupe rows on tx_signature and wallet_address together, never on the frame id.

replayed

A row recovered after a reconnect arrives with replayed: true and its original slot. Show it in its place in the list; do not send a new notification for it.

Same calls in other clients

  • TypeScriptrest.walletTrackerTrades({ order: "slot" })
  • TypeScriptrest.walletClassify(counterparties)
  • TypeScriptrest.walletHoldings(address)
  • Pythonclient.wallet_tracker_trades()
  • Pythonclient.wallet_holdings(address)
  • MCPmadeonsol_wallet_holdings

Try it

Scan a wallet before you design the screen

The free wallet scanner shows PnL, the KOL, alpha-wallet and deployer labels, and the latest trades for any Solana address, with no key. Then call the same data from your backend.

Scan a wallet Get a free API key Wallet API reference

From wallet event to portfolio activity

What happens between a transaction and a row your user reads

The first four stages run on MadeOnSol and return named fields; the last two are your product. Context is optional: a row works with the event alone, and gets its token name and labels from two extra calls.

  1. 01 · MadeOnSol

    Your user adds a wallet

    Your app puts the address on its watchlist with the user's own label. Tracking starts at that moment; earlier transactions are not backfilled into the watchlist.

    POST /wallet-tracker/watchlist

    wallet_addresslabel
  2. 02 · MadeOnSol

    The Wallet Tracker parses the transaction

    A token balance change with a SOL leg becomes a swap with a side; a USDC or USDT leg is converted to its SOL equivalent. A SOL movement of at least 0.001 SOL with no token change becomes a transfer with the counterparty it could match.

    event_typeactiontoken_mintsol_amountcounterparty
  3. 03 · MadeOnSol

    The event reaches your backend

    Poll the stored rows, receive each event as a signed webhook, or hold the stream open. The live event also carries the token's recent market cap change and volume when MadeOnSol prices that mint.

    GET /wallet-tracker/tradeswallet_tracker:event webhookwallet_tracker:events channel

    slotreplayedmc_change_pctvolume_usd
  4. 04 · MadeOnSol

    Context for the token and the other side

    One call names the token and its deployer tier; one batch call labels up to 100 counterparties as KOL, sniper, bundler or dumper, with a bot-confidence rating. Neither is on the event itself.

    GET /token/{mint}POST /wallet/batch/classify

    symboldeployer.tieris_kolbot_confidence
  5. 05 · Your app

    An activity row

    Your code turns the event and its context into a sentence, a key and badges: one row per wallet per transaction, sorted by slot, with recovered history marked instead of announced.

    your row keyyour wordingyour badges
  6. 06 · Your app

    The portfolio screen

    Grouping, valuation in the user's currency, notifications and the layout are your product. MadeOnSol supplies the facts behind each row.

    your valuationyour UI

Chips are the response or event fields each stage returns. Wallet Tracker API · Wallet API

The event, field by field

What the event tells you, and what it leaves to you

The stored row (REST) and the live event (webhook and WebSocket) are the same event with two differences: the live one adds the token's recent market data, and only the stored row has the unused name columns.

FieldREST rowLive eventWhat it means for a row
event_type · actionyesyesswap with buy or sell, or transfer with action null.
sol_amountyesyesThe wallet's SOL change, fees included. On a transfer it is the absolute amount: the event does not say whether SOL came in or went out.
counterpartytransfers onlytransfers, else nullThe account whose SOL change matched this one. Absent from the REST row on swaps.
slot · replayedyesyesChain position, and whether the event came through recovery after a reconnect.
mc_change_pct · volume_usd · mev_volume_pctnowhen the mint is pricedMarket cap change and organic volume per window at the moment of the event.
token name and symbolnot filled (always null)noNot available on the event. token_symbol and token_name appear on the stored row but are never written; read the name and symbol from GET /token/{mint}.
program or venuenonoThe event does not name the DEX or program the swap went through.
KOL, alpha or bot labelsnonoLabels are a separate lookup on the wallet or counterparty address.

Balances and results

Holdings, positions and PnL measure different things

Holdings come from the chain; positions and PnL come from the trades MadeOnSol captured. Where they disagree, that is information: a token the wallet holds but never bought through a captured trade shows up as transfer_delta.

Holdings

Ultra
GET /wallet/{address}/holdings

Current balances read from the chain (SPL and Token-2022 accounts plus native SOL), priced from MadeOnSol's token index, with the part of each balance no captured trade explains.

total_value_usd adds up the priced tokens only; sol_balance stays in SOL. Cached 90 seconds, 6 uncached lookups per minute.

sol_balancevalue_usdtransfer_deltasummary.priced

Positions

Pro
GET /wallet/{address}/positions

Open lots rebuilt first-in first-out from the wallet's captured trades, with cost basis, average entry and unrealized result in SOL.

Trade-derived, so a token received by transfer is not a position. Launchpad trades from the last 90 days.

cost_basis_solavg_entry_price_solunrealized_sol

PnL

Pro
GET /wallet/{address}/pnl

Realized and unrealized result in SOL, win rate, profit factor, hold times, drawdown, a daily PnL curve and every closed position.

cost_basis_observable_from says where the data starts; a sell whose buy predates it is left out rather than guessed.

realized_solunrealized_solpnl_curvecost_basis_observable_from

A holdings response, read for a portfolio

NEW was bought through a captured trade, so its delta is 0. DROP's captured trades net to 0 while the wallet holds 300,000, so that balance arrived by transfer or through a swap outside the captured pipeline. The third token has no MadeOnSol price, so it is left out of total_value_usd, and so is the SOL balance.

GET /api/v1/wallet/{address}/holdings (abridged)
// GET /api/v1/wallet/7xKX...3bPq/holdings
{
  "address": "7xKX...3bPq",
  "sol_balance": 3.412,
  "holdings": [
    {
      "mint": "8vdc...pump",
      "symbol": "NEW",
      "name": "New Token",
      "amount": 482113.52,
      "price_usd": 0.0000912,
      "value_usd": 43.97,
      "is_bonded": false,
      "trade_derived_amount": 482113.52,
      "transfer_delta": 0
    },
    {
      "mint": "Hx3r...pump",
      "symbol": "DROP",
      "name": "Drop",
      "amount": 300000,
      "price_usd": 0.0000042,
      "value_usd": 1.26,
      "is_bonded": true,
      "trade_derived_amount": 0,
      "transfer_delta": 300000
    },
    {
      "mint": "Zq81...xYt4",
      "symbol": null,
      "name": null,
      "amount": 25,
      "price_usd": null,
      "value_usd": null,
      "is_bonded": null,
      "trade_derived_amount": null,
      "transfer_delta": null
    }
  ],
  "summary": { "token_accounts": 4, "non_zero": 3, "returned": 3, "priced": 2, "total_value_usd": 45.23, "truncated": false },
  "verified_at": "2026-09-25T10:05:12.000Z",
  "trade_window_days": 90,
  "cache_hit": false,
  "ttl_seconds": 90
}

Boundaries

What MadeOnSol does not do

MadeOnSol provides wallet and onchain activity and the intelligence around it; valuation and accounting stay with your app, except for two verified capabilities: holdings value_usd for tokens in MadeOnSol's index only, and FIFO cost-basis PnL in SOL over the launchpad trades captured in the last 90 days. A complete history of every transaction also stays with your app.

TopicWhat MadeOnSol providesWhat stays with your app
Total portfolio valuePrices the memecoin and launchpad tokens it indexes. Holdings add those up; SOL is returned as a SOL balance, and stablecoins, wrapped SOL and other assets outside the index come back unpriced.Your own price source for SOL, stablecoins and major assets, and the currency conversion.
Accounting and taxFIFO cost basis in SOL over the launchpad trades captured in the last 90 days. No fiat cost basis, tax lots, fee breakdown or reports.Cost basis in the user's currency, lot rules, fees and anything a tax report needs.
Every transaction a wallet madeThe watchlist starts when a wallet is added and keeps events 120 days. Trade history covers the launchpad pipeline from 2026-04-12. Token transfers without a SOL or stablecoin leg, stablecoin transfers and SOL moves under 0.001 SOL are not events.Your own store of activity beyond the retention window, and your RPC provider for transfer direction and anything outside the pipeline.
Funding history of your users' walletsFunding evidence is collected for MadeOnSol's public tracked set: active KOL wallets and elite or good deployers. Customer watchlists are deliberately outside it.Where a user's money came from, if your product needs it.
Custody and executionReads data. It never holds keys, signs or sends transactions.Wallet connection, signing and anything that moves funds.

Live monitoring and plans

What to poll, what to push, and where each starts

Wallet endpoints start on Pro; holdings and the live wallet_tracker:events channel on Ultra. A user-facing app also needs the display licence noted under the build list.

Your app needsUsePlan
Activity feed for watched walletsGET /wallet-tracker/tradesSlot order by default; page with before_slot.Pro
History for a wallet a user just addedGET /wallet/{address}/tradesCaptured launchpad trades, last 90 days by default; older months come from the archive.Pro
A notification when a watched wallet moveswallet_tracker:event webhookDelivered only to the account whose watchlist produced it; min_sol and action filters apply.Pro
Live rows while the app is openwallet_tracker:events channelSubscriber-scoped on /ws/v1/stream.Ultra
Live value of the tokens a user holdstoken:prices channelMint-scoped; mints per connection: Pro 25 · Ultra 100 · Business 250.Pro
Price-drop alerts on held tokensPOST /price-alertsdrop_pct and an optional recovery_pct per token; delivered by webhook or the price_alert:events channel.Pro
Current balancesGET /wallet/{address}/holdingsRead from chain, cached 90 seconds.Ultra
More wallets than one watchlist holdsPOST /wallet/batch/tradesUp to 50 wallets per call; feed next_since back as since to poll only new trades. Watchlist sizes: Pro 50 · Ultra 100 · Business 500.Pro

Streaming reference · Price alerts · Compare plans

Compared

Portfolio trackers and wallet tracking

Both start from the same events. Wallet Tracking covers watching addresses and delivering their events to your backend; this page covers what a user sees once those events reach your app. The data layer behind both is described in the data dictionary.

AspectWallet TrackingPortfolio Trackers
Starts fromA list of addresses your backend watches.The wallets a user connects, and what they expect to see about them.
Main outputSwap and transfer events delivered to your system.Readable activity rows, balances and results, with labels for the tokens and the other side.
The question it answersWhat did these wallets just do?What happened in my wallet, and should I look twice at any of it?
MadeOnSol surfacesWatchlist, the events feed, webhooks, the wallet_tracker:events channel.The same events plus holdings, positions, PnL, token and counterparty context, token:prices and price alerts.

Architecture

From a user's wallet to your portfolio screen

  1. 1 · Source

    Chain activity

    Transactions and program events

  2. 2 · MadeOnSol

    Data and intelligence layer

    • Swap and transfer events
    • Holdings, positions and SOL PnL
    • Token and counterparty labels
  3. 3 · Delivery

    • REST API/wallet-tracker/* · /wallet/* · /token/*
    • Webhookswallet_tracker:event
    • WebSocket streamswallet_tracker:events · token:prices
  4. 4 · Yours

    Your activity rows, valuation and accounting

    Keys and business logic stay server-side

  5. 5 · Users

    Your portfolio app

Build outcomes

What portfolio teams build

  • Wallet activity feeds with readable rows
  • Multi-wallet portfolio dashboards
  • Airdrop and transfer-in detection
  • Counterparty and token warnings in the activity list
  • Price and activity notifications for held tokens

Showing MadeOnSol data to your own users requires a Business plan. See pricing

Keep going

Related

Solution

Wallet Tracking

The watchlist and event delivery behind the activity feed.

Solution

Token Risk

Risk evidence for the tokens in a user's wallet.

Product

Wallet scanner

PnL, labels and recent trades for any address, no key.

Guide

Consume wallet_tracker:event webhooks

Payload fields, signature check, dedupe per wallet and transaction.

FAQ

Questions portfolio builders ask

Does MadeOnSol calculate a wallet's portfolio value?+
No, not a complete one. MadeOnSol provides wallet and onchain activity and the intelligence around it; valuation and accounting stay with your app, except for two verified capabilities: holdings value_usd for tokens in MadeOnSol's index only, and FIFO cost-basis PnL in SOL over the launchpad trades captured in the last 90 days. Holdings (Ultra) returns SOL as a SOL balance and leaves stablecoins, wrapped SOL and assets outside the index unpriced, so summary.total_value_usd is the sum of the priced tokens only. Fiat cost basis, tax lots and reports are not provided.
What wallet activity does MadeOnSol return for a portfolio app?+
For each watched wallet, swaps (token, buy or sell, SOL amount, token amount) and SOL transfers of at least 0.001 SOL (amount and matched counterparty, without direction), each with its transaction signature, slot and a replayed flag. Token transfers without a SOL or stablecoin leg and stablecoin transfers are not events; holdings transfer_delta shows those arrivals. Token names and wallet labels are separate calls: GET /token/{mint} and POST /wallet/batch/classify.
How is Portfolio Trackers different from Wallet Tracking?+
Wallet Tracking is the infrastructure: a watchlist of addresses and delivery of their swap and transfer events to your backend over REST, webhooks and WebSocket. Portfolio Trackers is what a product builds on it for end users: readable activity rows, balances and results from holdings, positions and PnL, and labels for the tokens and counterparties in each row.
Can portfolio activity reach users in real time?+
Yes, through two push paths: the signed wallet_tracker:event webhook from Pro, and the wallet_tracker:events WebSocket channel from Ultra. The token:prices channel (Pro) streams price and market cap for the mints a user holds. Without push, poll GET /wallet-tracker/trades in slot order.
Which plan does a portfolio tracker need?+
Wallet Tracker REST, its webhook, positions, PnL and counterparty labels start on Pro. The wallet_tracker:events WebSocket channel and verified holdings need Ultra. GET /token/{mint} works on a Free key. Showing MadeOnSol data to your own users needs the Business plan.
Can I show a user's full transaction history?+
Not from MadeOnSol alone. The watchlist records a wallet from the moment you add it and keeps events 120 days. For earlier activity, GET /wallet/{address}/trades returns the trades captured by the launchpad pipeline since 2026-04-12. Store rows you want to keep longer, and use your RPC provider for transactions outside that pipeline.
Can a consumer app track every user's wallet?+
One API account holds one watchlist: 50 wallets on Pro, 100 wallets on Ultra, 500 wallets on Business. Beyond that, poll POST /wallet/batch/trades for up to 50 wallets per call and feed next_since back as since.
Can this work alongside an existing RPC provider?+
Yes, and a portfolio app needs one. MadeOnSol turns wallet transactions into events and adds token and wallet context; your RPC provider covers raw transactions, transfer direction, assets outside MadeOnSol's index and anything your app signs.
Can teams prototype first?+
Yes. The free wallet scanner at /wallet shows PnL, labels and recent trades for any address with no key, and a free key from the developer page calls GET /token/{mint}. The Wallet Tracker and the wallet endpoints start on Pro.

Next step

Show users what happened in their wallet, not just what is in it.

Scan a wallet to see the data, check what each dataset covers, then build the activity feed on the wallet API.

  1. 1 · TryScan a wallet Free, no key needed.
  2. 2 · ProofData dictionary Every dataset and its coverage window.
  3. 3 · DocsWallet Tracker API Watchlist, events, summary.
  4. 4 · PlanCompare plans Wallet Tracker from Pro.