1

I'm so confused on how approve functions works:

If myToken contract has an approve function but the owner did not approve the contract B. Then contract B deploys a function and create a function that calls mytoken.approve of a user then transfer user tokens to contract B. Is it possible?

For example myToken contract is a ERC20 Contract.

Can I create a contract StealTokens to approve and transfer tokens from myToken contract?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract MyToken {
    string public name = "MyToken";
    string public symbol = "MT";
    uint8 public decimals = 18;
    uint256 public totalSupply;
    address public owner; // The owner of the token contract

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor(uint256 initialSupply) {
        owner = msg.sender;
        totalSupply = initialSupply * 10**uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
    }

    function transfer(address to, uint256 value) public returns (bool) {
        require(to != address(0), "ERC20: transfer to the zero address");
        require(balanceOf[msg.sender] >= value, "ERC20: insufficient balance");

        balanceOf[msg.sender] -= value;
        balanceOf[to] += value;

        emit Transfer(msg.sender, to, value);
        return true;
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        require(balanceOf[from] >= value, "ERC20: insufficient balance");
        require(allowance[from][msg.sender] >= value, "ERC20: allowance exceeded");

        balanceOf[from] -= value;
        balanceOf[to] += value;
        allowance[from][msg.sender] -= value;

        emit Transfer(from, to, value);
        return true;
    }
}

1 Answer 1

1

the ERC-20 approve() function relies on the msg.sender value to decide who to approve the tokens on behalf of, this means that the person that calls approve() can only allow other users to spend their balance, not any one else's.

Look at the very first line of code inside the approve() function:

allowance[msg.sender][spender] = amount;

You can see that this sets the allowance value at msg.sender for the spender.

For example If Alice calls approve() with Bob's address as the spender and 100 as the amount, the amount of tokens Bob is allowed to spend on behalf of Alice is set to 100.

You can test the effects of approve() by using the view function allowance(), this function allows anyone to view how many tokens a spender is allowed to take/spend on behalf of the owner. To fit our previous example, if you called allowance(), with Alice's address as the owner and Bob's address as the spender, you should see 100 returned as the result since Alice just approved Bob to spend that.

allowance(aliceAddr, bobAddr) public view returns(remaining)

remaining is the amount that can be spent, aka the value at allowance[aliceAddr][bobAddr]

To put this in perspective for you, if you wanted to create a malicous approve() function that could set the allowance for any user by and user, it would be as simple as adding an additional input param to the function, or even just changing the function logic a little:

Malicious approve with new input:

function approve(address owner, address spender, uint256 amount) public returns (bool) {
        allowance[owner][spender] = amount;
        emit Approval(owner, spender, amount);
        return true;
    }

Malicious approve with changed input:

function approve(address owner, uint256 amount) public returns (bool) {
        allowance[owner][msg.sender] = amount;
        emit Approval(owner, msg.sender, amount);
        return true;
    }

Both of these malicious approve() functions allow anyone to increase their allowance on behalf of another user, this is behavior that is obviously unwanted in most cases.

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.