The Dark Side of ERC20s

The Dark Side of ERC20s

From early on, product engineers in the EVM space get to interact with the (in)famous ERC20 token standard. Cats, dogs, penguins, the US dollar, you name it, everything becomes a token to trade with, lend or distribute.

But standards don’t always evolve with the users’ needs. And if they do, unassuming integrations can start breaking left and right. A token distribution protocol like Sablier gets to see all shapes and sizes of ERC20s and needs to understand and adapt with every new one.

Let’s go over the dark side of these tokens and the common pitfalls you may need to shield your own users from.

Where the fun begins

As a result of endless hours of debugging failing transactions and user reports, I’ve compiled a list of these pitfalls. The following are valuable (and annoying) lessons from the point of view of a product engineer, designing experiences and helping users navigate 🛥️ around this dark nature of ERC20s.

1. Token details, string vs bytes32
2. The 96 bits allowance
3. Token balance “go up”
4. Blacklists, rules and more rules
5. Admin rights

At the end, I’ll present a few other quality of life improvements for your product planning, as well as tooling that has helped with debugging over time.


Token details, string vs bytes32

Problem: Contract calls for token name/symbol failing in apps or indexers.

Context: In its original form, ERC20 returns core details like token symbol or name as string. To optimize for gas costs, developers may choose to replace these representations with bytes32. For an unassuming engineer, this results in a failing query for token details, or infrastructure like The Graph and Envio seizing to parse events, due to a critical error.

Solution: Query for token details in both formats, at once or in sequence. If the call expecting a string result fails, revert to the bytes32 version (and manually convert it to string afterwards).

The 96 bits allowance

Problem: Approve transaction will fail with large allowances.

Context: While infinite allowances are an anti-pattern, in the spirit of shipping fast, they may be overlooked. Hard-coding them to large values (tip: padded to match the token decimals) and applying those values to any token will cause approval transactions to fail for tokens like COMP.

Solution: Implement granular allowances or handle max values for these assets.

Token balance “go up”

Problem: Tokens-in doesn’t always equal Tokens-out.

Context: Certain mechanisms like rebasing tokens, Lido's stETH or Aave’s yield tokens rely on dynamic balances that change over time, without any additional transfers or actions from the token holder. Their quantity simply .. goes up.

If your product or protocol uses a bespoke mechanism to measure token ownership, you might be influenced by this tokens-in-token-out invariant potentially breaking. Best case scenario, for assets increasing their balance (aTokens), it’s only the surplus that gets lost.

Solution: Warn users about depositing tokens without fixed/static balances or offer token wrappers like wstETH or ERC-4626 when dynamic balances cannot be supported.

Blacklists, rules and more rules

Problem: Transactions with transfers may fail due to internal blacklists or balance constraints.

Context: There are multiple types of internal lists that define who can transfer what and how much.

  1. From / to blacklists: Circle’s USDC may revert transfers to sanctioned addresses
  2. Maximum balance: some token contracts constrain holdings by adding per-address caps (e.g. one wallet can only hold < 10,000 tokens). This mechanism fails to address popular escrows where lots of funds can accrue, such as Uniswap pools or Sablier vesting contracts.

Solution: Since there’s no way to automatically cover all these edge-cases, you should simply check for this behavior when debugging failed transactions. Simulating txs will also help catch reverts caused by such lists.

Admin rights

Problem: Tokens burned through external measures.

Context: Some tokens come with admin rights over transfers and supply. One such right is to burn assets from handpicked wallets, or forcefully transfer them without the knowledge of the user. This is definitely not part of the ERC20 standard, nor is it common practice in the industry, but it may happen and result in assets “disappearing” from your protocol’s balances.

Solution: Since it's yet again, a hard to predict behavior, keep an eye out for shady tokens. If your protocol is immutable, explain that forceful actions like burning tokens are at the discretion and the sole responsibility of the token admins.


Quality-of-life++

While there are definitely more gotchas with today’s “extended” ERC20s, like fee-on-transfer or a handful of exploits one should be aware of (e.g. event spoofing), there's a couple of low-hanging fruits worth mentioning:

  1. Correctly handling token decimals (not everything comes with 18 decimals, since that's just popular not standard)
  2. Preventing token addresses from being filled in recipient fields. It’s a common copy/paste mistake to send things to the token address (or to 0x00), so preventing those transfers from happening can save users in time.
  3. Bundling allowances with transactions. This is a big improvement for Safe(s) since coordinating signatures especially in remote/distributed teams is a pain, but can also prove useful for Smart Wallets and ecosystems that support it (e.g. EIP-5794). At least until Account Abstraction hits mainstream adoption.

Fixing what’s broken

In the spirit of innovation, developers have already started implementing alternative systems that could one day become standard even in the EVM space.

While these are great and prove a need for improved token functionality, they don't all address the full list of gotchas we just went through.

Some notable designs that come to mind:

Tooling

As a final recommendation, learning how to read the chain in order to debug issues for your users will definitely help uncover more “mysteries” down the road.

From engaging in basic analysis with Etherscan or the Signature Database to (and I cannot recommend this enough) debugging at its finest using Tenderly, you’ll develop an eagle eye for unexpected behavior.

Tenderly's simulation dashboard (https://blog.tenderly.co)

Prevent, fix or explain - either way, users will thank you. And when they're silent, it means you did an amazing job hiding all this complexity from them. Congrats 🔥!

upload in progress, 0
If you have any questions, ideas, or issues, ping us on Discord or Twitter — we’d love to hear from you.