夜雨聆风学习资料网

ARTICLE · 1330405

USDT 智能合约源码拆解:mint、转账与权限控制

USDT 智能合约源码拆解:mint、转账与权限控制

USDT 智能合约源码拆解:mint、转账与权限控制

把以太坊上那份"祖传"合约摊开看,老代码里全是教科书级的设计取舍

系列三十二「稳定币技术拆解」第2篇

💡 你将学到:一套真实稳定币的完整权限矩阵、铸币与赎回的代码路径、为什么老合约至今还稳如老狗

先上一段代码热热身。下面是以太坊上 USDT 合约(节选自公开验证源码)的骨架,我做了注释翻译——先别细抠语法,感受一下它的"年代感":

tether_skeleton.sol

pragma solidity ^0.4.17;// 注意:0.4 时代的代码,比现在主流低了四个大版本

contract TetherToken {

stringpublic name;

stringpublic symbol;

uint8public decimals = 6;// 注意:6 位小数,不是 18

uint256public totalSupply;

mapping(address => uint256) public balanceOf;

mapping(address => mapping(address => uint256)) public allowance;

addresspublic owner;// 发行方控制地址

addresspublic upgradeAgent;// 历史遗留:早期版本升级接口

uintpublic deprecateDelegate;

mapping(address => bool) public blackList;// 冻结名单

boolpublic paused;// 全局暂停开关

...

}

三处细节值得停一停:0.4 版本的编译器(现在的新合约基本都是 0.8+)、6 位小数(对齐美元的两位小数再加冗余)、以及两个敏感词级别的变量——blackList 和 paused。后文一一展开。

01 一、权限矩阵:谁能动哪把刀

老派合约的权限控制没有 OpenZeppelin 那套花哨的 Role 系统,就是简单的 owner 判断。把所有受限函数列出来,权限结构一目了然:

•**铸币(issue)**:仅 owner。发行方在收到法币入金凭证后增发,是全网唯一的新增入口

•**赎回(redeem)**:仅 owner。销毁流通量,对应出金

•**冻结(blackList)**:仅 owner。将地址加入/移出冻结名单

•**暂停(pause)**:仅 owner。全局停止转账,应急开关

•**设置升级代理(setUpgradeAgent / deprecate)**:仅 owner。历史遗留能力

六把刀全部握在一个地址手里。这种设计放今天会被审计骂出天际(单点风险),但在"发行方 = 中心化信用主体"的稳定币语境下,它反而是符合业务模型的——因为稳定币的本质承诺就是"有一个主体对总量负责"。权限集中和中心化信用,在这里是自洽的。

02 二、转账函数:19 行代码里的四个拦截器

看核心的 transfer 实现(简化还原):

transfer.sol

function transfer(address _to, uint _value) public onlyPayloadSize(2) {

// 拦截器 1:全局暂停

if (paused) throw;

// 拦截器 2:发送方是否被冻结

if (blackList[msg.sender]) throw;

// 拦截器 3:接收方是否被冻结

if (blackList[_to]) throw;

// 经典三查:余额、溢出、非零

if (balanceOf[msg.sender] < _value) throw;

if (balanceOf[msg.sender] + _value < balanceOf[msg.sender]) throw;

if (balanceOf[_to] + _value < balanceOf[_to]) throw;

balanceOf[msg.sender]-= _value;

balanceOf[_to]+= _value;

Transfer(msg.sender,_to, _value);// 老版本的事件写法

}

逐个看这些拦截器。onlyPayloadSize 是个有趣的古董——0.4 时代没有 0.8 的自动溢出检查,也没有 EIP-20 的 Short Address Attack 防护标准,作者手写了这个修饰器来防"调用数据长度被恶意截断"的攻击。现代编译器已经让这类手写防护进了博物馆,但读懂它,你就理解了智能合约安全史的一段演进。

paused 和 blackList 两个拦截器合起来,意味着每一笔转账前都要多查两档状态。对用户来说,这就是"稳定币转账可能被拒"的技术根源——它不是 bug,是写在代码里的合规动作。

03 三、铸币与赎回:总量变化的唯一通道

issue.sol

function issue(uint amount) public onlyOwner {

require(balances[owner] + amount > balances[owner]);

require(totalSupply + amount > totalSupply);// 老式溢出检查

balances[owner]+= amount;// 先记到发行方名下

totalSupply+= amount;

Issue(amount);// 增发事件,链上全程可审计

}

function redeem(uint amount) public onlyOwner {

require(totalSupply >= amount);

require(balances[owner] >= amount);

balances[owner]-= amount;

totalSupply-= amount;

Redeem(amount);// 销毁事件

}

两条路径都只认 owner,都强制发事件。这意味着一个重要事实:这个合约的全部总量变化,链上可查、有账可对。上一篇文章讲的"各链铸销对账",审计上就是靠这些 Issue/Redeem 事件串起来的。

04 四、给老代码挑刺:三个今天不会这么写的点

读完源码,反过来看看哪些写法已经过时——这也是把老合约当教材的价值所在:

•**没有自定义错误**:所有失败都 `throw`,今天会换成 `error Unauthorized()` 这类自定义错误,Gas 省一大截

•**手写溢出检查**:0.8+ 编译器内置溢出回滚,那些 `+ amount > totalSupply` 的判断今天可以全删

•**单 owner 无时限**:今天的稳定币普遍会给关键权限加时间锁(Timelock),敏感操作要等一段时间才生效,给市场留反应窗口

老代码不是烂代码,它是特定时期的合理答案。读它的意义,是让你知道"现在为什么这样写"。

⚠️ 本文源码为教学节选与简化,以链上公开验证的完整源码为准。仅作技术探讨,不构成任何操作建议。

✅ 六把权限刀集中在一个 owner 地址,与中心化信用主体自洽

✅ 转账函数四个拦截器:全局暂停、双方冻结检查、老式溢出防护

✅ issue/redeem 是总量变化的唯一通道,事件全程可审计

✅ 老代码不是烂代码:手写溢出检查都是那个年代的合理答案

─────────────────────────

📖 下篇预告:稳定币脱锚事件复盘:锚定是怎么被打破又修复的

📢 本文由「区块链编程」创作出品

未经授权,禁止转载

如有转载需求,请联系作者

👉 关注「区块链编程」

关注我,解锁更多可能

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料