Dear community members,
As you may know, leyvathan.finance was subject to an attack on July 30th 2021, resulting in the minting of 100,000,000,000,000,000,000,000,000 $LEV tokens and price drop to near 0.
As a result, staking and withdraw function ceased to work, so users tuned toward emergencyWithdraw() to recover their funds. It appeared that their was a major logic flaw in this function, which allowed some users to withdraw much more tokens than what the Masterchef contract owed them.
This led to the loss of funds for many stakers who came later to withdraw their funds, only to see an error and an empty Masterchef.
This post-mortem aims to explain how such failure was possible in a plain and understandable way.
I. Lost keys and minting
Levyathan used a classic mechanism to reward stakers of indexes with $LEV tokens, inherited from PancakeSwap:
- The token has a mint() function allowing its owner to mint new tokens.
- The MasterChef contract is the token’s owner and its internal logic rewards stakers with $LEV minted.
- The TimeLock is MasterChef’s owner and is allowed to pass transactions to modify its parameters with a delay.
In Levyathan’s case, a function was added to the MasterChef by the developer to recover the token’s ownership. Presumably, this was done in order to allow to create a v2 of the contract, with enhanced features. Only its owner, the Timelock, had the permission to change it.
This Timelock should have been itself only actionnable by a multisig contract, preventing malicious changes following one of the team members losing the private key, for instance.
On Jul 28th, 2021, the owner of the Timelock, which wasn’t a multisig contract, passed a transaction through the Timelock, scheduling an ownership transfer.
On Jul 30th, 2021, the transaction was executed and the owner could then regain ownership of the MasterChef, of the token, mint a large amount of $LEV, then sell it and then bridge it.
Funds were sent on Ethereum and then hidden in Tornado.cash
II. Double trouble
Since the Masterchef lost its ownership of the token, it couldn’t mint tokens when users withdrew, which triggered errors. Usually, such problem can be mitigated with the emergencyWithdraw() function, which returns the staked tokens without any rewards.
An unknown problem was however present in the code:
Emergency withdraw returned an amount of tokens equal to rewardDebt and not user.amount. rewardDebt is an intermediary variable used by the contract to compute the final rewards owed to the staker, which is usually higher than the real reward.
One can easily see that the returned amount when triggering emergencyWithdraw() were very wrong, which led some users to withdraw larger amounts, which depleted the contract and prevented late stakers to withdraw.
If you have received a larger amount of tokens than what you should have, please contact an administrator of Levyathan’s telegram: https://t.me/levyathanfinance
You can also send the funds in an anonymous manner here: 0x6cadA45b257DA674f1169B4e68A59765226Fc9aE
Many users pointed that this issue should have been noticed by Certik during the audit. It appears that it was changed, probably by mistake, by the developer after the audit, but we’re still investigating the complete timeline of the events.
We are fully cooperating with authorities and will do anything that’s possible to recover stolen funds. Founders had provided large swaths of liquidity and have nothing left, which will drive even further the team’s efforts toward retrieving the attacker.
We deeply regret this latest incident and any economic loss for our affected community members. DeFi is a risky field, where users face economic risk such as impermanent losses, but also execution risk such what happened yesterday. Teams in the field are experimenting and some humane errors are possible,which is why testing, auditing and safeguards are paramount in this industry.
Thank you again for supporting us during this journey and stay close, as we’ll publish new updates.