Published in



Ciphershastra — Gambler Puzzle


  • Only the main contract code is presented in the Ciphershastra website. The rest of the code can be found in Etherscan, as the contract is verified.
  • It uses the 0.7 compiler version. There were many changes from version 0.7 to 0.8, but one of the most prominent ones was the introduction of built-in safe math operations. However the contracts seems to be using the SafeMath library, so it could still be protected against a possible overflow/underflow.
  • There is a maximum of 20 chips that a user can buy before gambling with those chips
  • Before gambling, the user must get verified by paying for the requested chips. In particular, the user must pay 576460752303423488 wei per chip. That is definitely a weird number. What if we convert it to hexadecimal to see how it looks like? We get 0x0800000000000000. That is a 64-bit number, which would overflow if we multiply it by 32
  • The _safeMint in buyChips will call back the caller through the onERC721Received, only if the caller is another contract. This callback comes from theERC721Receiver interface and it is used to make sure that a caller contracts is able to receive and handle ERC721 tokens
  • The doubleOrNothing function calls the libGambler library to calculate the outcome of the dice roll. If we look in Etherscan for the source code of this library, we can see that it uses some data source like the block timestamp, the coinbase and the difficulty, to calculate the roll. All these data can be easily predicted by using another contract and running the same calculation there to come up with the bet to be used
  • If the dice roll is predicted correctly, then the account that will receive the prize in the form of NFTs, is calculated in a very particular way, and if we look closely we can see a typo in the acount parameter, which could be confused with theaccount storage variable.

The Blind Spot

The Source of Truth

The Solution Contract

 address account = 0x0000000000000000000000000000000000000000;
uint256 bet = uint256(keccak256(abi.encodePacked(block.timestamp, block.coinbase,
account, “sus”))) % block.difficulty;

gambler.getVerified{ value: 0 }();
gambler.doubleOrNothing(account, bet);
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4) {
if (buyAgain) {
buyAgain = false;
return this.onERC721Received.selector;

Ready to attack!

  • Buy 16 chips
  • Receive the callback and buy another 16 chips before the contracts blocks us out from buying more chips
  • Get verified by paying 0 wei, as the calculation will overflow but no revert will be triggered
  • Double or nothing with the calculated bet which will match the dice roll in the Gambler contract
  • Profit!

Github Repo

About me

New to trading? Try crypto trading bots or copy trading

Join Coinmonks Telegram Channel and Youtube Channel get daily Crypto News

Also, Read



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Roberto Cano

Founder of The Solid Chain and Engineer with more than 20 years of experience offering Web3 Development, Architecture and Leadership services.