0

Looking for assistance on what I'm doing wrong.

I have a simple ERC20 contract I built from scratch for practice interacting with a simple vendor contract that allows someone to buy and sell tokens. My issue is with the sell logic.

  1. Deploy both contracts foWERC20.sol and Vendor.sol
  2. Send tokens (500 or more) from the minter account to the vender.sol contract
  3. Purchased tokens from Vender.sol in quantities of 500 with an address that has no tokens
  4. Attempts to sell tokens (500 at a time) back to Vendor.sol in exchange for ether

Problem:

When sellToken() is called from Vendor.sol on line 39, it triggers 2 external contracts inside of foWERC20.sol called approve() and than transferFrom().Inside of approve(), This function is supposed to set x amount of tokens another address has the rights of moving out of the callers wallet.

Inside of transferFrom is where the flag happens. line 100 require() does not pass and triggers the warning. require(approvedAmount >= _value, "Insufficent allowance");

So it seems the approved amount is not being set

Vendor.sol

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import "./FoWERC20.sol";

//@Dev this contract allows buying and selling of the FOW token along with the withdrawl
//of ETH paid in exchange for FOW


contract Vendor is Ownership {
    
    address vendorOwner;

    FoWERC20 fowERC20; // Custom FOW ERC20 Contract address

    event BuyTokenEvent(address indexed purchasedBy, address indexed purchasedFrom, uint256 ethValue);
    event SellTokenEvent(address indexed soldBy, address indexed soldTo, uint256 ethValue);
    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    constructor(address _fowERC20){
        vendorOwner = msg.sender;
        fowERC20 = FoWERC20(_fowERC20);
        
    }

    function buyToken() external payable returns(bool success){
        //tokens per 1 ether
        uint256 tokensPerEther = 500;
        //check if contract caller has enough funds
        require(msg.value >= 1 ether, "Not enough funds");
        //check if vender contract has enough tokens to sell
        require(fowERC20.balanceOf(address(this)) >= tokensPerEther, "Not enough Tokens");
        //create the transfer of tokens in exchange for ether 
        (bool _success) = fowERC20.transfer(msg.sender, (msg.value / 1 ether) * tokensPerEther);      
        emit BuyTokenEvent(msg.sender, address(this), msg.value);
        return _success;
    }

    function sellToken(uint256 howManyTokens) external payable returns(bool){
        //make selling sell in units of 500 tokens
        require(howManyTokens % 500 == 0, "Must sell in quantities of 500");
        //check if contract has a min amount of eth to trade for sale of tokens 
        require(address(this).balance >= 1.1 ether, "Contract balance has less than 1.1 ether");
        //proved address and amount allowed to be extracted from sellers address
        fowERC20.approve(address(this), howManyTokens);
        //transfer tokens from caller(seller) to contract 
        fowERC20.transferFrom(msg.sender, address(this), howManyTokens);
        //caller(seller) pays contract in ether
        (bool success,) = msg.sender.call{value: (howManyTokens * 1e18 )}("");
        emit SellTokenEvent(msg.sender, address(this), msg.value);
        return success;
    }

}

foWERC20.sol

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

abstract contract ERC20{
    function name() external virtual returns (string memory);
    function symbol() external virtual  returns (string memory);
    function decimals() external virtual  returns (uint8);
    function totalSupply() external virtual  returns (uint256);
    function balanceOf(address _owner) external virtual view returns (uint256 balance);
    function transfer(address _to, uint256 _value) external virtual returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) external virtual returns (bool success);
    function approve(address _spender, uint256 _value) external virtual returns (bool success);
    function allowance(address _owner, address _spender) external virtual view returns (uint256 remaining);

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);


    }

    contract Ownership{

        address public contractOwner;
        address public newContractOwner;

        event changeOfOwnershipEvent(address indexed changeFrom, address indexed changeTo);

        constructor(){
            contractOwner = msg.sender;
        }

        function changeOwnership(address _newContractOwner) external returns(bool success){
            require(msg.sender == contractOwner, "Not contract owner");
            newContractOwner = _newContractOwner;
            emit changeOfOwnershipEvent(msg.sender, newContractOwner);
            return true;
        }

        function approveOwnershipChange() external returns(bool success){
            require(msg.sender == newContractOwner, "Not the new contract owner");
            contractOwner = newContractOwner;
            newContractOwner = address(0);
            return true;
        }
    }


    contract FoWERC20 is ERC20, Ownership{

        string _name;
        string _symbol;
        uint8 _decimals;
        uint256 _totalSupply;
        address public _minter;
        
        mapping(address => uint256) public _balance;
        mapping(address => mapping( address => uint256)) public _allowance; 

        constructor(string memory name_, 
                    string memory symbol_,
                    uint8 decimals_,
                    uint256 totalSupply_,
                    address minter_){

            _name = name_;
            _symbol = symbol_;
            _decimals = decimals_;
            _totalSupply = totalSupply_;
            _minter = minter_;           
            _balance[minter_] = totalSupply_;

        }


        function name() public override view returns (string memory){
            return _name;
        }

        function symbol() public override view returns (string memory){
            return _symbol;

        }
        function decimals() public override view returns (uint8){
            return _decimals;
        }

        function totalSupply() public override view returns (uint256){
            return _totalSupply;
        } 

        function balanceOf(address _owner) external override view returns (uint256 balance){
            return _balance[_owner];
        }

        function ethBalance(address _address) external view returns(uint){
            return _address.balance;
        }

        function transfer(address _to, uint256 _value) external override returns (bool success){
            require(_balance[msg.sender] >= _value, "Transfer(): Insufficent Funds");
            _balance[msg.sender] -= _value;
            _balance[_to] += _value;
            emit Transfer(msg.sender, _to, _value);
            return true;
        }

        function transferFrom(address _from, address _to, uint256 _value) external override returns (bool success){
        //grabs the approved amount spender is allowed to control from mapping _allowance
        uint256 approvedAmount = _allowance[_from][_to];
        //checks approvedAmount is greater or equal to the value being asked to be removed
        require(approvedAmount >= _value, "Insufficent allowance");
        //updateds allowance to stop reentry exploit
        _allowance[_from][_to] -= _value;
        _balance[_from] -= _value;
        _balance[_to] += _value;
        emit Transfer(_from, _to, _value);
        return true;
    }
        function approve(address _spender, uint256 _value) external override returns (bool success){
            require(_balance[msg.sender] >= _value, "Insufficent Funds");
            _allowance[msg.sender][_spender] = _value;
            emit Approval(msg.sender, _spender, _value);
            return true;

        }
        function allowance(address _owner, address _spender) external override view returns (uint256 remaining){
            return _allowance[_owner][_spender];
        }

        function mintTokens(uint256 _mintIncreaseBy) public returns(bool success){
            require(msg.sender == _minter, "Not the minter");
            _balance[msg.sender] += _mintIncreaseBy;
            _totalSupply = _balance[_minter];
            return true;
        }

         function burnTokens(uint256 _mintDecreaseBy) public returns(bool success){
            require(msg.sender == _minter || msg.sender == contractOwner, "Not the minter");
            _balance[_minter] -= _mintDecreaseBy;
            _totalSupply = _balance[_minter];
            emit Transfer(_minter, address(0), _mintDecreaseBy);
            return true;
        }

        function emergencyWithdrawAll() external returns(bool success){
            uint256 _value = _balance[_minter];
            require(msg.sender == contractOwner, "Not contract owner");
            _balance[contractOwner] += _value;
            _balance[_minter] -= _value;
            emit Transfer(_minter, contractOwner, _value);
            return true;
        }
    }

1 Answer 1

0

ERC20 method approve(address _spender, uint256 _value) receives spender and value.

Method sellToken first calls fowERC20.approve(address(this), howManyTokens), meaning it tells the token to allow itself to spend tokens.

Next line it calls fowERC20.transferFrom(msg.sender, address(this), howManyTokens) which tells the token to transfer from msg.sender to this contract tokens, but contract doesn't have permission to spend msg.sender's tokens.

msg.sender needs to call fowERC20.approve(vendorAddress, howManyTokens) before calling sellToken for it to be able to access his tokens.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.