Tests done, registration and raid mechanic
Some checks are pending
CI / Foundry project (push) Waiting to run
Some checks are pending
CI / Foundry project (push) Waiting to run
This commit is contained in:
parent
7cf314e625
commit
7567cc6789
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
93
src/Geld.sol
93
src/Geld.sol
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
128
test/Geld.t.sol
128
test/Geld.t.sol
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user