Search projects, hackathons, sponsors and tech

    ← All projects

    About

    The problem it solves **Crypto UX is a nightmare for normies** the bot we've built solves the single biggest problem preventing crypto from going mainstream: We're taking a process that's fundamentally hostile to the average user and making it as simple as sending a text message. Think about a traditional gaming platform, like a game you download from the App Store. To play, you might need to create an account with an email and password. To buy in-game items, you use your credit card. It's familiar and easy. However, you don't truly own anything. The company owns your account and all your items. If they shut down the game, everything you bought disappears. The game's logic, how it decides if you win or loss, is a complete black box running on their private servers. You have to blindly trust that they're not rigging it against you. Now consider the typical crypto experience. It solves the ownership problem, you truly own your assets in your wallet, but it creates a mountain of new problems. You need to install a special browser extension like MetaMask wallet, Core wallet and so on, write down a secret phrase that you're told you'll lose all your money if you forget, and then navigate a confusing website full of "Connect Wallet" buttons and pop-ups asking for transaction approval. It’s intimidating and completely alien to anyone outside of the crypto bubble. You gain ownership but you lose any sense of user-friendliness. Our bot fixes this by creating a bridge between these two worlds. It offers the best of both. Like a traditional app, the user doesn't need to install anything new or learn any complex concepts. They stay inside WhatsApp, an app they already trust and use daily. Their wallet is created for them instantly and securely behind the scenes by Privy, so they don't have the burden of managing a seed phrase. But unlike a traditional app, they have true ownership of their funds. We, the developers, can't touch their money. It's theirs. And this is where the Chainlink integration becomes the killer feature. Instead of asking users to blindly trust our server like a traditional game, we give them a guarantee of fairness. Our game's logic (the coin flip, the dice roll, the lucky number) isn't decided by our private code. It's decided by Chainlink's provably random and tamper-proof oracle network, and the result is posted publicly on the blockchain for anyone to verify. We've taken the transparency of Web3 and delivered it with the simplicity of Web2. That's the problem we're solving.

    How it's made

    Challenges we ran into Building this bot was more than just writing code; we had to solve some tough problems to make a complex blockchain game feel simple and fast in a WhatsApp chat. Our biggest challenge was what we called the **"Waiting Problem."** When you play a game like "guess the Number," we can't just invent the numbers ourselves that wouldn't be fair. We use Chainlink to get truly random, unpredictable numbers. But asking Chainlink for a number isn't instant; it's like ordering a pizza. You place the order, and then you have to wait for it to be delivered. We couldn't just leave our users hanging in the chat, wondering if the game was broken. Our solution was to build a smart system that sends the user two key messages. The first one says, "Okay, we're drawing your numbers now!" Then, our backend listens carefully to the blockchain. The moment the numbers arrive from Chainlink, the bot instantly sends a second message: "Your numbers are here! Make your guess." This turns a confusing wait into a simple, two-step process. First call the Chainlink vrf for 5 random numbers between 11 - 99. ```sol function play() external payable nonReentrant { require(msg.value >= minBet && msg.value <= maxBet, "Bet out of range"); totalOutstandingBets += msg.value; uint256 id = s_vrfCoordinator.requestRandomWords( VRFV2PlusClient.RandomWordsRequest({ keyHash: keyHash, subId: subId, requestConfirmations: REQUEST_CONFIRMATIONS, callbackGasLimit: callbackGasLimit, numWords: NUM_WORDS, extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({ nativePayment: false })) }) ); games[id] = Game({ player: msg.sender, bet: msg.value, ts: block.timestamp }); emit GameStarted(id, msg.sender, msg.value); } ``` The user then guess the lucky number ```sol function makeGuess(uint256 id, uint8 guessIndex) external nonReentrant { Game memory g = games[id]; require(g.player == msg.sender, "Not your game"); require(guessIndex < 5, "Guess index must be 0-4"); GameData memory res = results[id]; require(res.numbers[0] != 0, "Game not ready"); bool won = (guessIndex == res.winningIndex); uint256 pot = g.bet * 5; if (won) { uint256 payout = (pot * PLAYER_PAYOUT_BPS) / BPS_DIVISOR; uint256 houseCut = (pot * HOUSE_FEE_BPS) / BPS_DIVISOR; uint256 adminCut = (pot * ADMIN_FEE_BPS) / BPS_DIVISOR; houseFees += houseCut; adminFees += adminCut; _safeTransfer(g.player, payout); emit GameResult(id, g.player, Outcome.PlayerWins, guessIndex, res.winningIndex, payout); } else { houseFees += g.bet; emit GameResult(id, g.player, Outcome.PlayerLoses, guessIndex, res.winningIndex, 0); } totalOutstandingBets -= g.bet; delete games[id]; delete results[id]; } ``` Another issue was the **"What if the Pizza Never Arrives?"** problem. Sometimes, the blockchain can get very busy, or in a rare case, the Chainlink request might get stuck. We couldn't let a player's money be trapped in the game forever. To fix this, we built an emergency escape hatch right into the smart contract called `reclaimBet`. If more than 15 minutes pass and the numbers still haven't arrived, the user can call this function to get their original bet back, no questions asked. It's a safety net that guarantees the user's funds are always safe, even if things go wrong. ```sol function reclaimBet(uint256 id) external nonReentrant { Game storage g = games[id]; require(g.player == msg.sender, "Not your game"); require(block.timestamp > g.ts + requestTimeout, "Too early"); totalOutstandingBets -= g.bet; uint256 amt = g.bet; delete games[id]; _safeTransfer(msg.sender, amt); emit BetReclaimed(id, msg.sender, amt); } ```