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:
- Check the token allowance
eve.allowance(msg.sender)
(msg.sender
isalice_address
in this example). - Calculate the correct amount of LJT to be paid out.
- Ensure that Bob’s Lemon Juice Stand has enough tokens by checking
ljt.balanceOf(bob_address)
. - Call
eve.transferFrom(alice_address, 20 ether)
and Assert that the call was successful. - 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:
- Alice calls
eve.approve(bob_address, 20 ether)
. No token balances change. - 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.