Solidity ERC-20 – Logic Behind Token Reflections

erc-20solidity

I've noticed that a lot of tokens that implement reflections come from safemoon. And I was curious about how safemoon did reflections. I expected to find a for loop somewhere where they are adding to each account's balance, but I did not find anything like that. Instead I foud _rOwned and _tOwned. I kept researching and I came across RFI the contract that started reflections (correct me if I am wrong). But even still I did not really understand how the accounts recieve more tokens? I found their reflect function. (in safemoon its called deliver)

    function deliver(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);
    }

But after finding this I only had more questions than anserws. Who is calling this function? Why is it not used anywhere in the source code? Another function I found that was just as confusing was reflectionFromToken()

    function reflectionFromToken(uint256 tAmount, bool deductTransferFee) public view returns(uint256) {
        require(tAmount <= _tTotal, "Amount must be less than supply");
        if (!deductTransferFee) {
            (uint256 rAmount,,,,,) = _getValues(tAmount);
            return rAmount;
        } else {
            (,uint256 rTransferAmount,,,,) = _getValues(tAmount);
            return rTransferAmount;
        }
    }

Once again where is the function being called? What is it even for? Thanks for the help!

Best Answer

I have also being studying the Safemoon token's source code.

  1. deliver function can only be called by non excluded addresses (contract's and contract owner's addresses), and implies a kind of token burning. I suppose is to be called by anyone who wants to "burn" tokens, especially for project team.

Look at these 2 lines of code in deliver function:

_rOwned[sender] = _rOwned[sender].sub(rAmount);
_rTotal = _rTotal.sub(rAmount);

Substract rAmount from sender's r balance and from rTotal (r supply). From _getRValues functions:

rAmount = tAmount.mul(currentRate);

And currentRate is just rSupply/tSupply in most of cases (except for excluded addresses).

  1. I'm not sure who call reflectionFromToken, but it's just a view function (it does not modify any value contract's state).

  2. Take a look to balanceOf and tokenFromReflection functions to properly understand the reflection and how the fee sharing is done across all the addresses.

Each holder's balance in the current moment is calculated in this way:

function balanceOf(address account) public view override returns (uint256) {
    if (_isExcluded[account]) return _tOwned[account];
    return tokenFromReflection(_rOwned[account]);//=> rAmount/currentRate
}

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);
}
Related Topic