In short, reflect contracts are tokens that have auto-staking mechanism built in. Safemoon, Octa, Lunarswap tokens, are some examples.
I decided to write the technical paper because there wasn’t one. In fact, if you look at the program , it is very hard to read and understand. I spent some time digging it to the program, tried to understand how it works, and then finally wrote the paper.
I think there are some grammar mistakes and some of the equations may be wrong.
Any feedback is welcome! And feel free to make Pull Request!
Thank you for taking time to write this paper. It really helped me to understand reflect algo. On your paper, you mentioned
RFI contracts has one fundamental issue over including users into staking. The conversion rate between
t-values and r-values is calculated using equa!on (5). Here, it is excluding non-stakers out of equa!on in
order to fully reward the stakers. However, when a new user gets included into staking, the rate updates,
thereby changing all stakers balance. Using this feature, it can be used to rug-pull rewards from stakers
by including a whale.
Could you elaborate more and give an example how rug-pull can happen? Thanks
What happens is that, when you include a user to staking (exclude from excludeStaking mapping), the conversion rate between t-value and r-value changes (look at equation 5). This results in distributing the reward also to the new user because stakers balance is calculated like this: balance[user] = rOwned[user] / rate. If you include a whale (maybe dev wallet), you will be able to take most of users' reward.
Thank you for taking time to write this article,
In reflect contact there is a function called reflect:
function reflect(uint256 tAmount) public {
address sender = _msgSender();
require(!_isExcluded[sender], "Excluded addresses cannot call this function");
(uint256 rAmount,,,,) = _getValues(tAmount);
_rOwned[sender] = _rOwned[sender].sub(rAmount);
_rTotal = _rTotal.sub(rAmount);
_tFeeTotal = _tFeeTotal.add(tAmount);
Can you please help me to understand why they calculate _rTotal like this:
_rTotal = _rTotal.sub(rAmount);
Another question is, In your paper, You assume that tTotal is constant, What if we want to burn some portion of fees like Safemoon (I meen real burn, Not like Safemoon that transfers burn portion to the address that receives profits from transaction fees), Should we subtract burn fees from tTotal in equation no. 4?
I don't really know why this function exists. Because it's not being called anywhere from the reflect contract code.
But looking at the code, it distributes tAmount of tokens to other holders from the sender.
_rTotal = _rTotal.sub(rAmount) is same as _rTotal -= rAmount, it's just that they are using safemath library. But you don't need to write it like this if you are using solidity >=0.8
If you want to burn tokens, you should also subtract from tTotal because tTotal is basically the total supply.
Enjoyed reading your technical white paper. Trying to follow your 'Deflationary mechanism' mathematical equations. Does it prove mathematically that assigning _rTotal = _rTotal.sub(rFee) during every transfer would theoretically never reach 0?
It's not a full mathematical proof but we can assume that rTotal will never go to 0 if you look at equation (4). rTotal is always multiplied by a number that is bigger than 0 and smaller than 1.
i have read your paper after spending hours studying the contract myself. It is really a great work. However, i am confused about something.
Since the rsupply is constantly decreasing at each transaction by substracting rFee, isn't the current rate eventually going to be inferior to 1 once rsupply will be below tsupply, and isn't that going to be a big issue since solidity can't handle numbers inferior to 1 and will therefore display 0? Am i missing a mathematical thing here? So isn't this type of contract limited in its life expectancy by definition?
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);
}
i am also confused about the verifications here, making sure thatt rSupply is inferior to rTotal. divided by T.
@Yann_N From the paper, the equation rTotal = MAX − (MAX mod tTotal) is equal to MAX − (MAX − q ⋅ tTotal); where, there is a q such that (MAX - q * rTotal) is equal to the remainder from (MAX mod tTotal). This q is conditioned 1 <= q <= MAX, so the ratio is will never be inferior to 1.