Moolya ICO: Burn the burnReturn

It started with a mystery.

Various crypto influencers reviewed the Moolya ICO in exchange for free MOOLYA tokens. Typical publicity-for-pay arrangement. But while expecting to receive 50,000 tokens, their Ethereum wallets received an incoming transaction of 833,333 MOOLYA. At the ICO price of $0.06, that’s worth $50,000 USD!


That there is a lot of MOOLYA

The beauty of the blockchain is that you can confirm every transaction. TxReceipt Status: Success. Plenty of block confirmations. Whoever screwed up those sends, it was now permanently etched into the Ethereum blockchain. Too f’n bad, they just made those influencers MOOLYA whales.

What is done cannot be undone

But when checking back an hour later their balance strangely showed up as 50,000 MOOLYA. The amount it was supposed to be all along. Wait, wha…? They didn’t send their MOOLYA riches out to anyone (nor could they have; the tokens are locked until the ICO ends, rendering them nontransferable).

And yet we know the 833,333 token transfer did happen. The transaction is right there in the freakin’ blockchain!

Where did those tokens go?!

After a little digging they realized there was a new transaction on their Ethereum address. It offered a half explanation: there was an additional transfer of 50,000 MOOLYA coins.

MOOLYA math: 833,333 + 50,000 = 50,000?

Two transactions in. No transactions out. Mystery.

When they first told me about this I figured they were just misunderstanding the transaction records. But nope, both sides of the impossible contradiction were clearly true: Yes, there were two transactions. Yes, the total balance was only 50,000.

The MOOLYA ERC-20 contract code is publicly available on so I started reading through it.

Solidity, anyone?

The vast majority of every ERC-20 contract code is actually just a copy-and-paste from the base ERC-20 template. That made it pretty easy to skim through. It was all totally generic, boilerplate stuff that just sets up the basic token mechanics.

But then I finally found something interesting in the custom-coded section at the end:

burnReturn: cool band name or bad STD situation?

It’s simple enough so let’s dig into this line-by-line. The function takes as inputs an Ethereum address _addr and an integer _value. And even though this is a public function, it has an access modifier onlyOwner that does what it sounds like: only the contract owner has permission to execute this function.

The require calls on lines 370 and 371 are just checking to make sure that the address provided isn’t a blank address (it would be nonsensical to do a token operation on nothingness) and that the address has at least _value number of MOOLYA tokens in its balance.

Those were just sanity checks. Now we get to the interesting part!

Line 372 subtracts _value number of MOOLYA tokens from the provided address’ balance. The contract owner is literally taking _addr's tokens away.

And where do those tokens go? Well in line 373 those tokens are added to msg.sender's balance. Someone else gets _addr's MOOLYA! Who is the msg.sender? It’s the address that called burnReturn. And the only person who can call that function is the contract owner.

SoburnReturn is not a “burn” function — that’s the terminology for when tokens are destroyed. No, instead it gives the contract owner the power to take anyone’s MOOLYA tokens away at any time and deposit it back into his/her own account.

This function makes the contract owner this ERC-20’s god. A single point of total control over who owns how many tokens.

But wait, there’s more! As Dr. Josh Cotton pointed out (props on spotting it!), there’s also a problematic mintable function:

Oh for f*ck’s sake

This is another onlyOwner function and by now you can probably read what it does yourself. On line 365 it increases the msg.sender's token balance by the _value specified. We know that means it goes to the contract owner. These new tokens are then added to the totalSupply, making MOOLYA trivially inflationary. They can make more coins for themselves any time they like.

Good grief. MOOLYA is not decentralized. At all.

Sifting through the transaction logs everything finally made sense: burnReturn was called to take back the 833,333 MOOLYA they sent out on mistake.

At least it cost them $0.19 to take away $50,000 USD

Clicking “Decode Input Data” translates the function params into friendlier values:

_value is denominated in wei; move the decimal 18 places left to get 833,333

So the influencers were accidentally paid out 833,333 MOOLYA, the team realized their mistake and burnReturned those tokens back to themselves, then sent out the proper 50,000 MOOYLA. Mystery solved.

In this case the burnReturn saved the Moolya team some serious bacon.

Sadly, their transaction history seems to be riddled with other token distribution mistakes:

You’re MOOLYA dust! No, wait, you’re a MOOLYA whale

In their first attempt to send 5,555,556 tokens they forgot that Ethereum contracts are denominated in wei (1 ether = 1x10¹⁸ wei). These two addresses were expecting $333,333 USD worth of MOOLYA and instead only got dust. That is until the second transaction where the Moolya team did the wei conversion correctly.

Silly mistake. But at least they learned from it. Or not.

Oops, I did it again?!

Same problem but, uh, wait, the fix-up transfer didn’t go to the same address! 0xe96 got dust while two minutes later 0x091 got the amount that they clearly intended to originally send to 0xe96. Man, nice to be 0x091… when it whales it pours!

It’s starting to become clear what the burnReturn is for. It may or may not be for nefarious token-stealing purposes down the road. But it’s clearly necessary to fix up the Moolya team’s many screwups.

I’d like to return to the MOOLYA smart contract to point out a few more concerns.

The first is the blank line on line 375. This is a pretty ticky-tacky complaint, but why is that blank line there? Programmers have certain formatting rules (really more like a tidiness aesthetic) that often become dogmatic (the “Silicon Valley” episode where using spaces instead of tabs is grounds for ending a relationship is really a thing). That blank line should not be there. That bothers me.

What bothers me more is what should be in that blank line but isn’t: an Event. Think of an Event as a way to announce important things happening in your smart contract. For example Transfer is a crucial Event for an ERC-20 as it announces when one user has moved funds to another user. Events even get their own section in block explorers:

If you look through MOOLYA’s Events, you’ll see a stream of Transfers but you know what you don’t see? There are no BurnReturn Events. Why is that? Every other token transfer operation triggers a Transfer Event but when the contract owner moves your tokens no such matching Event is fired? Not surprisingly there’s no TokensMinted Event, either.

We know that you can’t hide your tracks on the blockchain, but you can make them a bit harder to find. This is sketchy.

That blank line on 375 should have a Transfer Event (and maybe it even did at one point in time). It’s trivial. In fact, here’s what should be there:

emit Transfer(_addr, msg.sender, _value);

There, was that so hard?

Elsewhere in the custom-coded section there is an unforgivable formatting violation:

One of these things is not like the other…

Do you see it? allocate's function body is not indented! In solidity this doesn’t affect the code but in languages like python and ruby this would be disastrous. This is simply not done.

This is a sign of mind-boggling sloppiness. The indentation issue in particular is proof that there was no code review by another developer (programmers are wise enough to not just simply trust themselves that they built something right; real project teams always require a separate set of eyeballs on each developer’s code).

The custom-coded part of MOOLYA is only 40 lines of code. That’s it! The rest is copy-and-paste. It’s deeply disconcerting to find this many issues in just 40 lines.

So is this a scam?

I don’t know. But it doesn’t even matter what their intentions are. The fact is that the contract has an unacceptable back door. Can it be fixed? Well, the contract owner could renounce the ownership of the contract once the ICO is complete, thereby rendering burnReturn and mintable totally unusable from that point forward.

Why Estonia? No clue

But should you take it on faith that they’ll do the right thing after you’ve invested your money? And what does the code sloppiness and the incompetent token transfer mishaps say about the team running this project?

The Moolya team seems to be entirely Indian while the ICO info all points to Estonia. Maybe they outsourced their ICO and just picked a bad partner to work with? I dunno. But for an ICO that is looking to raise $3–$25 million USD (and has already raised ~$7 million USD at time of writing), I would expect all of that value to be entrusted to something stronger than these shaky 40 lines of code.

All I can say at this point is:

DYOR: Do Your Own Research

Don’t trust, verify.

Keith Mukai is a Python/DApp developer and the creator of He has definitely made plenty of stupid coding mistakes of his own. You can find him on twitter @KeithMukai where he may regret not using a pseudonym.