🐶
Solidity

Understanding ERC20 approve() and allowance() Methods

By Ondřej Dolanský on 11/27/2024

This article explains the inner workings of approve and allowance methods in ERC20 tokens, shedding light on how they facilitate secure token transfers within the Ethereum ecosystem.

Understanding ERC20 approve() and allowance() Methods

Table of Contents

Introduction

In the world of Ethereum and ERC20 tokens, you have control over your digital assets. But what happens when you want a smart contract to use some of your tokens on your behalf? This is where the concepts of approve and allowance become crucial for managing permissions and ensuring the security of your tokens.

Step-by-Step Guide

Let's break down ERC20 approvals and allowances:

Imagine this: You have some ERC20 tokens (like digital money). You want a smart contract (a program on the blockchain) to spend some of them on your behalf, but not all.

Here's where approve and allowance come in:

  1. approve(spender, amount): This is your action. You use it to give permission ("approval") to another address (spender) to use a specific amount of your tokens.

    // You give permission to the "exchangeContract"
    // to spend up to 100 of your tokens
    myToken.approve(exchangeContract, 100); 
  2. allowance(owner, spender): This function lets anyone check how many tokens a spender is allowed to use from an owner.

    // Check how much the "exchangeContract"
    // is allowed to spend from your account
    uint256 allowedAmount = myToken.allowance(msg.sender, exchangeContract); 
  3. transferFrom(from, to, amount): The spender (e.g., the exchange contract) uses this function to actually move your tokens. Important:

    • The spender can only transfer up to the allowance you set.
    • The from address must be your address (the one that gave approval).
    // Inside the "exchangeContract" (simplified):
    function buySomething(address _from, uint256 _amount) public {
        // ... some logic ...
    
        // Transfer tokens from the user to the contract
        myToken.transferFrom(_from, address(this), _amount); 
    
        // ... more logic ...
    }

Key Points:

  • Security: Approving a large amount and leaving it can be risky. If the spender contract is compromised, you could lose tokens.
  • Best Practice: Consider approving only the exact amount needed for a specific transaction.
  • Advanced: Functions like increaseAllowance and decreaseAllowance exist to manage allowances more safely.

Code Example

This codebase demonstrates a basic ERC20 token implementation and its interaction with an example exchange contract. The MyToken.sol contract defines the ERC20 token with standard functions like transfer, approve, transferFrom, and allowance. The Exchange.sol contract simulates an exchange platform that utilizes the ERC20 token. It showcases the use of the transferFrom function to spend user-approved tokens for a hypothetical purchase scenario. The example emphasizes the importance of the approve function, where users grant permission to the exchange contract to spend a specified amount of their tokens. This pattern highlights a common practice in decentralized finance (DeFi) for interacting with ERC20 tokens in a secure and controlled manner.

This example demonstrates a simple ERC20 token (MyToken) and how to use approve, allowance, and transferFrom for interacting with it.

1. MyToken.sol (ERC20 Token Contract):

pragma solidity ^0.8.0;

// Import the ERC20 interface from OpenZeppelin
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";

contract MyToken is IERC20 {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;
    string private _name;
    string private _symbol;

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _totalSupply = 1000 * 10**18; // 1000 tokens with 18 decimals
        _balances[msg.sender] = _totalSupply;
    }

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

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

    function decimals() public pure override returns (uint8) {
        return 18;
    }

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

    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        _transfer(msg.sender, to, amount);
        return true;
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public override returns (bool) {
        address spender = msg.sender;
        uint256 currentAllowance = _allowances[from][spender];
        require(currentAllowance >= amount, "ERC20: insufficient allowance");
        unchecked {
            _approve(from, spender, currentAllowance - amount);
        }
        _transfer(from, to, amount);
        return true;
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

2. Exchange.sol (Example Contract Using Approvals):

pragma solidity ^0.8.0;

// Import the ERC20 interface
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";

contract Exchange {
    IERC20 public myToken; // Instance of your ERC20 token

    constructor(address _tokenAddress) {
        myToken = IERC20(_tokenAddress);
    }

    function buySomething(uint256 _amount) public {
        // ... some logic ...

        // Transfer tokens from the user to the contract
        require(myToken.transferFrom(msg.sender, address(this), _amount), "Transfer failed");

        // ... more logic ...
    }
}

Explanation:

  • MyToken.sol: This contract defines a basic ERC20 token with standard functions like transfer, approve, transferFrom, allowance, etc.
  • Exchange.sol: This contract simulates an exchange that interacts with your ERC20 token. The buySomething function demonstrates how to use transferFrom to spend user-approved tokens.

How to Use:

  1. Deploy MyToken.sol: This will create your ERC20 token.
  2. Deploy Exchange.sol: Pass the address of your deployed MyToken contract to the constructor.
  3. Approve Tokens: Call the approve function on your MyToken contract. Specify the Exchange contract address as the spender and the desired token amount.
  4. Interact with Exchange: Call the buySomething function on the Exchange contract. The contract will then be able to use transferFrom to spend your approved tokens.

Important Notes:

  • This is a simplified example. Real-world implementations will have more complex logic and security considerations.
  • Always thoroughly test your contracts and use reputable auditing services before deploying to a live environment.
  • Educate yourself about the risks associated with approving tokens and use best practices like approving only the necessary amount for each transaction.

Additional Notes

Understanding the Risks:

  • Unlimited Allowance: Setting a very high allowance (or not setting a limit) can be dangerous. If the spender contract is compromised, the attacker could drain your entire approved balance.
  • Front-Running: Malicious actors could potentially "front-run" your transactions. For example, if you approve a large amount, an attacker could use bots to monitor the blockchain, see your approval, and quickly execute a trade before you, potentially manipulating the price.

Mitigating Risks:

  • Approve Only What's Needed: The safest practice is to approve the exact amount of tokens required for a specific interaction with a contract.
  • Revoke Approvals: If you no longer need a contract to spend your tokens, revoke the approval by setting the allowance to 0.
  • Use increaseAllowance and decreaseAllowance: These functions provide a safer way to adjust allowances without having to revoke and re-approve. They help prevent certain types of front-running attacks.
  • Token Approval Management Tools: Several tools and browser extensions can help you manage your ERC20 token approvals, providing an overview of your allowances and allowing you to revoke them easily.

Beyond the Basics:

  • ERC20 Approval Standards: While approve and transferFrom are standard ERC20 functions, the functions increaseAllowance and decreaseAllowance are not strictly part of the original ERC20 standard. However, they are widely adopted and considered best practice for improved security.
  • Gas Optimization: Approving tokens consumes gas. Approving a large amount once might seem efficient, but if you interact with the contract frequently, it might be more gas-efficient to approve smaller amounts as needed.
  • Smart Contract Auditing: Before interacting with any smart contract, especially those that require token approvals, it's crucial to ensure the contract has been audited by reputable security firms to minimize the risk of vulnerabilities.

Remember: Understanding ERC20 approvals is essential for safely navigating the world of decentralized applications (dApps) and DeFi. Always be cautious, do your research, and prioritize the security of your digital assets.

Summary

This table summarizes the key concepts of ERC20 token approvals and allowances:

Concept Description Your Role Spender's Role
approve(spender, amount) Grants permission to a spender (address) to use a specific amount of your tokens. You call this function to set the allowance. Receives the allowance.
allowance(owner, spender) Checks the remaining amount of tokens a spender is allowed to use from an owner. You can use this to verify the allowance you set. Anyone can call this function to check the allowance.
transferFrom(from, to, amount) Used by the spender to transfer tokens from the owner (you) to another address. Your address is used as the from address. Spender calls this function to move your tokens.

Security Considerations:

  • Approving large amounts and leaving them unused can be risky.
  • Best practice is to approve only the exact amount needed for a transaction.

Advanced Features:

  • increaseAllowance and decreaseAllowance offer more granular control over allowances.

Conclusion

Understanding and properly managing ERC20 token approvals is crucial for secure participation in the decentralized world. By grasping the roles of approve, allowance, and transferFrom, users can confidently interact with smart contracts while minimizing the risks associated with token permissions. Remember to follow best practices like approving only the necessary amounts, revoking unused allowances, and utilizing advanced features like increaseAllowance and decreaseAllowance for enhanced security. As you explore the exciting possibilities of dApps and DeFi, prioritize the safety of your digital assets by staying informed and adopting secure practices.

References

  • ERC20 Decoded: Understanding approvals, allowances and ... ERC20 Decoded: Understanding approvals, allowances and ... | Recently I joined Zeal, a crypto wallet startup 🎉 I have been doing deep dives into the crypto world and I must say, it has been a refreshing experience and a good break from management as life at home ramped up (another addition to the family! 🐣 ) In the next few weeks, I will be sharing some of
  • solidity - Understanding Approve and TransferFrom - Confused ... solidity - Understanding Approve and TransferFrom - Confused ... | Feb 21, 2023 ... As described in the ERC-20 standard: The approve(address _spender, uint256 _value) method allows the spender _spender , in this case Alice, ...
  • ERC-20's approve/transferFrom security concerns for the ICRC-1 ... ERC-20's approve/transferFrom security concerns for the ICRC-1 ... | Hi everyone, Yesterday during the Ledger&Tokenization Working Group we discussed ERC-20 the approve/transferFrom flow and its issues. Only 16 people participated in the meeting so I thought it could be useful to have a conversation about the topic here on the forum. For context, on Friday we asked people whether ERC-20 like approve/transferFrom should be added to the ICRC-1 Token Standard and half of the people voted against it. If we had to make a decision right now we would not include appro...
  • IERC20 approve and Allowance not matching - Contracts ... IERC20 approve and Allowance not matching - Contracts ... | Hey, so i'm writing a smart contract for a staking pool but i have been stuck for days with the exactly same problem. I keep getting "insufficient allowance" when trying to use my contract to transfer to another address on behalf of the client. The thing is that my contract has run IERC20.approve() method and it returns a true. I tried debugging and found that even though the approve method works. the allowance for some reason is still 0. No matter the amount i placed on the approve method. Is ...
  • ERC 20 - OpenZeppelin Docs ERC 20 - OpenZeppelin Docs | Emitted when the allowance of a spender for an owner is set by a call to approve . ... Finally, the non-standard decreaseAllowance and increaseAllowance ...
  • Help - ERC20: transfer amount exceeds allowance. : r/ethdev Help - ERC20: transfer amount exceeds allowance. : r/ethdev | Posted by u/Ok_Cry_5566 - No votes and 6 comments
  • increaseAllowance and decreaseAllowance ERC20 - Support ... increaseAllowance and decreaseAllowance ERC20 - Support ... | We all know the problem ERC20 has about front-running approve, so I won't go into explaining this. in OZ v4, increaseAllowance and decreaseAllowance was added to mitigate this and honestly, the way I look at these functions, they really solved the problem. Assume Bob has 100 tokens allowed to spend by Alice. If Alice wants to update the allowance to 150, she calls increaseAllowance(50). Even if Bob front-runs this and calls transferFrom beforehand, Bob will only be able to end up in the end wi...
  • ERC827 Token Standard (ERC20 Extension) · Issue #827 ... ERC827 Token Standard (ERC20 Extension) · Issue #827 ... | EIP: 827 Title: ERC827 Token Standard (ERC20 Extension) Author: Augusto Lemble me@augustol.com Type: Token Standard Status: Draft Category: ERC Created: 2018-01-11 Updated: 2019-05-21 This standa...
  • ERC 20 - OpenZeppelin Docs ERC 20 - OpenZeppelin Docs | Finally, the non-standard decreaseAllowance and increaseAllowance functions ... Not doing so will make the contract unpausable. Functions.

Were You Able to Follow the Instructions?

😍Love it!
😊Yes
😐Meh-gical
😞No
🤮Clickbait