第 7 章 · 02 Jupiter 代币价格与搜索


文档摘要

第 7 章 · 02 Jupiter 代币价格与搜索 本节摘要:本节精读两个 Solana 代币数据工具—— (约 75 行)和 (约 68 行)。它们都走 Jupiter 官方 API( ),一个按 mint 地址查 USD 价格(Price API V3),一个按 symbol/名称/mint 搜代币信息(Tokens API V2)。两个文件结构高度对称:用 发 GET、读 加 header、返回 字符串。本节重点讲清三个工程要点:为什么价格按 mint 地址而非 symbol 查(Solana 上同名代币泛滥)、Jupiter 的 API key 是可选的(有 key 提高额度,没 key 也能用公共额度)、以及两个工具的对称设计如何降低心智负担。

第 7 章 · 02 Jupiter 代币价格与搜索

本节摘要:本节精读两个 Solana 代币数据工具——jupiter_price.py(约 75 行)和 jupiter_search.py(约 68 行)。它们都走 Jupiter 官方 API(api.jup.ag),一个按 mint 地址查 USD 价格(Price API V3),一个按 symbol/名称/mint 搜代币信息(Tokens API V2)。两个文件结构高度对称:用 httpx.Client 发 GET、读 JUPITER_API_KEYx-api-key header、返回 json.dumps(resp.json()) 字符串。本节重点讲清三个工程要点:为什么价格按 mint 地址而非 symbol 查(Solana 上同名代币泛滥)、Jupiter 的 API key 是可选的(有 key 提高额度,没 key 也能用公共额度)、以及两个工具的对称设计如何降低心智负担。读完本节,你理解 Solana 链上「认物」和「认价」的标准做法。

内容来源:原项目源码 autohedge/tools/jupiter_price.pyautohedge/tools/jupiter_search.py,逐行精读并套用体系化模板。

⚠️ 现实澄清:Jupiter 是 Solana 上最大的 DEX 聚合器,API 相对稳定,但免费额度有限。生产环境建议申请 JUPITER_API_KEY(在 portal.jup.ag),否则高频调用会被限流。

学习目标

阅读完本节,你应当能够:

  1. 逐行读懂 get_token_pricesearch_tokens 两个函数。
  2. 说清 Jupiter Price V3Tokens V2 Search 两个端点的分工(按 mint 查价 vs 按 symbol 搜代币)。
  3. 理解 Solana 为什么按 mint 地址而非 symbol 标识代币(防同名伪造)。
  4. 解释 JUPITER_API_KEY可选鉴权的含义(公共额度 + 提升额度)。
  5. 看出两个文件的对称设计并体会复用模式。

一、两个工具的分工

Solana 链上有成千上万种代币,要拿到「某代币当前值多少美元」和「某名字对应哪个代币」,是两个不同的问题:

需求 工具 端点 入参 出参
已知 mint,查 USD 价格 get_token_price /price/v3 mint 地址(可多个) {mint: {usdPrice, decimals, ...}}
只知道名字/symbol,找代币 search_tokens /tokens/v2/search symbol/名称/mint [{id, name, symbol, mcap, liquidity, ...}]

典型工作流是先 search 拿 mint,再 price 查价:用户输入「USDC」,先 search_tokens("USDC") 拿到 USDC 的 mint 地址 EPjF...Dt1v,再 get_token_price("EPjF...Dt1v") 拿到当前价格。两步缺一不可——直接拿 symbol 查价不行,因为 Jupiter 价格端点只认 mint。

二、为什么按 mint 而非 symbol

这是 Solana 与传统交易所的根本差异。在 A 股,「600519」唯一对应贵州茅台;在 CEX,「BTC」基本唯一指比特币。但在 Solana 这种人人可发币的链上:

  • 任何人都能发一个 symbol 也叫 USDC、名字也叫 USD Coin 的假币。
  • 真正的 USDC 只能靠 mint 地址(EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v)唯一确定。
  • 假冒代币的 mint 地址完全不同,链上一看便知。

所以 Jupiter Price API 只认 mint 地址,这是防伪造的根本设计。search_tokens 返回结果里也带 isVerifiedorganicScoreliquidity 等字段,帮用户分辨真假。

💡 核心心法:Solana 的世界,mint 地址是代币的身份证号,symbol 只是花名。花名可以重名,身份证号不会。任何链上操作(查价、转账、兑换)都必须最终落到 mint 地址。

三、jupiter_price.py 逐行

完整代码:

import json import os from typing import List, Union import httpx from loguru import logger JUPITER_PRICE_BASE = "https://api.jup.ag" def get_token_price(ids: Union[str, List[str]]) -> str: """ Get USD prices for one or more Solana token mint addresses via Jupiter Price API V3. ... """ if isinstance(ids, list): # ① 列表→逗号拼接 ids_param = ",".join(ids) else: ids_param = ids if not ids_param or not ids_param.strip(): # ② 空入参兜底 logger.warning("get_token_price: ids is empty") return "{}" url = f"{JUPITER_PRICE_BASE}/price/v3" params = {"ids": ids_param} headers = {} key = os.getenv("JUPITER_API_KEY") # ③ 可选鉴权 if key: headers["x-api-key"] = key try: with httpx.Client(timeout=10) as client: resp = client.get( # ④ GET /price/v3 url, params=params, headers=headers or None ) resp.raise_for_status() return json.dumps(resp.json()) # ⑤ 原样转字符串返回 except httpx.HTTPError as e: logger.error(f"Jupiter price API request failed: {e}") raise # ⑥ 失败抛异常

逐段:

  • ① 列表拼接:支持传单个 mint 字符串,或多个 mint 的列表。列表时用逗号拼成一个字符串(如 "So111...11112,EPjF...Dt1v"),作为 ids 查询参数。一次请求查多个价,省往返。
  • ② 空兜底:入参空直接返回 "{}",不发空请求。和 yahoo_api 的约定一致——工具失败/空输入返回安全字符串。
  • ③ 可选鉴权:JUPITER_API_KEY 有就加 x-api-key header,没有就用公共额度。headers or None 这一句很妙——空 dict 在 Python 里是 falsy,{} or None 等于 None,httpx 收到 None 就不发 header,避免发个空 header 惹麻烦。
  • ④ GET 请求:超时 10 秒,比 ultra_tools 的 30 秒短——查价是轻量读操作,不该等太久。
  • ⑤ 原样返回:json.dumps(resp.json()) 把 Jupiter 返回的 JSON 原样序列化回字符串。注意这里不做任何字段筛选,把完整响应交给 LLM,让它自己挑 usdPricepriceChange24h 等字段。
  • ⑥ 失败抛异常:和 exa_search(失败返回字符串)不同,这里 raise 把 HTTP 错误往上抛。设计取舍:价格查不到是硬错误(可能是 mint 写错、网络断了),让上层知道比吞掉好。

四、返回结构:价格对象

Price V3 返回结构(以 wrapped SOL 为例):

{ "So11111111111111111111111111111111111111112": { "decimals": 9, "usdPrice": 145.32, "blockId": 123456789, "priceChange24h": 2.15 } }

字段含义:

  • key 是 mint 地址:所以查多个代币时,返回是个 map,按 mint 取值。
  • decimals:精度(用于链上最小单位↔人类单位的换算。SOL 的 decimals=9 意味着 1 SOL = 10^9 lamports)。
  • usdPrice:当前 USD 价格。
  • blockId:这个价格对应的链上区块号(可用于判断价格新鲜度)。
  • priceChange24h:24 小时涨跌幅(百分比)。

💡 decimals 的重要性:get_order 的 amount 参数用的是最小单位(lamports),不是人类单位(SOL)。要换算必须知道 decimals。所以查价时一并拿到 decimals,是后续构造交易金额的必要信息——第 8 章会用到。

五、jupiter_search.py 逐行

完整代码:

import json import os import httpx from loguru import logger JUPITER_TOKENS_BASE = "https://api.jup.ag/tokens/v2" def search_tokens(query: str) -> str: """ Search for Solana tokens by symbol, name, or mint address via Jupiter Tokens API V2. ... """ if not query or not query.strip(): # ① 空入参兜底 logger.warning("search_tokens: query is empty") return "[]" url = f"{JUPITER_TOKENS_BASE}/search" params = {"query": query.strip()} headers = {} key = os.getenv("JUPITER_API_KEY") # ② 可选鉴权(同 price) if key: headers["x-api-key"] = key try: with httpx.Client(timeout=10) as client: resp = client.get( # ③ GET /tokens/v2/search url, params=params, headers=headers or None ) resp.raise_for_status() return json.dumps(resp.json()) # ④ 原样返回 except httpx.HTTPError as e: logger.error(f"Jupiter tokens search request failed: {e}") raise

结构和 get_token_price 几乎一模一样:同样的空兜底、同样的可选鉴权、同样的 GET + raise_for_status、同样的 json.dumps 返回。差别只有三处:

  • BASE 不同:/tokens/v2 vs /price/v3
  • 入参语义不同:query(symbol/名称/mint 字符串)vs ids(mint 地址)。
  • 空兜底返回不同:"[]"(数组空)vs "{}"(对象空)——因为 search 返回的是数组,price 返回的是 map,空值形态要对应。

💡 对称设计的红利:两个工具结构一致,学会一个就会用另一个。读者维护时心智负担小,LLM 调用时也容易触类旁通。这是「同构 API」应保持「同构客户端」的好实践。

六、search 返回结构:代币信息

search 返回一个数组,每个元素是一个代币信息对象:

[ { "id": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "icon": "https://...", "holderCount": 1234567, "fdv": 3.4e9, "mcap": 3.3e9, "usdPrice": 1.0, "liquidity": 1.2e7, "organicScore": 0.95, "isVerified": true, "tags": ["stablecoin", "verified"] } ]

几个关键字段:

  • id:就是 mint 地址(Price API 用的就是它)。
  • organicScore:「有机评分」,衡量这个代币的交易/持仓是不是真实的(防刷量)。
  • isVerified:是否经过 Jupiter 验证。
  • liquidity:流动性(美元),流动性太低的代币很难成交、容易被夹。
  • fdv/mcap:完全稀释估值 / 流通市值。

💡 真假代币分辨:搜 USDC 可能返回好几个结果(真 USDC + 各种假币)。要靠 isVerified=trueorganicScore 高、liquidity 大、holderCount 多这几个指标综合判断,锁定真的那个,拿到它的 id(mint)再去查价。

七、两个工具的协作流程

把 search 和 price 串起来,从用户输入到拿到价格的完整链路:

注意中间「筛选」这一步是 LLM 或上层逻辑做的——search 返回多个候选,工具本身不替你决定哪个是真 USDC。这正契合工具设计的边界:工具提供信息,判断留给 Agent。

八、与 yahoo_api 的对比

把 Jupiter 两个工具和 yahoo_api 放一起对比,体会「链上数据」与「股票数据」工具的差异:

维度 yahoo_api jupiter_price/search
标识符 symbol(如 AAPL,基本唯一) mint 地址(唯一),symbol 易重名
数据源稳定性 非官方抓取,常被 429 Jupiter 官方 API,较稳
鉴权 无(免费) 可选 JUPITER_API_KEY
失败处理 部分数据返回 + _warning HTTPError
返回 JSON 字符串(经过 _df_to_json_serializable 加工) JSON 字符串(原样 json.dumps)

⚠️ 现实澄清:Jupiter 工具的失败处理是直接 raise,意味着如果网络抖动或被限流,工具会抛异常给上层 Agent。swarms 框架默认不一定会优雅处理工具异常——这可能让 Agent 整个 run 失败。对比 yahoo_api 的「部分返回」,这里的容错更粗放,是可改进点。

本节要点回顾

  1. 两个工具分工:get_token_price 按 mint 查价(Price V3),search_tokens 按 symbol/名称/mint 搜代币(Tokens V2);先 search 拿 mint,再 price 查价。
  2. 按 mint 不按 symbol:Solana 人人可发币,symbol 易重名、易伪造;mint 地址是唯一身份证,价格端点只认 mint。
  3. get_token_price 要点:支持 mint 列表逗号拼接;超时 10 秒;原样返回含 usdPrice/decimals/priceChange24h 的对象;失败 raise。
  4. search_tokens 要点:返回代币信息数组,带 isVerified/organicScore/liquidity 用于分辨真假;结构和 price 工具高度对称。
  5. 可选鉴权:JUPITER_API_KEY 有就加 x-api-key 提升额度,没有用公共额度;headers or None 处理空 dict。
  6. decimals 的伏笔:查价时一并拿到 decimals,是第 8 章构造链上交易金额(最小单位换算)的必要信息。

下一节,我们直面本项目一个尴尬真相——polygon_api.py 文件名叫 polygon,代码里指向的却是 massive.com 的占位实现,这背后是项目成熟度的一个缩影。


发布者: 作者: 灏天文库 转发
评论区 (0)
U