ERC20 Tokens – How to Send ERC20 Tokens Owned by Contract but Not by msg.sender

erc-20vyper

I have two methods: sendfromself() and getbalance()

The only difference is that the first method, checks if the msg.sender has any coins in the ERC20 address supplied in the method's arguments, and if so, transfers that balance to the contract.

The second method, checks if the contract has any ERC20 coins and if so, transfers these coins to a nominated address.

Strangely the second method works, the first one sendfromself() does not. For the avoidance of doubt, I have invoked the ERC20 approve() method with the address of this contract.

What am I doing wrong? Why doesn't the first method send me msg.sender's ERC20 coins?

from vyper.interfaces import ERC20


event balancex:
    balancex: uint256
    
other: constant(address) = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db


@payable
@external
def sendfromself(_coin: address) -> bool:

    amount: uint256 = ERC20(_coin).balanceOf(msg.sender)
    log balancex(amount)
    if amount > 0:
        ERC20(_coin).transferFrom(msg.sender, self, amount)
    return True

@payable
@external
def getbalance(_coin: address) -> bool:

    amount: uint256 = ERC20(_coin).balanceOf(self)
    response: Bytes[32] = raw_call(
        _coin,
        concat(
            method_id("transfer(address,uint256)"),
            convert(other, bytes32),
            convert(amount, bytes32),
        ),
        max_outsize=32,
    )
    if len(response) != 0:
        assert convert(response, bool)

    return True

Best Answer

For transferFrom(from, to, amount) to work, the calling address (msg.sender) need to have enough token allowance assinged by the from address, even if the from address was the calling address itself. This might be counter-intuitive, as why should the calling address have approval to use its own token balance? But apparently that's how ERC20 is implemented. So you should proceed this line with:

ERC20(_coin).approve(msg.sender, amount)
Related Topic