Note: I have no intention of discrediting the project or anyone involved with it. The beautiful part about crypto and smart contract development is all of the code is on display. I’m just commentating on a monumental screwup and a very interesting situation.
Legolas Exchange (LGO) raised ~$30M USD early in 2018 and deployed an ERC-20 token on Ethereum. The token has a peculiar feature built into the code that is supposed to reward hodlers (sic). For every 6 month period in the first 2 years, all of the accounts that haven’t moved their funds yet should receive an additional 5% in tokens as a reward for their loyalty. The reward has the potential to be higher than 5% — increasing as others abandon their rewards.
Sounds great right?
Unfortunately the immutable contract was coded such that the extra 20% of tokens, that should have been used to pay out the bonuses, is virtually guaranteed to be lost.
I say “virtually guaranteed” because there is a way to retrieve them — it just requires ungodly levels of cooperation. More on this soon.
The Bug
There are several parts of the contract that I think should not have made it through any serious audit, such as the perverse use of percentages to assign token amounts.
Nevertheless, the problematic bug that led to this article can be found on line 326 of the contract source:
// calculate the bonus for one holded LGO
uint256 bonusByLgo = (BONUS_AMOUNT / 4)/unspentAmounts[_bonusDate];
I’m not going to go deep into this — you can easily check what I’m saying for yourself. All you need to know is that this variable, bonusByLgo is expecting to be assigned a percentage. Something like 0.05. If you’re a developer (from any background, not just smart contract development!) you know how absurd this is. A uint256 can only be assigned 0, 1, or any other whole integer.
So when the bonus percentage to dish out is calculated, the user claiming is always going to receive a 0% reward due to natural rounding-down.
Besides this mistake — the bigger mistake is this: Any testing of the claimBonus method would have found this problem. $30 million US Dollars were put into a contract where the core feature was completely untested.
How They “Resolved” It
To their credit, the LGO team started paying out the bonuses manually, from their own reserve, which was also 20% of tokens. You can see these transactions happening over a long period of time — if you want to do in-depth chain analysis you can find one LGO-owned address dispensing a round of bonuses here.
Potentially holders of LGO will be reading this and have no idea something was wrong, but this mistake cost the company ~$7M USD at the worst, if we were to value the tokens at LGO’s highest historical market cap.
A Really Weird Way Out
So as I mentioned when discussing the bug, the variable bonusByLgo will almost always evaluate to 0. Except in one case. When (BONUS_AMOUNT/4) is bigger than unspentAmounts[_bonusDate] . When this happens, the remaining holders will receive one bonus token for every LGO token they hold — a 100% bonus!!
What can trigger this condition? Only if 95% of the holders move their tokens. Only when 5% of the tokens are still eligible for bonuses, they would all receive a 100% bonus at each of the remaining bonus dates.
The really crazy part is this! If the community came together and all decided to move their tokens — completely the opposite of what the contract intended — they could potentially deliver LGO back at least some of their tokens and save LGO money. You could even imagine a situation where the LGO team or another rogue developer builds a smart contract that allows the bonus to be claimed and distributed amongst the remaining holders — though good luck getting the required >95% participation.
The Lesson
Be skeptical of any token contracts with overly complex functionality. “Smart” smart contract developers should be staying away from any such gimmickry, and when it is absolutely needed, it should be tested and audited to extremes.
For more on interesting aspects of smart contracts find me on Twitter: https://twitter.com/codingupastorm