代币通缩模型最早起源 meme币Pig 发扬光大在safemoon ,有早期用户0.2eth变6700万美元的神话。
道理比较简单,就是币随交易,越来越少,从而导致代币越来越有价值。 而体现合约中,查阅后却没有一个很好的解释,趁着最近在看合约代码,试图做一些浅显的理解: 我以一个实现通缩的Token catgirl 为例
首先先明确两个uint类型私有变量:_tTotal 和 _rTotal
其中 _rTotal >> _tTotal
uint256 private constant MAX = ~uint256(0);
//对外展示的币总量,为100000 * 10**12 * 10**9,
uint256 private _tTotal = 100000 * 10**12 * 10**9;
//是内部实际的币总量,为2^256-(2^256%_tTotal)
uint256 private _rTotal = (MAX - (MAX % _tTotal));
如果你对银行业务比较了解,你应该知道一个名词叫做存款准备金率。其核心就是 中央银行要求的存款准备金占其存款总额的比例就是存款准备金率(deposit-reserve ratio)。
_rTotal and _tTotal类似于 银行存款,但是相反的是。这里 _ rTotal 为中央银行的总代币供应量, _tTotal是你口袋中+存在银行里的代币数量。rTotal大于_tTotal,为什么会大于,下面会将,现在只需要记住_rTotal和_tTotal的类比概念即可。
函数 _getRate() 则表示_rTotal/_tTotal,类似于上面的存款准备金率概念。
核心原理:就是在_tTotal 除扣除手续费外总量不变的情况下,_rTotal每次交易扣除2倍手续费来进行通缩,从而提升所有持有相同t币数量的t币价值。
下面我根据下图来进行讲解
地址A 代表发币地址 初始tTotal = 10000
内部:
tTotal[A] = 10000
rTotal[A] = 127366483000000
外部显示:balanceOf(A) = 10000
此刻
rTotal = 127366483000000
tTotal = 10000
rate = rTotal / tTotal = 12736648300
假设用户A向用户B转账100个 t币 tAmount = 100
故:
rAmount = tAmount * rate = 1273664830000
tfee = 10%*tAmount = 10
rfee = tfee *rate = 127366483000
rTransferAmount = rAmount - rfee = 1146298347000
交易结束后rTotal再销毁 一份rfee 并且记录等值的tfree
调用safemoon中的reflectFee() 方法 从而改变rate
此时:
rate = rTotal / tTotal = 12736648300
rTotal = rTotal-rfee(转账消耗) - rfee(系统销毁r币) = 127111750034000
rOwned[A] -= rAmount = 127366483000000 - 1273664830000 = 126092818170000
rOwned[B] += rTransferAmount (扣除税)= 1146298347000
tOwned[A] -= 10000-100 = 9900
tOwned[B] += rTransferAmount/rate = 90
balanceOf(A) = rOwned[A] /rate = 9900
balanceOf(B) = rOwned[B] /rate = 90
此刻
rTotal = 127111750034000
tTotal = 9990
rate = rTotal / tTotal = 12723898902.302301 此时 小于第一次交易的rate
假设用户A向用户C转账100个 t币 tAmount = 100
故:
rAmount = tAmount * rate = 1272389890230
tfee = 10%*tAmount = 10
rfee = tfee *rate = 127238989023
rTransferAmount = rAmount - rfee = 1145150901207.207
此时:
rOwned[A] -= rAmount
rOwned[C] += rTransferAmount (扣除税)
tOwned[A] -= tAmount
tOwned[C] += tAmount - tfee
交易结束后rTotal再销毁 一份rfee 并且记录等值的tfree
调用safemoon中的reflectFee() 方法 从而改变rate
此时指标:
rate = rTotal / tTotal = 12723898902.302301
rTotal = rTotal-rfee(转账消耗) - rfee(系统销毁r币) = 126857272055954
rOwned[A] -= rAmount = 126092818170000 - 1272389890230 = 124820428279770
rOwned[C] += rTransferAmount (扣除税)= 1145150901207.207
tOwned[A] -= 9900-100 = 9800
tOwned[C] += rTransferAmount/rate = 90
balanceOf(A) = rOwned[A] /rate = 9809.919839679378
balanceOf(B) = rOwned[B] /rate =90.09018036072145
balanceOf(C) = rOwned[C] /rate = 90
balanceOf() 函数是由 rOwned[] 和rate 决定,和tOwned[]没有关系。
可以发现此时 B账户的r币没变,但是balanceOf 得到数量余额变多了,这就是通缩燃烧r币导致的代币变多,从而价值提升。
第一步 设置变量
// 用户内部持有的实际币数量,可以看成是每个用户拥有的盘子数量
mapping (address => uint256) private _rOwned;
// 只用于非分红用户的转账,可以看成是一个帮助类
mapping (address => uint256) private _tOwned;
// 类似于ERC20的allowance,指用户授权某些账户的可使用额度
mapping (address => mapping (address => uint256)) private _allowances;
// 账户白名单,用来判断是否需要转账手续费
mapping (address => bool) private _isExcludedFromFee;
// 账户通缩名单标志位,用来判断是否参与通缩分红 true代表排除通缩之外 false 代表进行通缩奖励 默认全员通缩
mapping (address => bool) private _isExcluded;
// 巨鲸黑名单标志位 购买超过总量的1%
mapping (address => bool) public _isExcludedFromAntiWhale;
// 拥有token标志位
mapping (address => bool) private _AddressExists;
// 地址数组
address[] private _addressList;
// 不计入通缩address记录 只有_isExcluded[address]=true的 才加入
address[] private _excluded;
uint256 private constant MAX = ~uint256(0);
//对外展示的币总量,为100000 * 10**12 * 10**9,可以看出是总的蛋糕
uint256 private _tTotal = 100000 * 10**12 * 10**9;
//是内部实际的币总量,为2^256-(2^256%_tTotal),是一个很大的数,可以看出是盘子的数量
uint256 private _rTotal = (MAX - (MAX % _tTotal));
// 收取的手续费,可以看成是打碎了多少盘子,但是不影响总蛋糕_tTotal
uint256 private _tFeeTotal;
// 代币名称
string private _name = "CatGirl";
// 代币符号
string private _symbol = "CATGIRL";
// 代币精度
uint8 private _decimals = 9;
// 转账收取的手续费,这部分手续费会直接销毁,进而导致_rTotal减少,也就是总量的通缩。
uint256 public _taxFee = 4;
// 上一次设置的手续费,是个历史记录
uint256 private _previousTaxFee = _taxFee;
// 转账收取的流动性手续费,这部分手续费会添加到uniswap的交易对里
uint256 public _liquidityFee = 1;
// 上一次设置的手续费,是个历史记录
uint256 private _previousLiquidityFee = _liquidityFee;
// 彩票税
uint256 public _lottoFee = 2;
uint256 private _previousLottoFee = _lottoFee;
// 开发者税
uint256 public _devFee = 1;
uint256 private _previousDevFee = _devFee;
// uniswap的路由器,用于添加流动性
IUniswapV2Router02 public uniswapV2Router;
// 在uniswap的创建的catgirl-bnb交易对
address public uniswapV2Pair;
// 用于lockTheSwap这个modifier,用来加锁的
bool inSwapAndLiquify;
bool inLotteryDraw;
代码核心四个方法:
function _getRate() private view returns(uint256) {
(uint256 rSupply, uint256 tSupply) = _getCurrentSupply();
return rSupply.div(tSupply);
}
function _getCurrentSupply() private view returns(uint256, uint256) {
uint256 rSupply = _rTotal;
uint256 tSupply = _tTotal;
for (uint256 i = 0; i < _excluded.length; i++) {
if (_rOwned[_excluded[i]] > rSupply || _tOwned[_excluded[i]] > tSupply) return (_rTotal, _tTotal);
rSupply = rSupply.sub(_rOwned[_excluded[i]]);
tSupply = tSupply.sub(_tOwned[_excluded[i]]);
}
if (rSupply < _rTotal.div(_tTotal)) return (_rTotal, _tTotal);
return (rSupply, tSupply);
可以看到用的是
function _getRValues(TData memory _data) private pure returns (uint256, uint256, uint256) {
uint256 rAmount = _data.tAmount.mul(_data.currentRate);
uint256 rFee = _data.tFee.mul(_data.currentRate);
uint256 rLiquidity = _data.tLiquidity.mul(_data.currentRate);
uint256 rLotto = _data.tLotto.mul(_data.currentRate);
uint256 rDev = _data.tDev.mul(_data.currentRate);
uint256 rTransferAmount = rAmount.sub(rFee).sub(rLiquidity).sub(rLotto).sub(rDev);
return (rAmount, rTransferAmount, rFee);
receive() external payable {}
function _reflectFee(uint256 rFee, uint256 tFee) private {
_rTotal = _rTotal.sub(rFee);
_tFeeTotal = _tFeeTotal.add(tFee);
}
每次交易都会调用该方法,通缩减少rFee个r币
_rTotal = _rTotal.sub(rFee);
function balanceOf(address account) public view override returns (uint256) {
// 如果account在非通缩分红名单中 直接返回_tOwned[account] 否则进行通缩计算新值
if (_isExcluded[account]) return _tOwned[account];
return tokenFromReflection(_rOwned[account]);
}
function tokenFromReflection(uint256 rAmount) public view returns(uint256) {
require(rAmount <= _rTotal, "Amount must be less than total reflections");
uint256 currentRate = _getRate();
return rAmount.div(currentRate);
}
balanceOf(address) = _rOwned[account].div(currentRate)
https://its201.com/article/zgsdzczh/116706584
https://reflect-contract-doc.netlify.app/
https://ethereum.stackexchange.com/questions/98622/binance-smart-chain-tokens-what-are-ttotal-rtotal-tsupply-rsupply-rowned-t