1
0
forked from mico/idle_moloch

Tests done, registration and raid mechanic

This commit is contained in:
mic0 2024-10-20 19:17:16 +02:00
parent 7cf314e625
commit 7567cc6789
Signed by: mico
GPG Key ID: A3F8023524CF1C8D
5 changed files with 175 additions and 94 deletions

View File

@ -2,18 +2,16 @@
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol"; import {Script, console} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol"; import {Geld} from "../src/Geld.sol";
contract CounterScript is Script { contract GeldScript is Script {
Counter public counter; Geld public geld;
function setUp() public {} function setUp() public {}
function run() public { function run() public {
vm.startBroadcast(); vm.startBroadcast();
geld = new Geld();
counter = new Counter();
vm.stopBroadcast(); vm.stopBroadcast();
} }
} }

View File

@ -1,14 +0,0 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
function increment() public {
number++;
}
}

View File

@ -4,33 +4,32 @@ pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/Ownable.sol";
struct Raider {
uint16 level;
}
struct Army {
Raider moloch_denier;
Raider apprentice;
Raider annointed;
Raider champion;
}
struct Player {
uint256 total_minted;
uint256 created_at;
uint256 last_raided_at;
}
contract Geld is ERC20, Ownable { contract Geld is ERC20, Ownable {
struct Raider {
uint16 level;
}
struct Army {
Raider moloch_denier;
Raider apprentice;
Raider annointed;
Raider champion;
}
struct Player {
uint256 total_minted;
uint256 created_at;
uint256 last_minted_at;
}
uint8 constant DECIMALS = 4; uint8 constant DECIMALS = 4;
uint256 public constant BUY_IN_AMOUNT = 0.0005 ether; uint256 public constant BUY_IN_AMOUNT = 0.0005 ether;
uint256 public constant GELD_PER_PURCHASE = 50 * 10 ** DECIMALS; uint256 public constant INITIAL_GELD = 50 * 10 ** DECIMALS;
uint256 public constant MINT_TIME = 15; uint256 public constant RAID_WAIT = 15;
uint256 public total_minted = 0; uint256 public total_minted = 0;
mapping(address => Player) private players; mapping(address => Player) private players;
mapping(address => Army) private armies; mapping(address => Army) private armies;
// Modifier for functions that should only be available to registered players // Modifier for functions that should only be available to registered players
modifier onlyPlayer() { modifier onlyPlayer() {
@ -41,28 +40,27 @@ contract Geld is ERC20, Ownable {
constructor() ERC20("Geld", "GELD") Ownable(msg.sender) {} constructor() ERC20("Geld", "GELD") Ownable(msg.sender) {}
// This effectively registers the user // This effectively registers the user
receive() external payable { function register() external payable {
require(players[msg.sender].created_at != 0, "Whoops, player already exists :)"); require(players[msg.sender].created_at == 0, "Whoops, player already exists :)");
require(msg.value == BUY_IN_AMOUNT, "Incorrect buy in amount"); require(msg.value == BUY_IN_AMOUNT, "Incorrect buy in amount");
_mint(msg.sender, GELD_PER_PURCHASE);
players[msg.sender] = Player({
total_minted: GELD_PER_PURCHASE,
created_at: block.timestamp,
last_minted_at: 0
});
armies[msg.sender] = Army({
moloch_denier: Raider({ level: 0 }),
apprentice: 0,
annointed: 0,
champion: 0
})
total_minted += GELD_PER_PURCHASE; // Mint some starting tokens to the player
_mint(msg.sender, INITIAL_GELD);
// Set initial states
players[msg.sender] = Player({total_minted: INITIAL_GELD, created_at: block.timestamp, last_raided_at: 0});
armies[msg.sender] = Army({
moloch_denier: Raider({level: 0}),
apprentice: Raider({level: 0}),
annointed: Raider({level: 0}),
champion: Raider({level: 0})
});
total_minted += INITIAL_GELD;
} }
// Override for default number of decimals // Override for default number of decimals
function decimals() public view virtual override returns (uint8) { function decimals() public view virtual override returns (uint8) {
return DECIMALS; return 4;
} }
// Allows the owner to withdraw // Allows the owner to withdraw
@ -71,13 +69,13 @@ contract Geld is ERC20, Ownable {
} }
// Manual minting for itchy fingers // Manual minting for itchy fingers
function manualMint() external onlyPlayer { function raid() external onlyPlayer {
require(block.timestamp >= players[msg.sender].last_minted_at + MINT_TIME, "Tried minting too soon"); require(block.timestamp >= players[msg.sender].last_raided_at + RAID_WAIT, "Tried minting too soon");
// TODO: Make real calculation based on army
_mint(msg.sender, 50);
players[msg.sender].last_minted_at = block.timestamp; // TODO: Make real calculation based on army
_mint(msg.sender, 50 * 10 ** decimals());
players[msg.sender].last_raided_at = block.timestamp;
} }
// Function to get Player struct // Function to get Player struct
@ -89,4 +87,13 @@ contract Geld is ERC20, Ownable {
function getArmy() public view onlyPlayer returns (Army memory) { function getArmy() public view onlyPlayer returns (Army memory) {
return armies[msg.sender]; return armies[msg.sender];
} }
receive() external payable {
revert("No plain Ether accepted, use register() function to check in :)");
}
// Revert any non-function-call Ether transfers or calls to non-existent functions
fallback() external payable {
revert("No fallback calls accepted");
}
} }

View File

@ -1,24 +0,0 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
counter.setNumber(0);
}
function test_Increment() public {
counter.increment();
assertEq(counter.number(), 1);
}
function testFuzz_SetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
}

View File

@ -1,16 +1,130 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import { Test, console } from "forge-std/Test.sol"; import {Test, console} from "forge-std/Test.sol";
import { Geld } from "../src/Geld.sol"; import {Geld, Army, Player} from "../src/Geld.sol";
contract Gels is Test { contract GeldTest is Test {
Geld public geld; Geld public geld;
address public player1;
address public player2;
address public owner;
function setUp() public { function setUp() public {
geld = new Geld(); owner = address(0x126);
} player1 = address(0x123);
player2 = address(0x124);
vm.deal(owner, 10 ether);
vm.deal(player1, 10 ether);
vm.deal(player2, 10 ether);
vm.prank(owner);
geld = new Geld();
}
function registerPlayer() private {
geld.register{value: geld.BUY_IN_AMOUNT()}();
}
function test_00_no_fallback() public {
vm.expectRevert();
// Send Ether with some data to trigger fallback
address(geld).call{value: 0.1 ether}("0x1234");
}
function test_01_no_receive() public {
vm.startPrank(player1);
vm.expectRevert();
payable(address(geld)).transfer(0.1 ether);
}
function test_02_registration() public {
vm.startPrank(player1);
uint256 initialBalance = address(geld).balance;
uint256 initialTotalMinted = geld.total_minted();
// Send registration fee ETH to the contract
registerPlayer();
// Check that initial GELD is received by the player
assertEq(geld.balanceOf(player1), geld.INITIAL_GELD());
// Verify the contract balance is updated
assertEq(address(geld).balance, initialBalance + geld.BUY_IN_AMOUNT());
// Verify player is set initially
Player memory player = geld.getPlayer();
assertEq(player.total_minted, geld.INITIAL_GELD());
assertEq(player.created_at, block.timestamp);
assertEq(player.last_raided_at, 0);
Army memory army = geld.getArmy();
assertEq(army.moloch_denier.level, 0);
assertEq(army.apprentice.level, 0);
assertEq(army.annointed.level, 0);
assertEq(army.champion.level, 0);
// Verify that total_minted is updated
assertEq(geld.total_minted(), initialTotalMinted + geld.INITIAL_GELD());
}
function test_03_fundsCanBeWithdrawn() public {
uint256 initialBalance = owner.balance;
// Switch to Player 1 and register it
vm.startPrank(player1);
registerPlayer();
// Switch back to owner and withdraw funds
vm.startPrank(owner);
geld.withdraw();
uint256 newBalance = owner.balance;
uint256 newContractBalance = address(geld).balance;
// contract balance should be empty
assertEq(newContractBalance, 0);
// owner should have the extra funds
assertEq(newBalance, initialBalance + geld.BUY_IN_AMOUNT());
}
function test_04_onlyOwnerCanWithdraw() public {
// Register player 1
vm.startPrank(player1);
registerPlayer();
// attempt to withdraw with player 1, it should fail
vm.expectRevert();
geld.withdraw();
}
function test_05_raid() public {
// Let some time pass so we dont start at block timestamp 0
vm.warp(120);
// Register player 1
vm.startPrank(player1);
registerPlayer();
uint256 balance = geld.balanceOf(player1);
// Trigger raid funds minting
geld.raid();
// New balance should be larger
uint256 newBalance = geld.balanceOf(player1);
assertLt(balance, newBalance);
// Expect fail if we raid again, we need to wait a bit
vm.expectRevert();
geld.raid();
// After wait time passes raid should work again
vm.warp(block.timestamp + geld.RAID_WAIT());
geld.raid();
// Balance should reflect that
uint256 newestBalance = geld.balanceOf(player1);
assertLt(newBalance, newestBalance);
}
} }