Yesterday, we proudly announced an unkillable gateway to crypto. 6 hours later, a critical flaw found and, in quite a professional manner, reported and exploited by Adam Dossa, who later returned the drained funds to the original trade creators. I was woken up at 3 AM, quickly filled in, and proceeded to disable the platform on mainnet and update the launch post with the disheartening news.

And now I’m writing a post-mortem on what was supposed to be unkillable, so that’s pretty funny. (Technically speaking, that version of DAIHard does live on—the Factory contract will happily serve more users — but it’s a mangled thing, only really good for giving DAI to strangers. Not exactly the breakthrough we were hoping to provide.)

This relatively brief post-mortem will analyze what went wrong on a few different levels of analysis: technical, organizational, and personal.

Technical: The Exploit

A DAIHard trade is created by the DAIHard Factory contract, but the order of operations is not so straightforward (this is partially due to the fact that an ERC20 token can’t simply be included in a contract call).

The Factory creates the new trade in the “Created” phase. At this point, the trade is effectively blank, with no settings and no token balance. The Factory sends the user’s deposit to the newly created trade, so the next step can take into account the token balance. The Factory calls the open method on the new trade, which sets all the initial settings (for example, the initiator (aka owner), the open mode (seller-opened or buyer-opened), etc.) This moves the contract to the “Open” phase.

That last step is where the exploit is: open can be called at any time, by anyone, to overwrite any settings — including the de-facto owner. The new owner can then use the recall method to get a “refund”.

The fix is extremely simple. There should have been an inPhase modifier on the open method, just like with every other external state-changing method in the contract. The open method should only be called in the “Created” phase; the open method would then change the phase to “Open”, meaning that it could never be called again.

You can see the fix in this commit.

The bug slipped by me because I was modelling the contract with the assumption that such logic was in place. I simply forgot to put it there.

Adam also mentioned there were other problems with the code, but we haven’t yet had a chance to talk to him more about this.

Organizational: Why The Hell Didn’t We Audit?

u/vvpan on Reddit put it well:

Come on guys… Auditing is costly, but just do it!

Let me be clear: auditing is absolutely a necessary step for any new platform that relies on smart contracts. No argument there at all.

A month or so ago, I asked a team member to reach out for auditing, but neither one of us tracked it on Trello. As we approached launch (and pushed back the launch date a few times), it simply never popped back into our awareness. We never chose not to audit — we just forgot.

(We otherwise faithfully kept all important tasks on Trello, so when the “pre-launch” list was finally empty yesterday, we trusted it.)

It’s an underwhelming and embarrassing conclusion, I know. If there’s anything to be learned from this, it’s this: track the “audit” task on your task management software before adding anything else. You can put it off as long as you want — but definitely don’t forget about it.

Personal: How Could I Forget?

Okay fine, so we didn’t track it on Trello. Still though — how could it have never come up in our minds again?

For me, this came from the fact that the DAIHard contract was a modified version of Burnable Payments, which had been tested by fire (pun unintended but allowed to exist), and had proved reliable. So the DAIHard contract felt fairly stable.

That’s not to say I ever consciously decided it didn’t need an audit; I’m at least not that careless. But I had an impression that it was “only” a “modification” of a previous, dependable contract, which gave me a false sense of security. In other words, I wasn’t as nervous about the security as I should be, so auditing never felt like a necessity, so much as a best-practice formality.

This little episode should help me to remember next time.

Moving Forward

Thanks for reading. Hopefully this can help others avoid similar silly mistakes in the future. We’ll have more to say in a week or so on our plans for moving forward.

If you’d like to keep abreast of updates on this project, you can join or Telegram channel for updates or our Telegram group to chat with us.

(A section of the original article has been redacted, pending internal discussion.)