ERC20 Token Allowances

Published: April 13, 2018, updated: January 4, 2025

Update from 2025: the cryptocurrency boom of 2017 and 2017 and ICOs were an interesting time, huh? It’s not my cup of tea anymore. At the same time, I’d like for this post to stand here as a historical artifact, and encourage you to spend your time on more worthwhile things instead.

Thank you for reading my disclaimer. Please enjoy the following post:

Have you ever wondered how your users can spend ERC20 platform tokens to buy assets and other tokens from you? Or—in more technical terms— how to make sure a smart contract executes a particular function as soon as it receives tokens? This post is all about using ERC20 approve() and transferFrom() to allow your users to do much more than just keep tokens in a wallet.

Before jumping into the nitty-gritty, I’d like to present a common use case. Together, we learn how to create a solution using smart contracts.

Alice wants to pay 20 Eve Tokens (ET) to Bob’s Lemon Juice Stand and receive 20 Lemon Juice Tokens (LJT) in return.

Bob can only give Alice her LJT if he can verify that he has received the correct amount of ET from her in a single transaction. For that, the smart contract needs proof of payment.

Let’s assume that Alice has a token wallet at alice_address, and Bob’s Lemon Juice Stand has a smart contract called bob at bob_address. There’s also an ET contract called eve and a LJT contract called ljt.

If Alice uses eve.transfer(bob_address, 20 ether), the amount appears at Bob’s Lemon Juice Stand address and Bob can verify this with eve.balanceOf(bob_address). His balance increases by 20 ET. But just checking eve.balanceOf(bob_address) won’t allow Bob to check where the tokens came from. How can Alice prove that she transferred the tokens?

To put it another way, Bob’s Lemon Juice Stand’s ET balance could also increase by 20 ET if two people send each him 10 ET. Based on just the ERC20 token contract, you can’t trace the origin of these funds. We have to find a different way to achieve this while still staying in the realm of smart contracts—which is also called on-chain. Let’s take a closer look at what that means.

The term on-chain describes all the features that can be implemented using smart contracts on the Ethereum Virtual Machine. Off-chain describes anything that is not run on the Ethereum Virtual Machine but still uses the Ethereum block chain as a data source and verifiable transaction log.

If we want to verify that Alice’s payment took place using a program— implemented off-chain—there are a few options. We can easily listen for ET ERC20 events using, for example, a web3.js filter. The two possible events are Transfer and Approval, as is documented here. Assuming that our contract emits an event called Transfer(from, to, amount), we can listen for any event matching Transfer(alice_address, bob_address, 20 ether) and we can be immediately notified when Alice transfers her tokens.

Inside a smart contract, we can’t listen for events like this. We need to look for an alternative solution that allows us to send tokens and executes some piece of smart contract code in Bob’s Lemon Juice Contract all in one transaction on-chain.

For that we need to use eve.approve(receiver, amount) and eve.transferFrom(from, amount). With eve.approve(), we can give someone permission to withdraw a certain amount of tokens from our address. This is called an allowance. Bob must then have a function in his smart contract called bob.giveToken() that does the following:

  1. Check the token allowance eve.allowance(msg.sender) (msg.sender is alice_address in this example).
  2. Calculate the correct amount of LJT to be paid out.
  3. Ensure that Bob’s Lemon Juice Stand has enough tokens by checking ljt.balanceOf(bob_address).
  4. Call eve.transferFrom(alice_address, 20 ether) and Assert that the call was successful.
  5. Call ljt.transfer(alice_address, 20 ether) and Assert that the call was successful.

If Bob’s Lemon Juice Stand contract provides the bob.giveToken() function, the process is easy. If Alice now wants to receive LJT in return for her ET, the following two transactions are necessary:

  1. Alice calls eve.approve(bob_address, 20 ether). No token balances change.
  2. Alice calls bob.giveToken(). If the transaction is validated, Alice now has 20 more LJT and 20 fewer ET.

We can’t solve this problem in less than two transactions. With ERC20, there is no way to transfer tokens and call smart contract functions at the same time. Simple and secure is better when it comes to token transactions. Newer standards such as ERC223 or EIP777 offers this capability.

Tags

I would be thrilled to hear from you! Please share your thoughts and ideas with me via email.

Back to Index