This is an entry for OP Game's Gitcoin Round 10 bounty.
- Unity 2020.1+
- Eth Brownie
To qualify for faucet use, users can be required to own a certain amount of tokens on either Ethereum and/or Binance Smart Chain network (among other EVM chains). During development, you can run the minting brownie script to grant yourself enough tokens.
You can view example validators here & here
The way to run validation/gate-keeping is via the game application itself, since it is network agnostic. The validation happens on-chain with TokenHolderThresholdValidator contracts. BSC contracts can't directly call Ethereum contracts, so we have the game independently call each network's validator contracts. All validators are required to succeed before granting rewards. These validators simply check if you have enough of a particular token to qualify for the faucet (aka, you meet the threshold of tokens held). You can deploy a faucet without validators; this feature is entirely optional.
You can specify the maximum payout amount when creating the smart contract instance on the blockchain. In this current template, the game design has a variable score and total where score <= total
. The difference between score and total form the ratio that is factored into the max payout:
FinalPayout = (Score/Total) * MaxPayout
Due to how fixed point math works in EVM, the actual implementation in Solidity is refactored.
Once the smart contract has been manually deployed, the game must be built with the updated smart contract info:
- Mainnet/Testnet infura API address.
- Block explorer base address.
- Faucet smart contract address.
- Designated gas wallet address to cover expenses.
- Designated gas wallet private key to sign faucet grant transactions.
Needless to say, don't save the production private key into the project. You should only enter it manually prior to building the WebGL project.
I'm not exactly a web dev so I had to rely on miscelaneous tutorials to get the WebGL properly bootstrapped with Web3 plugins such as Metamask. Brave Browser doesn't work, for example. The Metamask button on the start screen has only worked when running on Chrome with Metamask being the sole Web3 provider installed. This is likely the culprit: https://medium.com/valist/how-to-connect-web3-js-to-metamask-in-2020-fee2b2edf58a
After playing a session (win or lose), the player can bypass the retry delay period simply by reloading the page immediately. A user could do this consecutively to illegitimately boost their rewards, potentially draining the faucet. This happens because time tracking for each player is done via block timestamps in Solidity. The delay period for any player is only updated on the next block. The solution here is to either:
- Query the mempool to validate whether a player has pending payouts (including zero amounts).
- Centralize the deployment by saving some of the game session data on a persistent backend, possibly even the website's own database.
There may be multiple players interacting with the faucet simultaneously. Because we only check for balance before starting a game, the faucet may become depleted by another player's payout before your game ends. When requesting your payout, the transaction will fail because there is not enough balance remaining. Similar to above, the solution here is to either:
- Query the mempool for pending payouts and pre-calculate the maximum collective payout to determine if there is enough for a 100% payout game session.
- Centralize the deployment by saving some of the game session data on a persistent backend, possibly even the website's own database.
The project includes an example scene [Assets/Faucet/Demo.unity] where you can preview basic functionality.
For a full-integration demonstration, see the Coin Catch game repository.