USDT 和 USDC 不是同一个整数:收款前要查的五件事

两个都显示 $1.00,但不是同一个资产:发行方、储备、合约、精度、冻结权限都不同。收款前的五项检查与一段可直接跑的 RPC 脚本。

USDT 和 USDC 不是同一个整数:收款前要查的五件事

一句话结论:如果你写支付、钱包或交易机器人,迟早会看到这种代码:if token in ("USDT", "USDC"): value = amount。两者在屏幕上都是 $1.00,于是很容易被当成同一个"美元"。它们不是:不同的发行方、不同的储备、不同的规则、链上是不同的合约、各有各的管理员权限。收款前至少要查五件事——资产负债表、不要硬编码 1.00、按 (chain, contract_address) 而不是 ticker 识别、逐个合约读 decimals()、以及两家发行方都能在合约层冻结地址。文末给出一段只用 Python 标准库、直接读以太坊主网合约的核查脚本。

披露:本文编译自 NutshellCrypto 的开发者清单(dev.to,2026-10-17),作者同时推广其站内的 USDT vs USDC 完整对比,属于工具/内容站推广;文中的链上读取方法可独立复现。原文声明"仅供教育,不构成投资建议",并说明写作过程有 AI 辅助。


一、两个发行方,两张资产负债表

项 USDT USDC
发行方 Tether(2014 年推出) Circle(母公司已在纽交所上市)
市值(CoinGecko,2026-10-16) 约 $183.5B 约 $72.6B
储备(报告期) $187.75B 储备 / $183.64B 负债(2026-06-30) $73.40B 储备 / $73.32B 流通(2026-08-31)
储备构成 大部分是国债与回购,也持有黄金、比特币、有担保贷款 几乎全部是贝莱德管理的政府货币市场基金 + 银行现金
审计/鉴证频率 季度鉴证 月度鉴证

所以:USDT 的缓冲更厚,但部分由波动资产构成;USDC 的缓冲更薄,但由流动性很强的资产构成。 如果你的应用替用户持有大额余额,这个差异应该写进风险说明,而不只是显示在界面上。

二、不要硬编码 1.00

两种币都短暂脱过锚:

时间 事件 价格
2018-10 USDT 约 $0.92
2022-05 TerraUSD 崩盘期间 USDT 约 $0.95
2023-03 Circle 储备被困硅谷银行,USDC 约 $0.88

每一次都恢复了,但一个假设"恰好等于 $1"的机器人或会计系统,在这些窗口里会错误定价。

实用规则:像对待任何资产一样,从价格源给稳定币定价,并在价格偏离你设定的区间时告警。

三、同一个 ticker,每条链都是不同合约

USDT 和 USDC 存在于很多网络,每个版本都是独立的代币合约:

  • USDT 主要在 Tron 和 Ethereum;
  • USDC 主要在 Ethereum,Circle 也在几十条链上发了原生 USDC。

把 Tron 上的 USDT 发到一个只监听 Ethereum 的地址,是经典的丢钱方式。

对开发者意味着:

  • 代币注册表以 (chain, contract_address) 为键,永远不要用 ticker;
  • 从每个合约读 decimals(),不要假设。两者在以太坊上都是 6 位,但跨链/桥接版本不一定一致;
  • 在用户发送前,把网络显示清楚。

四、两家都能冻结你的代币

这一点会让"自托管就没人能动我的余额"的想法落空:两家发行方都能在合约层把地址加入黑名单,通常是因为执法或制裁请求。你的私钥仍然控制地址,但代币合约可以拒绝转移余额。

你可以自己验证。下面的脚本只用 Python 标准库,通过普通 JSON-RPC 调用以太坊主网合约,读取 decimals、该链上的总供应量,以及某个地址是否被拉黑:

import json, urllib.request

RPC = "https://ethereum-rpc.publicnode.com"
TOKENS = {
    "USDT": ("0xdAC17F958D2ee523a2206206994597C13D831ec7", "0xe47d6060"),  # isBlackListed(address)
    "USDC": ("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "0xfe575a87"),  # isBlacklisted(address)
}
DECIMALS, TOTAL_SUPPLY = "0x313ce567", "0x18160ddd"

def eth_call(to, data):
    body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": "eth_call",
                       "params": [{"to": to, "data": data}, "latest"]}).encode()
    req = urllib.request.Request(RPC, body, {"Content-Type": "application/json",
                                             "User-Agent": "stable-check/0.1"})
    with urllib.request.urlopen(req, timeout=15) as r:
        return int(json.load(r)["result"], 16)

def check(wallet):
    arg = wallet.lower().removeprefix("0x").rjust(64, "0")
    for name, (token, blacklist_sel) in TOKENS.items():
        dec = eth_call(token, DECIMALS)
        supply = eth_call(token, TOTAL_SUPPLY) / 10**dec
        frozen = bool(eth_call(token, blacklist_sel + arg))
        print(f"{name}: decimals={dec}  supply on Ethereum={supply:,.0f}  frozen={frozen}")

check("0x000000000000000000000000000000000000dEaD")

两个容易踩的细节:

  1. 两家连函数名拼写都不同:USDT 是 isBlackListed(大写 L),USDC 是 isBlacklisted(小写 l),所以选择器也不同;
  2. 公共 RPC 会限流,超过快速检查请用你自己的节点或服务商。

五、规则决定你能"上架"哪种币

监管现在直接影响产品能提供什么:

  • USDC 是欧盟 MiCA 框架下的授权电子货币代币(EMT);主要交易所曾对欧盟用户限制 USDT 交易;
  • 加拿大注册平台提供 USDC,但不提供 USDT;
  • 美国的 GENIUS Act 建立了稳定币发行方许可制度,Tether 在美国受监管的选择是另一个代币 USAT。

如果你面向多个地区发布,支持的资产清单可能需要按司法辖区不同。

六、收款验收清单

# 检查 具体做法
1 资产身份 用 (chain, contract_address) 识别,不用 ticker
2 精度 从合约读 decimals(),不要写死 6/18
3 价格 从价格源读,不硬编码 1.00;设置偏离告警
4 冻结状态 收款前/大额前查黑名单
5 网络匹配 收款地址与链绑定,发送前向用户明确显示
6 辖区规则 按地区维护可上架资产清单
7 储备风险 大额余额的发行方/储备构成进入风险说明
8 到账判定 用确认数/最终性规则,不只看交易哈希

七、给 Agent 支付与自动收款的三条延伸

  1. 不要让 Agent 自己"猜"代币。把 (chain, address, decimals) 作为工具参数的一部分,模型只负责选择,不负责构造。
  2. 把黑名单检查放进付款前流水线:如果收款地址被发行方冻结,付款会失败;如果付款方地址被冻结,代币可能根本转不出来。
  3. 把"1 USDT = 1 USD"从代码假设降级为监控指标:脱锚时自动收紧额度或暂停出金,比事后解释便宜。

八、一句话总结

USDT 和 USDC 都是"美元稳定币",但在链上它们不是同一个整数、不是同一个合约、不是同一套规则。收款系统要做的不是"识别 ticker",而是识别资产:哪条链、哪个合约、几位小数、能不能动、受哪个辖区管。


Sources

同主题阅读

本文属于「Agent 支付 / x402 与稳定币支付」专题。DefiPlot 翻译整理,转载请标明出处。

免责声明:本文仅用于学习目的,不构成投资或法律建议,不推荐任何代币、交易所或司法辖区方案。监管与合约行为可能变化,收款前请自行核验并咨询专业人士。

免责声明:本站资源仅用于学习目的,也不应被视为投资建议,读者在采取任何行动之前应自行研究并对自己的决定承担全部责任。