Hey llamigos,
We have been informed by a security researcher that our contract was vulnerable to an exploit allowed potential cheaters to mint dogs at an increased rate. We paused the mint and have worked around the clock to provide a solution so we can continue the game. We have now reopened the contract.
In short:
DiamondHeist Contract has been upgraded to improve randomness and prevent cheating
No cases have been found where users cheated and all funds are safe
We adapted the mint process from 1 transaction to 2 transactions: mint and reveal.
The security researcher pinged us on Saturday to let us know that we are vulnerable to an exploit that was seen in similar versions of our game, where we use a simple randomness function that is vulnerable to flashbots. An exploiter can create a contract where they predict the block timestamp and the rarity of our collection to mint only dogs.
From the security expert:
Here's a replication of what an attacker would do.

Basically taking in an address, simulating the exact seed your diamondheist contract would produce on a hypothetical mint, and parsing out the result of that hypothetical mint using the seed.
An attacker would then create a script that would create a flashbots bundle that looks something like this
[Call roll_alpha(address) passing a randomly created address,Send ETH to randomly created address,mint() from randomly created address]If the first function reverts (meaning you have not minted a rank 8 dog) the entire bundle reverts & no gas is paid
If the first function goes through (meaning rank 8 dog would be hypothetically minted from given address) the 2nd and 3rd tx's in the bundle will successfully mint said rank 8 dog.
This can be attempted thousands of times on thousands of different addresses each block to guarantee a probabilistic hit each attempt.
There are a few different methods of countering this, all following this same idea: Generate randomness off-chain, rather than on chain & fully assure that randomness is not seen by any potential attackers before arriving on chain.
I recommend a commit/reveal system like WND had setup. However, when sending seeds from an admin wallet to the contract, flashbots MUST be used to prevent any attackers from frontrunning your randomness.
If you want to be very UX friendly, but spend a lot of gas. You can send a seed to the contract immediately after every mint, and have that seed assigned to a unique mint commit ID.
If you want to be less UX friendly, but save on gas. You can have timed interval batches that will collect pending mint commits waiting to be revealed.
Every inteveral (eg. 30 mins) you will send a seed to the contract along with: a batch identifier, and the length of the batch. This will prevent anyone from frontrunning into the batch after the seed has been generated.
We quickly paused the mint as soon as we were aware of the issue. The next step was to reproduce the attack ourselves. We deployed our contracts on Goerli testnet and we created a flashbots bundle ourselves. After some time we were able to mint 3 dogs in a row! So the exploit was valid.
We checked the Leaderboard to see if some users had a disproportionate amount of dog mints. We noted that it seems that this exploit has not been used before.
We quickly discussed some other ideas and possibilities that were possible, such as minting for the users themselves, but this would cost us all the gas fees which would cost us a few million $ in transaction fees if all our collections mint out and would not be immediately appreciated.
Sending batches of seeds every 30 minutes was good, but our mint is slow at the moment and we were still vulnerable if a block was uncled and the hackers had some time to mint.
Another option, was to sign two transactions and submit it to our server, to sandwich attack a seed transaction, but most users are reluctant to sign things and would prefer to execute a transaction directly;
Finally, we decided to use a Tenderly Web3 Action to submit a seed transaction after every mint, so we don’t have to manage server infrastructure ourselves and this could be done automatically after a mint event has happened.
This would mean we have to split up our mint transaction into two separate transactions.
User commits to a mint and pays
Server picks it up and sends a seed transactions
User receives a notice that the seed is ready and sends a reveal transaction
It looks like the Loom below:
https://www.loom.com/share/b5301d0a822a476cae5f32a4953cbf19
We tested for an entire day on Goerli until we had the entire user experience on point. To make sure the web3 actions work well. That the gas was fully optimized (we rewrote everything a few times) and had unit tests to trim down the gas and code to the best version possible.
Together with the security expert, we were able to confirm that everything now is fixed and we deployed the new contract as an upgrade. No costly user migration necessary and we can proceed as before!
We want to give a big thank you to the researcher for responsibly disclosing the issue to us and working with us to find a solution. We have implemented their suggested solution and have also implemented additional measures to further improve the security of our game.
We think this case show our incredible talent as a team to fix an exploit of this size and our long-term vision to make this a project that will make a big impact in the space.
We thank you for your support and trust.
Jim, Meows and Jordan.
For those unfamiliar, here are the threads with a deeper exploration of the topic for those games and how they resolved it:
In particular, we were familiar with some of the issues involved in those games and we made big improvements to think ahead for a situation like now:
We used merkletrees for whitelist, for WND it cost them 25 ETH in gas: https://twitter.com/wndgame/status/1468492098429022209
We fixed the reentrancy issues beforehand in staking.
We made sure our contracts are upgradeable, with users not having to migrate to another collection. Massive lifesaver here!
