最近市场上出现了很多分红币,比如最近流行的( BabyDoge, MiniDoge 之类的),大概模式都是交易手续费的 x% 按照持有比例分给所有持币人,今天我们来解析一下背后的代码实现方式。
第一想法肯定是计算出分红的手续费之后,按照比例给所有人加上,solidity 代码如下:
// 分红总金额
uint profit;
// 当前总持币量
uint totalAmounts;
for (uint i = 0; i < users.length; i++) {
// 给每个用户分红
amounts[i] += amounts[i] / totalAmounts \* profit;
}
但这样做会有一个很大的问题:随着持币人数过多,每一笔转账消耗的计算资源会急速增长,甚至超出交易的单笔上限。
目前分红类代币采用了一个很聪明的做法,使用了类似于金融中的通缩概念:“如果货币总量减少,每个人手上的货币不就变得更值钱的,相当于变相完成了分红“
接下来我们来看源代码是怎么实现的
// 存储用户的虚拟数量
mapping(address => uint256) private _rOwned;
// 真正的发行量(比如发行了 1w 枚币,精度为 0,_tTotal = 10000 )
uint _tTotal;
uint256 private constant MAX = ~uint256(0);
// 最大的一个可以整除 _tTotal 的数,这个数字类似于“虚拟的货币总量”
uint _rTotal = (MAX - (MAX % _tTotal));
假设在合约部署时需要把 1w 枚币发给 owner,代码如下
constructor() {
_tTotal = 10000;
// 初始化 rTotal
uint256 private constant MAX = ~uint256(0);
uint _rTotal = (MAX - (MAX % _tTotal));
// 把所有的币发给 owner
_rOwned[owner] = _rTotal;
}
那么此时怎么计算用户余额呢?我们来看 balanceOf 函数
function balanceOf(address account) public view override returns (uint256) {
uint256 currentRate = _rTotal / _tTotal;
return _rOwned[account] / currentRate;
}
看起来就是先用 虚拟货币总量 / 真实货币总量 得到一个比例,然后除以这个比例即可拿到真实的余额。
我们再看一下 最重要的 transfer 函数是怎么实现的,我们假设分币比例是 3%
function transfer(address from, address to, uint amount) public {
uint currentRate = _rTotal / _tTotal;
uint rAmount = amount _ currentRate;
// 3%的分红
uint rProfit = amount _ currentRate _ 3 / 100;
// 转让人扣除 100%
_rOwned[from] = _rOwned[from] - rAmount;
// 接收人收到 97%
_rOwned[to] = _rOwned[to] + rAmount _ 97 / 100;
// 剩下 3%扣除总的虚拟货币,这时候所有人的 banlanceOf 函数计算都会按照比例增长
_rTotal = _rTotal - rProfit;
}
不得不说整套体系设计得挺厉害的,虽然存在一定的计算误差,但大大提高了计算效率。
微信:czz5242199
telegram: @zhuozhongcao