How to split each ERC20/ERC777 transfer 3 ways?

I am trying to split every transaction with % burn then a % to another address ontop of the address the transaction is going to. I am tyring to do this without having to edit the ERC source contract. At first I thought about using _beforeTokenTransfer hook to do so, but looking at the source, when I call _burn it will cause a loop as _burn also calls _beforeTokenTransfer. Then for sending to a second address within that hook will recurse as well to _beforeTokenTransfer. If I override these functions I still have the problem of _balances being private. The docs say not to copy code from the source or edit the source, so i am trying to not do that. How can I got about this using hooks or should I override functions and make _balances variable public/internal so I can edit is as I need to in my token contract? Seem like I will be editing the open zepplin contract.

1 Like

I think this answer can give you some idea.

1 Like

lol thanks, I had my focus on editing the _balances manually but then I noticed the lower level calls will work for burn and staking, then overrides on the main call. I think I got it now using that example, using ERC777 I ended up with this code. Not tested yet.

function send(
    address recipient,
    uint256 amount,
    bytes memory data
) public override {
    super.send(recipient, split(amount), data);
}

function transfer(address recipient, uint256 amount)
    public
    override
    returns (bool)
{
    super.transfer(recipient, split(amount));
}

function operatorSend(
    address sender,
    address recipient,
    uint256 amount,
    bytes memory data,
    bytes memory operatorData
) public override {
    super.operatorSend(
        sender,
        recipient,
        split(amount),
        data,
        operatorData
    );
}

function transferFrom(
    address holder,
    address recipient,
    uint256 amount
) public override returns (bool) {
    super.transferFrom(holder, recipient, split(amount));
}

function split(uint256 _amount) internal returns (uint256) {
    uint256 remainingAmount = _amount;

    if (burnPercentage > 0 && totalSupply() > minimumSupply) {
        uint256 burnAmount = _amount.mul(burnPercentage).div(1000);
        uint256 availableBurn = totalSupply().sub(minimumSupply);
        if (burnAmount > availableBurn) {
            burnAmount = availableBurn;
        }
        remainingAmount = remainingAmount.sub(burnAmount);
        _burn(msg.sender, burnAmount, "", "");
    }

    if (isStakingActive && stakePercentage > 0) {
        uint256 tokensToStakePool = _amount.mul(stakePercentage).div(1000);
        remainingAmount = remainingAmount.sub(tokensToStakePool);
        _send(
            msg.sender,
            stakingPoolAddress,
            tokensToStakePool,
            "",
            "",
            false
        );
    }

    return remainingAmount;
}

I still had to go into the ERC777 and make these functions virtual override to do it the way I did. If there is another way let me know.

1 Like

Hi @niZmo,

Welcome to the community :wave:

I also suggest looking at: Points to consider when creating a fungible token (ERC20, ERC777)

Especially: :warning: If you create a fee on transfer or deflationary token (burn a percentage on transfer) this can cause issues when used with other contracts such as: https://medium.com/balancer-protocol/incident-with-non-standard-erc20-deflationary-tokens-95a0f6d46dea

1 Like

Hi @niZmo,

Which functions did you need to make virtual?

There is an open issue on making some functions virtual: https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2154

Is this hack a thing on Uniswap pools as well? Haven’t heard of it happening on there. There are many deflationary tokens now too.

1 Like

All the functions I called super on only had override in the base contract and I couldn’t reach them until I added virtual. I may be wrong on it, my IDE was acting funky, an I have went back to working on the dapp, putting the token to the side for now. I really want to use ERC777 as it will give me a lot of flexibility, as I can manage the users funds in different ways, but the reentracy attacks on uniswap with the hooks scare me. May go to an ERC20, but the 777 is really interesting, and I am sure it would be the popular choice if not for the reentrancy attacks.

1 Like

Hi @niZmo,

I only found the following on Uniswap: https://uniswap.org/faq/#why-is-my-swap-failing-or-stuck
I would suggest checking with Uniswap if your token would cause issues.

You should check each service you want to use your token with to see if this is going to cause an issue.

You need to take reentrancy into account and have appropriate testing and auditing.