Etherscan Tricks: Transfer ERC20 Tokens Out Of Thin Air

How to technically make a fake ERC20 tokens withdrawal from someone’s address

Disclaimer: This article is only for educational purpose. I love Etherscan team and their great works that help encourage the growth of Ethereum community.


Two weeks ago my friend sent me this post on Reddit, with holders of ERC20 tokens complaining about spotting some weird transferral history at their accounts, with tokens they have never seen and bought in the past. It caused some fuzz and nervous discussions among people about how to deal with it, and how much does it do harm to their wallets.

Reddit post on weird withdrawal

What you will learn

This article will demonstrate a measure to achieve the similar consequence shown in the aforementioned Reddit post. Eventually, you will acquire the concept and the skill to replicate the trick on Etherscan.


ERC20 is simply a token interface standard that is suggested for developers to obey in order to make sure every token on the market have a compatible interface to achieve basic functions, such as transfer and approve.

Etherscan does not record the transactions based on whether an exact transferral action has taken place, yet the Transfer Event emitted by the token contract. It is correct! However, what if an ERC20 compatible token contract forge(fake) an event without doing any actual transferral at all?

Let’s do some hands-on

The contract code (FakeToken.sol) is as followed:

FakeToken.sol referenced from OpenZeppelin’s SimpleToken.sol

Simply copy over the Openzeppelin’s SimpleToken contract as the base, with an additional fakeTransfer function.

By the fakeTransfer function at line 14 to 16, we are able to emit the Transfer Event without doing any transfer. When this fakeTransfer function is triggered on the Ropsten network, you can see there is a transfer recorded by Etherscan on the Ropsten network.

Transaction Hash:

In an extraction of the transaction as followed, we can observe that address 0x0a1e0… initiates the transaction which transfers 2018 least units of ERC20 (FAK) from some arbitrary address 0xdd989… to itself.

Extraction of fake transferral

If you go into the ERC20 history of 0xdd989…, you will notice that 0xdd989… NEVER holds an ERC20 (FAK) in the past.

No ERC20 inflow at address 0xdd989…

Bonus: You can make it a “fake” burn function

function fakeBurn(address _sender, uint256 _value) public {
emit Transfer(_sender, address(0), _value);

This is how it’s done. :)


This fake transferral cannot make an impact if the owner knows that this is bogus. There are two cases that can possibly make it useful:

  1. You just want to promote and increase the awareness of your token lol
  2. You want to scare the holders that their tokens have been stolen (by using the exact name as your targetted ERC20 token in the token contract).

Response from Etherscan Team

I have mailed Etherscan’s team in advance on this issue before publishing. The followings are their swift response (in 2 days):

Hi Jeff,
Thanks for writing in and pointing out the SPAM token promo trick.
We have a SPAM reputation in place to mark any promotional or deemed SPAM technique that would add unnecessary "noise" to Etherscan.
As a transaction in the nature you have pointed out is initiated with a gas fee paid, it would be recorded as a transaction and while calling the contract will initiate any function in the contract (including emitting a log). It is unavoidable due to the transaction being "paid" for.
We could only downplay these "promotional" attempt by marking it as SPAM.
Once again, thank you for your effort and writing to us.
Have a nice day
- Etherscan Team [Kav]

I totally agree that manually downplaying the SPAMs could be the only effective way, yet I reckon that this trick can still deceive some holders who are unaware of this issue. ;p

Thank you

Hope you enjoy the article and have learned some cool skills.

I really love Etherscan and wish that this tiny trick can be prevented soon in the near future to avoid misunderstandings. ❤

Github code is here.