Skip to content

Commit

Permalink
Monad devnet deployment and Faucet feature (#17)
Browse files Browse the repository at this point in the history
* added faucet feature

* enforces cooldown after claims

* fixes responsiveness for gameover screen

* updated env.example

* fixing chain errors

* removed sepolia support

* reset score to zero after claiming

* block spammers from abusing disburse

* fix incorrect claim mapping
  • Loading branch information
velikanghost authored Feb 16, 2025
1 parent f5bdce7 commit 4a8b5cd
Show file tree
Hide file tree
Showing 37 changed files with 1,634 additions and 218 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged --verbose
#yarn lint-staged --verbose
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
A web-based mini game built with [Scaffold-ETH 2](https://github.com/scaffold-eth/scaffold-eth-2).

Latest version is [here](https://nextjs-seven-liard-31.vercel.app/).
Deployed on Sepolia Testnet.
Deployed on Monad Testnet.

## 🎮 Game Overview

Protect your precious croissants from the invading mouches (flies)! Click or tap the screen to swat them away.

### Controls

- **Click/Tap**: Swat flies
- **Special**: Unlock the Golden Swatter NFT for enhanced swatting power!

Expand All @@ -18,6 +19,7 @@ Protect your precious croissants from the invading mouches (flies)! Click or tap
### Prerequisites

Before you begin, ensure you have the following installed:

- Node.js (>= v18.18)
- Yarn (v1 or v2+)
- Git
Expand All @@ -27,19 +29,20 @@ For detailed environment setup, follow the [Scaffold-ETH 2 Documentation](https:
### Installation

1. Clone the repository
```bash
git clone https://github.com/SotoAlt/smack-mouch-nextjs-main.git
cd smack-mouch-nextjs-main
```
```bash
git clone https://github.com/SotoAlt/smack-mouch-nextjs-main.git
cd smack-mouch-nextjs-main
```
2. Install dependencies
```bash
yarn install
```

```bash
yarn install
```

3. Start the development server
```bash
yarn start
```
```bash
yarn start
```

## 🤝 Contributing

Expand All @@ -52,6 +55,7 @@ Contributions are always welcome! Here's how you can help:
5. Open a Pull Request

Please make sure to:

- Open an issue first to discuss proposed changes
- Follow the existing code style
- Update documentation as needed
Expand All @@ -60,6 +64,7 @@ Please make sure to:
## 🐛 Bug Reports

Found a bug? Please use our bug report template when creating an issue and include:

- Detailed description of the issue
- Steps to reproduce
- Expected behavior
Expand Down
84 changes: 84 additions & 0 deletions packages/hardhat/contracts/MouchDrops.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MouchDrops {
address public owner;
address public admin;
uint256 public cooldownPeriod = 10 minutes;
uint256 public maxDisbursement = 30 ether;
mapping(address => uint256) public lastRequestTime;

event EtherReceived(address indexed sender, uint256 amount);
event OwnerWithdraw(address indexed owner, uint256 amount);

constructor(address _admin) {
owner = msg.sender;
admin = _admin;
}

modifier onlyOwner() {
require(msg.sender == owner, "Caller is not the owner");
_;
}

modifier onlyAdmin() {
require(msg.sender == admin, "Not authorized!");
_;
}

receive() external payable {
emit EtherReceived(msg.sender, msg.value);
}

function updateAdmin(address _newAdmin) external onlyOwner {
admin = _newAdmin;
}

function disburse(
address payable recipient,
uint256 amount
) external onlyAdmin {
require(recipient != address(0), "Invalid recipient");
require(amount > 0 && amount <= maxDisbursement, "Invalid amount");
require(
address(this).balance >= amount,
"Insufficient contract balance"
);
require(
block.timestamp >= lastRequestTime[recipient] + cooldownPeriod,
"Cooldown period not yet expired"
);
lastRequestTime[recipient] = block.timestamp;

// ✅ Send Ether to the recipient
(bool sent, ) = recipient.call{ value: amount }("");
require(sent, "Failed to disburse rewards");
}

function setCooldownPeriod(uint256 newCooldownPeriod) external onlyOwner {
cooldownPeriod = newCooldownPeriod;
}

function setMaxDisbursement(uint256 newMaxDisbursement) external onlyOwner {
require(
newMaxDisbursement > 0,
"Max disbursement must be greater than zero"
);
maxDisbursement = newMaxDisbursement;
}

function withdrawAll() external onlyOwner {
uint256 contractBalance = address(this).balance;
require(contractBalance > 0, "No funds to withdraw");

(bool sent, ) = owner.call{ value: contractBalance }("");
require(sent, "Failed to withdraw funds");

emit OwnerWithdraw(owner, contractBalance);
}

function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "New owner cannot be zero address");
owner = newOwner;
}
}
22 changes: 22 additions & 0 deletions packages/hardhat/deploy/01_deploy_mouch_drops.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre;
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();

const constructorArg = "0x41f3C185123a4519f30eE1507cfd146e85a59a22";

// Ensure compilation before deployment
await hre.run("compile");

await deploy("MouchDrops", {
from: deployer,
args: [constructorArg], // Add constructor arguments here if any
log: true,
});
};

export default func;
func.tags = ["MouchDrops"];
1 change: 1 addition & 0 deletions packages/hardhat/deployments/monadDevnet/.chainId
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20143
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"address": "0xd7999DB02BC211d03147C6f4deE91a61f990dd1b",
"address": "0x6D161991630b676119A81860B42eCBF5D99199A2",
"abi": [
{
"anonymous": false,
Expand Down Expand Up @@ -134,25 +134,25 @@
"type": "function"
}
],
"transactionHash": "0xba77d39469c8b8ad6cf8c8f87be259fc9661b23dd6afeeffd590dbac0b1af884",
"transactionHash": "0xaefec8401def3735ebe2a95a38fb9c5a2c34502b537460bec2f4e4f957a1ec3a",
"receipt": {
"to": null,
"from": "0x29ed0e0006A5acA268f9aff3d96152649Dc27fdA",
"contractAddress": "0xd7999DB02BC211d03147C6f4deE91a61f990dd1b",
"transactionIndex": 48,
"from": "0x28738040d191ff30673f546FB6BF997E6cdA6dbF",
"contractAddress": "0x6D161991630b676119A81860B42eCBF5D99199A2",
"transactionIndex": 0,
"gasUsed": "364641",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0x383fde7962db4182b27e5d8bd56c0c7bd19fc72e58dc57e5993e8fbd1d92f438",
"transactionHash": "0xba77d39469c8b8ad6cf8c8f87be259fc9661b23dd6afeeffd590dbac0b1af884",
"blockHash": "0x7a8d187c5e5780eb485c2ae23bfbfdf3cec44bf3c7cd0734d7b30371d1cf25c1",
"transactionHash": "0xaefec8401def3735ebe2a95a38fb9c5a2c34502b537460bec2f4e4f957a1ec3a",
"logs": [],
"blockNumber": 6990683,
"cumulativeGasUsed": "6289659",
"blockNumber": 2610784,
"cumulativeGasUsed": "364641",
"status": 1,
"byzantium": true
},
"args": [],
"numDeployments": 1,
"solcInputHash": "e823c74db6caf3b2d292269081c40f00",
"solcInputHash": "c6686906fcbba2d64277ec9a1a88a1b8",
"metadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"player\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"score\",\"type\":\"uint256\"}],\"name\":\"NewHighScore\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MAX_TOP_SCORES\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"player\",\"type\":\"address\"}],\"name\":\"getHighScore\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTopScores\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"player\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"score\",\"type\":\"uint256\"}],\"internalType\":\"struct GameScore.Score[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"playerHighScores\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"score\",\"type\":\"uint256\"}],\"name\":\"setHighScore\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"topScores\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"player\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"score\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/GameScore.sol\":\"GameScore\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/GameScore.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.20;\\n\\ncontract GameScore {\\n struct Score {\\n address player;\\n uint256 score;\\n }\\n\\n Score[] public topScores;\\n uint256 public constant MAX_TOP_SCORES = 10;\\n\\n mapping(address => uint256) public playerHighScores;\\n\\n event NewHighScore(address player, uint256 score);\\n\\n function setHighScore(uint256 score) external {\\n if (score > playerHighScores[msg.sender]) {\\n playerHighScores[msg.sender] = score;\\n emit NewHighScore(msg.sender, score);\\n _updateTopScores(msg.sender, score);\\n }\\n }\\n\\n function getHighScore(address player) external view returns (uint256) {\\n return playerHighScores[player];\\n }\\n\\n function getTopScores() external view returns (Score[] memory) {\\n return topScores;\\n }\\n\\n function _updateTopScores(address player, uint256 score) internal {\\n uint256 index = topScores.length;\\n for (uint256 i = 0; i < topScores.length; i++) {\\n if (score > topScores[i].score) {\\n index = i;\\n break;\\n }\\n }\\n\\n if (index < MAX_TOP_SCORES) {\\n if (topScores.length < MAX_TOP_SCORES) {\\n topScores.push(Score(player, score));\\n } else {\\n for (uint256 i = MAX_TOP_SCORES - 1; i > index; i--) {\\n topScores[i] = topScores[i - 1];\\n }\\n topScores[index] = Score(player, score);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x689d0c071092cf6fa43850cc0c2b3fb109e09e3ab3172a740677f2c01d79ac95\",\"license\":\"MIT\"}},\"version\":1}",
"bytecode": "0x608060405234801561001057600080fd5b506105a2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80635112711a1461006757806357171a481461009e5780638b4dc1c2146100d55780639271b997146100f5578063dbdd4eb21461010a578063ee5c4e5d14610112575b600080fd5b61007a610075366004610456565b610127565b604080516001600160a01b0390931683526020830191909152015b60405180910390f35b6100c76100ac36600461046f565b6001600160a01b031660009081526001602052604090205490565b604051908152602001610095565b6100c76100e336600461046f565b60016020526000908152604090205481565b610108610103366004610456565b61015f565b005b6100c7600a81565b61011a6101ce565b604051610095919061049f565b6000818154811061013757600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b336000908152600160205260409020548111156101cb5733600081815260016020908152604091829020849055815192835282018390527fe265ae360c48dc3c258727b698cc5a994da69b0d32354166f39003a488919443910160405180910390a16101cb3382610243565b50565b60606000805480602002602001604051908101604052809291908181526020016000905b8282101561023a576000848152602090819020604080518082019091526002850290910180546001600160a01b031682526001908101548284015290835290920191016101f2565b50505050905090565b60008054905b6000548110156102995760008181548110610266576102666104f7565b90600052602060002090600202016001015483111561028757809150610299565b8061029181610523565b915050610249565b50600a81101561045157600054600a111561034357604080518082019091526001600160a01b038481168252602082018481526000805460018101825590805292517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563600290940293840180546001600160a01b0319169190931617909155517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56490910155505050565b60006103516001600a61053c565b90505b818111156103e457600061036960018361053c565b81548110610379576103796104f7565b90600052602060002090600202016000828154811061039a5761039a6104f7565b60009182526020909120825460029092020180546001600160a01b0319166001600160a01b03909216919091178155600191820154910155806103dc81610555565b915050610354565b506040518060400160405280846001600160a01b031681526020018381525060008281548110610416576104166104f7565b600091825260209182902083516002929092020180546001600160a01b0319166001600160a01b039092169190911781559101516001909101555b505050565b60006020828403121561046857600080fd5b5035919050565b60006020828403121561048157600080fd5b81356001600160a01b038116811461049857600080fd5b9392505050565b602080825282518282018190526000919060409081850190868401855b828110156104ea57815180516001600160a01b031685528601518685015292840192908501906001016104bc565b5091979650505050505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016105355761053561050d565b5060010190565b8181038181111561054f5761054f61050d565b92915050565b6000816105645761056461050d565b50600019019056fea2646970667358221220531d5053f65bec9ab8fc5a2d1b4cb1a31f4dba222e99cbd490f137c5e97ab59c64736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100625760003560e01c80635112711a1461006757806357171a481461009e5780638b4dc1c2146100d55780639271b997146100f5578063dbdd4eb21461010a578063ee5c4e5d14610112575b600080fd5b61007a610075366004610456565b610127565b604080516001600160a01b0390931683526020830191909152015b60405180910390f35b6100c76100ac36600461046f565b6001600160a01b031660009081526001602052604090205490565b604051908152602001610095565b6100c76100e336600461046f565b60016020526000908152604090205481565b610108610103366004610456565b61015f565b005b6100c7600a81565b61011a6101ce565b604051610095919061049f565b6000818154811061013757600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b336000908152600160205260409020548111156101cb5733600081815260016020908152604091829020849055815192835282018390527fe265ae360c48dc3c258727b698cc5a994da69b0d32354166f39003a488919443910160405180910390a16101cb3382610243565b50565b60606000805480602002602001604051908101604052809291908181526020016000905b8282101561023a576000848152602090819020604080518082019091526002850290910180546001600160a01b031682526001908101548284015290835290920191016101f2565b50505050905090565b60008054905b6000548110156102995760008181548110610266576102666104f7565b90600052602060002090600202016001015483111561028757809150610299565b8061029181610523565b915050610249565b50600a81101561045157600054600a111561034357604080518082019091526001600160a01b038481168252602082018481526000805460018101825590805292517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563600290940293840180546001600160a01b0319169190931617909155517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56490910155505050565b60006103516001600a61053c565b90505b818111156103e457600061036960018361053c565b81548110610379576103796104f7565b90600052602060002090600202016000828154811061039a5761039a6104f7565b60009182526020909120825460029092020180546001600160a01b0319166001600160a01b03909216919091178155600191820154910155806103dc81610555565b915050610354565b506040518060400160405280846001600160a01b031681526020018381525060008281548110610416576104166104f7565b600091825260209182902083516002929092020180546001600160a01b0319166001600160a01b039092169190911781559101516001909101555b505050565b60006020828403121561046857600080fd5b5035919050565b60006020828403121561048157600080fd5b81356001600160a01b038116811461049857600080fd5b9392505050565b602080825282518282018190526000919060409081850190868401855b828110156104ea57815180516001600160a01b031685528601518685015292840192908501906001016104bc565b5091979650505050505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016105355761053561050d565b5060010190565b8181038181111561054f5761054f61050d565b92915050565b6000816105645761056461050d565b50600019019056fea2646970667358221220531d5053f65bec9ab8fc5a2d1b4cb1a31f4dba222e99cbd490f137c5e97ab59c64736f6c63430008140033",
Expand All @@ -169,15 +169,15 @@
"storageLayout": {
"storage": [
{
"astId": 10,
"astId": 2947,
"contract": "contracts/GameScore.sol:GameScore",
"label": "topScores",
"offset": 0,
"slot": "0",
"type": "t_array(t_struct(Score)6_storage)dyn_storage"
"type": "t_array(t_struct(Score)2943_storage)dyn_storage"
},
{
"astId": 17,
"astId": 2954,
"contract": "contracts/GameScore.sol:GameScore",
"label": "playerHighScores",
"offset": 0,
Expand All @@ -191,8 +191,8 @@
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_struct(Score)6_storage)dyn_storage": {
"base": "t_struct(Score)6_storage",
"t_array(t_struct(Score)2943_storage)dyn_storage": {
"base": "t_struct(Score)2943_storage",
"encoding": "dynamic_array",
"label": "struct GameScore.Score[]",
"numberOfBytes": "32"
Expand All @@ -204,20 +204,20 @@
"numberOfBytes": "32",
"value": "t_uint256"
},
"t_struct(Score)6_storage": {
"t_struct(Score)2943_storage": {
"encoding": "inplace",
"label": "struct GameScore.Score",
"members": [
{
"astId": 3,
"astId": 2940,
"contract": "contracts/GameScore.sol:GameScore",
"label": "player",
"offset": 0,
"slot": "0",
"type": "t_address"
},
{
"astId": 5,
"astId": 2942,
"contract": "contracts/GameScore.sol:GameScore",
"label": "score",
"offset": 0,
Expand Down
Loading

0 comments on commit 4a8b5cd

Please sign in to comment.