WeiDai exploit postmortem
Dear WeiDai community,
It is with deep regret and sadness that I must confirm an exploit has occurred on the WeiDai protocol and that I want to apologize with utmost sincerity to all those affected. My hope for this article is to bring clarity and explain the possible ways of recapitalizing WeiDai in the longer term. Since it’s launch in November 2019, through bull and bear markets, WeiDai has achieved a consistent APY of about 30% without reliance on inflationary incentives, credit or false hype but purely through the tokenomics of burning. The support of so many of you who share the ideal of a thriftcoin — a stablecoin that sustainably beats price inflation and does not require the holder have deep knowledge of finance — has not been lost on me and I won’t rest until WeiDai is back on its original growth trajectory.
The Exploit: ownership
WeiDai has a “bank” that acts as a logical interface for redemption, issuing and calculating the redeem rate. The bank contract in turn has a reserve contract. This is where the Dai is stored. The purpose of separating reserve from bank is that, without changing any logic, we can switch out how the Dai is stored so that it can potentially earn DeFi yields while in the vault. For instance, depositing the Dai in Aave. Finding the perfect source of yield required ensuring that the gas costs from minting and redeeming didn’t rise too much and that the yield source did not significantly increase protocol risk. Until I could dedicate significant time to WeiDai, the funds have been in a contract called InertReserve which simply holds the Dai. All reserve contracts are required to implement a function transferToNewReserve with this signature
function transferToNewReserve(address reserve) public;
This allows the swapping out of one reserve for another by sending all the Dai to the new reserve. At the time of the exploit, InertReserve’s implementation looked like this:
function transferToNewReserve(address reserve) public {
uint daiBalance = ERC20(daiAddress).balanceOf(address(this)); require(ERC20(daiAddress).transfer(reserve,daiBalance),
"transferToNewReserve failed at Dai transfer");
}
The exploit occurred because I neglected to add an onlyPrimary modifier to the function. In other words, no check was made on who called the function. This is the one of the most trivial bugs and it was certainly not committed intentionally. The person who exploited this bug simply had to execute the function and supply their own address. This is the transaction for the withdrawal of the 50,146.78 Dai.
https://etherscan.io/tx/0xb2608a53f8291789a3dd2b73a68576f000a8c29e337b7eab74e1df7ce0498206
While exploiting this required some thorough investigation, the fault lies with me and I take full responsibility for the loss of funds and do not hold the exploiter accountable. If the person who executed this transaction is reading this and is willing to return any portion of the funds, I will be prepared to make a long term commitment to reimburse the full amount with interest and will maintain complete anonymity and indeed gratitude for performing such a strong white hat test on the protocol before even more funds were put at risk.
Immediate Actions taken
Upon discovering the vulnerability, I deployed a version of InertReserve with an onlyPrimary modifier and transferred the 19 dai still in the original to the new.
The WeiDai front end has been paused until further notice.
The bank contract exposes a function called daiPerMyriadWeidai which, as the name suggests, returns the quantity of dai in reserve for every 10000 WeiDai. This initial value for this number was 100, implying a starting redeem rate of 0.01. At the time of the exploit, the ratio was sitting at 166, implying that the redeem rate had grown by 66% since launch. Given the reserve of about 50000 Dai, this implied a circulating supply of WeiDai of just over 3 million. After the exploit, the new daiPerMyriadWeiDai would be about 19x10000/3000000 = 0.063333333. However Ethereum does not have fractional units which means it would be 0. Minting WeiDai involved a division by this number so for now, minting will fail. However, as soon as the Dai in reserve exceeds 300, the number will be 1, at which point minting and redeeming can resume.
Since WeiDai trading on Behodler is suddenly overvalued, being able to mint WeiDai cheaply would constitute a way to withdraw liquidity. For this reason, trading of WeiDai on Behodler has been halted and minting of WeiDai has temporarily been halted.
Possibilities for recapitalisation
There are a number of possibilities for recapitalisation and all will be explored in order to achieve the fastest recapilisation while remaining faithful to the ideals of the existing community:
- Me: As funds make themselves available, I will personally replenish the reserve.
- Yield: As mentioned earlier, the intent of the reserve is to put the Dai to work in DeFi. One reason I was holding back was to utilize a post-Limbo Behodler in order to swap out Dai for something that generates high ongoing yield denominated in stablecoins such as OUSD or a PyroToken for a Yearn stablecoin LP token. This would automatically edge WeiDai growth into a position of being, by definition, the best stablecoin instrument in DeFi. This would be in addition to the approximately 30% growth that has occurred purely from burn tokenomics.
- Limbo: Limbo has a surprising degree of flexibility and one of the uses put forward in the community is to use it to recapitalise a bonding curve. For instance, Limbo offers a pool with a decent APY on WeiDai and a threshold for migration. At the point of migration, in addition to burning SCX, burn WeiDai. If calibrated correctly, this could mutually benefit WeiDai, Flan and SCX while instantly transferring value back to WeiDai holders.
- External funding for WeiDai: WeiDai started as a passion project and has to date not received a cent of external funding, nor has a cent been spent on marketing, all the while achieving 30% consistent APY. In that time, the cryptocurrency markets have matured and WeiDai would benefit tremendously from a refreshed UI, deep liquidity reserves on multiple AMM in order to encourage arbitrage burning, and perhaps a more concerted marketing push.
Option 3 requires testing and investigation to ensure that it is purely for the mutual benefit of all communities in involved but most of all it requires a completion to a fully tested Limbo. Part of the reason for the delay in Limbo has been because I have been applying a formal verification technique for smart contracts in addition to the usual testing procedures. I have no doubt that it would have caught this exploit had I applied it to WeiDai.
Closing thoughts
I’m absolutely confident that WeiDai will recover its original price and growth trajectory in time so that the long run growth will be unaffected by this event but I do fully appreciate that this is no consolation to those who may want their funds now. Indeed, at the time WeiDai was incepted, the volatility in cryptocurrency markets required holding Bitcoin for 4 years to be certain of returns after purchase. This is a difficult ask for those living month to month and WeiDai was intended to bridge that gap. I must once again express my deepest apology that this covenant was broken and must reiterate my firm resolve to make it right.