Skip to content

Check tx.origin in the functions where the _value is taken from the approval #1330

Closed
@rjkz808

Description

🎉 Description

So, in the transferFrom, burnFrom or other functions in the ERC20 token smart-contracts we need to check the tx.origin balance

  • 🐛 This is a bug report.
  • 📈 This is a feature request.

💻 Environment

Next, we need to know what your environment looks like.

  • Which version of OpenZeppelin are you using?
  • openzeppelin-solidity ^1.12.0
  • What network are you deploying to? Ganache? Ropsten?
  • I'm using Ganache, but you can deploy to any existing network
  • How are you deploying your OpenZeppelin-backed contracts? truffle? Remix? Let us know!
  • You can use either Truffle or Remix

📝 Details

If you want for example add a refund to the allowance crowdsale, you cannot normally do this, before the msg.sender approves the crowdsale contract to spend all it's tokens. When you call the transfer the msg.sender always will be the address(this).

🔢 Code To Reproduce Issue [ Good To Have ]

Just try to write something like this

contract ExampleCrowdsale {
  using SafeERC20 for ERC20

  // I've skipped a part of crowdsale

  function buyTokens(address _beneficiary) public payable {
    require(_beneficiary != address(0));
    require(msg.value > 0);
    require(hasStarted() && !hasEnded());
    require(weiRaised.add(msg.value) <= cap);
    require(_getTokenAmountWithBonus(msg.value) <= saleAmount);

    weiRaised = weiRaised.add(msg.value);
    token.safeTransferFrom(
      owner,
      _beneficiary,
      _getTokenAmountWithBonus(msg.value)
    );

    emit TokenPurchase(
      msg.sender, 
      _beneficiary, 
      msg.value, 
      _getTokenAmountWithBonus(msg.value)
    );
  }
}

then add the refund function

function refund() public {
  require(!goalReached() && isFinalized);
  msg.sender.transfer(paidAmountOf[msg.sender]);
  token.safeTransfer(address(this), token.balanceOf(msg.sender));
}

but it will always revert. We can do something like

function refund() public {
  require(!goalReached() && isFinalized);
  require(token.allowance(msg.sender, address(this) == token.balanceOf(msg.sender)));
  msg.sender.transfer(paidAmountOf[msg.sender]);
  token.safeTransferFrom(msg.sender, address(this), token.balanceOf(msg.sender));
}

but it requires one more transaction from the user.

👍 Other Information

Solution

You need to add 4 lines to the transferFrom and other ERC20 functions where the value is taken from the approval

function transferFrom(
  address _from,
  address _to,
  uint256 _value
)
  public
  returns (bool)
{
  require(_value <= balances[_from]);
  require(_to != address(0));
  
  if (_from != tx.origin) {
    require(_value <= allowed[_from][msg.sender]);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
  }
  balances[_from] = balances[_from].sub(_value);
  balances[_to] = balances[_to].add(_value);
  emit Transfer(_from, _to, _value);
  return true;
}

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions