How We Built "Make It Snow" Part 1

In my last post, I introduced Make It Snow: a social giveaway dapp enabling social airdrops in chat groups and communities. For each drop or giveaway, the creator commits a certain number of tokens before sharing out to groups. The first X people to “claim” can win a portion of the tokens. In this post, I’m going to share with you the engineering challenges behind the project and the lessons we learned.

When we first devised Make It Snow, we thought it would be a very easy starter project - a quick foray into writing and deploying solidity code. How hard could it be to take some tokens and split it among N people?

function claim() {
	if (claimsLeft != 0) {
		caller.call{ value: splitAmount }("")
	}
}

Done, right?

Problem 1: Bots

If a smart contract simply had a “claim” function that anyone or anything can call, then bots can just monitor the contract and snipe the claims as soon as they’re available. Humans stand no chance.

There is no such thing as captcha in the smart contract world. Smart contracts cannot distinguish whether the call is made by a human or bot. One way to circumvent this is by introducing a social password for each giveaway. This password doesn’t need to be sophisticated; it just needs to be complex enough to deter bots for a few minutes while humans race to make the claim.

Done, right?

Problem 2: Front running bots

The fundamental problem with introducing passwords is that bots can pick up on those, too! With blockchains, all transaction data sent to the mining pool is visible to the public. When someone makes a function call to a smart contract with the password, essentially what they’re doing is this

Caller: Hey miners! I’d like to call the “claim” function on the Make It Snow contract with the password “ABC”. Can someone mine this function call for me?

In the same way miners can read the data from this call, bots can pick up on the password and “front run” the transaction. This means paying a higher gas fees to the miners and to ensure their duplicate transaction gets mined before the original caller’s transaction. Humans stand no chance, again.

Front running bots are not new: they’ve been plaguing the DeFi space for a long time now. Suppose a whale decides to dump Solana; a front running bot can detect this and dump Solana before the whale’s transaction.

There are several ways to mitigate front running; for us, we chose a strategy known as “commit and reveal.” This strategy splits the original claim function into two phases: “commit” and “reveal.”

During the commit phase, the caller “commits” to a claim by calling the smart contract with an encrypted message. This encrypted message is created by taking the sender’s address and combining it with the giveaway’s password to form a unique message. Although the message is publicly readable, it is encrypted and cannot be converted it into the original password. In addition, no other caller can “copy” it because the encrypted message is unique to each caller.

Once all the claims have been made, we switch over to “reveal” phase. In the reveal phase, each caller calls the smart contract with the unencrypted password. The contract then checks for a 2 things

  1. the password matches that of the giveaway’s password
  2. the password matches the original commitment made by the user

#2 is possible because we can take the raw password and sender’s address, hash them, and recreate the commitment. The contract checks to see that the stored commitment is the same as the one generated during the reveal phase.

Note that once the password is “revealed”, the password will be public and any bots will be able to pick up on it. Unlike the previous approach, however, this information is no longer useful for the bot. If they try to make a “reveal” transaction, the contract will error out because the bot has never made a commitment. If the bot tries to make a “commit” transaction first, the contract will error out because we’re no longer in the commit phase.

This is not easy to grasp; if you’re interested, reach out on Twitter or check out our contract on snowtrace to see the actual code!

Now we’re done, right?

Problem 3: Gas Fees

We knew from the beginning that Ethereum - with its sky high gas fees - would not be a viable option for Make It Snow. Imagine if each transaction cost $50; how much would the giveaway have to drop for the transactions to make sense?

Thankfully, there are L2s. We gleefully developed on Arbitrum thinking “gas fees are solved.” Sadly, we weren’t thinking much and didn’t prioritize validating the amount of gas fees needed. When we got our code to work, we were shocked to find out that even on Arbitrum, the gas fees would be ~$5-10 for our contract. That is still economically infeasible for most giveaways.

In the end, we had to find a chain with even smaller gas fees; that’s where Avalanche came in. To be honest, we know very little about the Avalanche ecosystem. It was simply one of the few options where gas fees were low enough for this contract to be economically viable.

Bots, front running bots, and gas fees were the primary problems we faced during our development of Make It Snow. You’ll notice that we had to introduce two transactions in order to solve the bot problem, and that is a huge sacrifice on the user experience. Turns out there’s another way to tackle this problem: I’ll cover that in the next post.

Subscribe to Gilbert
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.