Back to Merlin|Feature Specs

xStock Resolver

Status: Live

Overview

The xStock Resolver is the canonical token identification layer for Merlin's trading pipeline. It maintains an in-memory registry of 61 xStock tracker certificate tokens — ERC-20s on Ethereum mainnet issued by Backed Finance — and resolves free-form user input (company names, tickers, partial strings, xStocks.fi ticker format) to a single, unambiguous token record. When resolution is ambiguous, the resolver returns ranked candidates and requires explicit user clarification before any trade intent is forwarded downstream.

Architecture

User input (raw string)
        |
        v
  Normalize input
  (lowercase, strip whitespace + punctuation)
        |
        v
  Stage 1: Exact symbol match
  ("xTSLA" or "TSLAx" → xTSLA)
        |
        v
  Stage 2: Exact ticker match
  ("TSLA" → xTSLA, "FB" → xMETA alias)
        |
        v
  Stage 3: Exact company name match
  ("Tesla" → xTSLA, "Alphabet" → xGOOG)
        |
        v
  Stage 4: Partial / fuzzy match
  (difflib.SequenceMatcher, threshold ≥ 0.6)
        |
        v
  Ambiguity check
  (single winner → resolve, multiple ≥ threshold → return candidates)
        |
        v
  Canonical token record
  {symbol, xstocks_ticker, name, ticker, type, address, confidence}
        |
        v
  Downstream pipeline (Node 2+)

Crypto assets (ETH, USDC, USDT, WETH) are handled via a separate CRYPTO_ASSETS dict and bypass the xStock matching stages entirely. The resolver checks crypto identity before entering the xStock stages so that "ETH" or "Ethereum" never incorrectly fuzzy-matches an xStock token.

Price data is fetched independently of resolution: CoinMarketCap for crypto assets, Backed Finance REST API for xStock tokens. Both price sources share a 60-second in-memory TTL cache to avoid redundant API calls during high-frequency chat sessions.

Implementation Details

  • Registry size: 61 xStock tokens in XSTOCK_REGISTRY list at module load time (no DB read required)
  • Token record fields: symbol (Merlin canonical, e.g. xTSLA), xstocks_ticker (xStocks.fi format, e.g. TSLAx), name (full company/fund name), ticker (underlying exchange ticker), type (stock | etf | commodity), address (Ethereum mainnet ERC-20 contract address)
  • Ticker conventions: Two parallel formats are supported. xStocks.fi uses TICKERx (e.g. TSLAx, SPYx). Merlin uses xTICKER (e.g. xTSLA, xSPY). Both are accepted as input and normalized to the Merlin xTICKER form internally.
  • Known ticker aliases: GOOGLxGOOG, FBxMETA. These are hardcoded in the alias table alongside any other legacy or dual-class tickers.
  • Fuzzy matching: difflib.SequenceMatcher(None, normalized_input, candidate_field) run across symbol, ticker, and name fields. Scores below 0.6 are discarded. The highest-scoring candidate above threshold wins; if two or more candidates score within 0.05 of each other, the result is ambiguous and all candidates are returned.
  • Confidence scale: exact (1.0, direct symbol/ticker hit), high (0.85–0.99, company name match), partial (0.6–0.84, fuzzy substring), ambiguous (multiple candidates, user clarification required)
  • Crypto assets: ETH, USDC, USDT, WETH are defined in a separate CRYPTO_ASSETS dict with CoinMarketCap IDs and decimals. They are returned with type: crypto and bypass xStock resolution entirely.
  • Price oracle — crypto: CoinMarketCap /v1/cryptocurrency/quotes/latest endpoint, keyed by COINMARKETCAP_API_KEY environment variable.
  • Price oracle — xStocks: Backed Finance public API at https://api.backed.fi/api/v2/public. Endpoint returns NAV and last-trade price per token.
  • Price cache: Shared in-memory dict, 60-second TTL. Cache is per-symbol. Stale entries are evicted on next read, not on a background timer.
  • No blockchain reads for price: Prices are sourced entirely from Backed Finance API and CoinMarketCap. There is no on-chain price verification (Uniswap pool reads, Chainlink oracles) at this stage.

Token Registry

Stocks (45 tokens)

xStock Symbol Underlying Ticker Company / Fund
xTSLA TSLA Tesla Inc.
xAAPL AAPL Apple Inc.
xGOOG GOOGL Alphabet Inc.
xAMZN AMZN Amazon.com Inc.
xMSFT MSFT Microsoft Corp.
xNVDA NVDA NVIDIA Corp.
xMETA META Meta Platforms Inc.
xNFLX NFLX Netflix Inc.
xCOIN COIN Coinbase Global Inc.
xPLTR PLTR Palantir Technologies
xGME GME GameStop Corp.
xAMD AMD Advanced Micro Devices
xORCL ORCL Oracle Corp.
xCRM CRM Salesforce Inc.
xAVGO AVGO Broadcom Inc.
xINTC INTC Intel Corp.
xJPM JPM JPMorgan Chase & Co.
xV V Visa Inc.
xMA MA Mastercard Inc.
xBAC BAC Bank of America Corp.
xGS GS Goldman Sachs Group
xMSTR MSTR MicroStrategy Inc.
xHOOD HOOD Robinhood Markets Inc.
xLLY LLY Eli Lilly and Company
xADBE ADBE Adobe Inc.
xCRWD CRWD CrowdStrike Holdings
xPANW PANW Palo Alto Networks
xTSM TSM Taiwan Semiconductor
xKO KO The Coca-Cola Company
xPEP PEP PepsiCo Inc.
xWMT WMT Walmart Inc.
xHD HD The Home Depot Inc.
xMCD MCD McDonald's Corp.
xXOM XOM Exxon Mobil Corp.
xCVX CVX Chevron Corp.
xPFE PFE Pfizer Inc.
xMRK MRK Merck & Co. Inc.
xJNJ JNJ Johnson & Johnson
xUNH UNH UnitedHealth Group
xABT ABT Abbott Laboratories
xABBV ABBV AbbVie Inc.
xNVO NVO Novo Nordisk A/S
xRBLX RBLX Roblox Corp.
xAPP APP Applovin Corp.
xRIOT RIOT Riot Platforms Inc.

ETFs (12 tokens)

xStock Symbol Underlying Ticker Fund Name
xSPY SPY SPDR S&P 500 ETF Trust
xQQQ QQQ Invesco QQQ Trust
xGLD GLD SPDR Gold Trust
xSLV SLV iShares Silver Trust
xIWM IWM iShares Russell 2000 ETF
xIEMG IEMG iShares Core MSCI Emerging Markets ETF
xIJR IJR iShares Core S&P Small-Cap ETF
xSCHF SCHF Schwab International Equity ETF
xTBLL TBLL SPDR Bloomberg 3-12 Month T-Bill ETF
xTQQQ TQQQ ProShares UltraPro QQQ (3x leveraged)
xVTI VTI Vanguard Total Stock Market ETF
xVT VT Vanguard Total World Stock ETF

Commodities (3 tokens)

xStock Symbol Underlying Ticker Description
xPALL PALL Aberdeen Standard Physical Palladium ETF
xPPLT PPLT Aberdeen Standard Physical Platinum ETF
xCOPX COPX Global X Copper Miners ETF

Crypto Assets (separate registry, 4 assets)

Symbol Name Price Source
ETH Ethereum CoinMarketCap
USDC USD Coin CoinMarketCap
USDT Tether CoinMarketCap
WETH Wrapped Ether CoinMarketCap

Code Map

File Purpose
backend/services/xstock.py XSTOCK_REGISTRY list, CRYPTO_ASSETS dict, resolve_token(input_str) function, fuzzy matching logic, alias table, confidence scoring
backend/services/prices.py get_price(symbol) function, CoinMarketCap client (crypto), Backed Finance API client (xStocks), 60-second TTL cache, cache invalidation
backend/routers/chat.py GET /market/assets endpoint — returns serialized registry with metadata for frontend asset picker

API Endpoints

Method Path Description Auth
GET /market/assets Returns all xStock tokens and crypto assets with symbol, name, ticker, type, and address. Does not include live prices — prices are fetched separately per symbol. None (public)

Example response shape:

{
  "xstocks": [
    {
      "symbol": "xTSLA",
      "xstocks_ticker": "TSLAx",
      "name": "Tesla Inc.",
      "ticker": "TSLA",
      "type": "stock",
      "address": "0x..."
    }
  ],
  "crypto": [
    {
      "symbol": "ETH",
      "name": "Ethereum",
      "type": "crypto"
    }
  ]
}

Firestore Schema

None. The token registry is loaded entirely in-memory from XSTOCK_REGISTRY at service startup. There is no Firestore collection for token metadata. This eliminates read latency and Firestore costs for a dataset that changes infrequently (new tokens are added via code deploy, not database writes).

Configuration

Environment Variable Description Required
COINMARKETCAP_API_KEY CoinMarketCap Pro API key for crypto price quotes Yes (crypto prices)
Backed Finance API URL Hardcoded to https://api.backed.fi/api/v2/public — no env override needed N/A
Price cache TTL Hardcoded to 60 seconds in prices.py — not configurable at runtime N/A

Current Limitations

  • Placeholder contract addresses: Several of the 61 tokens in XSTOCK_REGISTRY have placeholder or unverified Ethereum mainnet contract addresses. Before executing any trade, the Trade Execution agent (Node 6) must validate the address against the Backed Finance API or Etherscan. Never use an address from this registry as a final settlement address without verification.
  • No on-chain price verification: Prices are sourced from Backed Finance API and CoinMarketCap only. There is no cross-check against Uniswap V3 pool prices or Chainlink oracle feeds. Stale or incorrect API prices would not be caught at this layer.
  • No real-time streaming: Price data is polled on-demand with a 60-second TTL cache. There is no WebSocket or SSE stream for live price ticks. Price displayed to the user may be up to 60 seconds old.
  • No on-chain liquidity check: The resolver does not verify that a Uniswap V3 pool exists with sufficient liquidity for the resolved token before forwarding to the trade pipeline. Low-liquidity tokens will fail at the quote stage, not the resolution stage.
  • Extended registry not fully covered: Backed Finance publishes 80+ tokens. The current registry contains 61. Tokens not in the list are not resolvable and the user is directed to xstocks.fi/products to check availability.
  • Alias table is hardcoded: Ticker aliases (FB → META, GOOGL → GOOG) are maintained as a static dict. Any new dual-class or legacy ticker aliases must be added manually in code.

Related

  • agents/xstock-resolver.md — Agent definition for Node 1 of the trade pipeline
  • agents/trade-execution.md — Trade Execution agent (Node 6) that consumes resolver output
  • agents/guardrails.md — Guardrails agent that enforces US person / sanctioned country blocks after resolution
  • specs/project-spec.md — Full Merlin platform specification
  • sources/futurewallet-docs.md — FutureWallet xStocks section (upstream reference)
  • https://xstocks.fi/products — Live xStocks token registry with current contract addresses
  • https://api.backed.fi/api/v2/public — Backed Finance public API (price + NAV data)