idle_moloch/test/RaidGeld.t.sol
2024-11-09 22:11:53 +01:00

457 lines
17 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {stdStorage, StdStorage} from "forge-std/Test.sol";
import {RaidGeld, Army, Player, Boss, LastBossResult} from "../src/RaidGeld.sol";
import "../src/RaidGeldUtils.sol";
import {Constants} from "../src/Constants.sol";
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
contract raid_geldTest is Test, Constants {
using stdStorage for StdStorage;
RaidGeld public raid_geld;
address public player1;
address public player2;
address public owner;
event Approval(address indexed owner, address indexed spender, uint256 value);
event PlayerRegistered(address indexed player, uint256 initialGeld);
event RaidPerformed(address indexed player, uint256 totalMinted, uint256 geldBalance);
event UnitAdded(
address indexed player,
uint8 unitType,
uint16 nUnits,
uint256 cost,
uint256 geldBalance,
uint16 molochDenierLevel,
uint16 apprenticeLevel,
uint16 anointedLevel,
uint16 championLevel
);
function setUp() public {
owner = address(0x126);
player1 = address(0x123);
vm.deal(owner, 10 ether);
fundAccount(player1);
vm.prank(owner);
raid_geld = new RaidGeld(DAO_TOKEN, POOL, BAAL, NFT_POSITION_MANAGER);
raid_geld.weth().deposit{value: 5 ether}();
}
function fundAccount(address _acc) private {
vm.deal(_acc, 10 ether);
console.log("dao token", DAO_TOKEN);
stdstore.target(DAO_TOKEN).sig("balanceOf(address)").with_key(_acc).checked_write(1000 ether);
}
function registerPlayer() private {
raid_geld.registerEth{value: raid_geld.BUY_IN_AMOUNT()}();
}
function registerPlayerWithDaoToken() private {
raid_geld.daoToken().approve(address(raid_geld), raid_geld.BUY_IN_DAO_TOKEN_AMOUNT());
raid_geld.registerDaoToken();
}
function test_00_no_fallback() public {
vm.expectRevert();
// Send Ether with some data to trigger fallback
(bool success,) = address(raid_geld).call{value: 0.1 ether}("0x1234");
if (success) this;
}
function test_01_no_receive() public {
vm.startPrank(player1);
vm.expectRevert();
payable(address(raid_geld)).transfer(0.1 ether);
}
function test_02_1_registrationWithEth() public {
vm.startPrank(player1);
uint256 contractBalance = raid_geld.daoToken().balanceOf(address(raid_geld));
uint256 userBalance = address(player1).balance;
// Making sure event is emitted when player is registered
vm.expectEmit(address(raid_geld));
emit PlayerRegistered(player1, raid_geld.INITIAL_GELD());
registerPlayer();
// Check that initialraid_geld.is received by the player
assertEq(raid_geld.balanceOf(player1), raid_geld.INITIAL_GELD());
// Verify the contract balance is updated
uint256 contractBalance2 = raid_geld.daoToken().balanceOf(address(raid_geld));
uint256 userBalance2 = address(player1).balance;
// Contract should get DAO tokens
assertLt(contractBalance, contractBalance2);
// Player should lose ETH
assertEq(userBalance2, userBalance - raid_geld.BUY_IN_AMOUNT());
// Verify player is set initially
Player memory player = raid_geld.getPlayer(player1);
assertEq(player.total_minted, raid_geld.INITIAL_GELD());
assertEq(player.created_at, block.timestamp);
assertEq(player.last_raided_at, block.timestamp);
Army memory army = raid_geld.getArmy(player1);
assertEq(army.moloch_denier.level, 0);
assertEq(army.apprentice.level, 0);
assertEq(army.anointed.level, 0);
assertEq(army.champion.level, 0);
}
function test_02_2_registrationWithDaoToken() public {
vm.startPrank(player1);
uint256 initialBalance = raid_geld.daoToken().balanceOf(address(raid_geld));
// Making sure event is emitted when player is registered
// doesnt test player emitted event because other events get emitted before it
registerPlayerWithDaoToken();
// Check that initial raid_geld is received by the player
assertEq(raid_geld.balanceOf(player1), raid_geld.INITIAL_GELD());
// Verify the contract dao token balance is updated
uint256 expectedDaoBalance = initialBalance + raid_geld.BUY_IN_DAO_TOKEN_AMOUNT() -
raid_geld.BUY_IN_DAO_TOKEN_AMOUNT() * raid_geld.SACRIFICE_SHARE() / raid_geld.MANTISSA();
assertEq(
raid_geld.daoToken().balanceOf(address(raid_geld)), expectedDaoBalance
);
// Verify player is set initially
Player memory player = raid_geld.getPlayer(player1);
assertEq(player.total_minted, raid_geld.INITIAL_GELD());
assertEq(player.created_at, block.timestamp);
assertEq(player.last_raided_at, block.timestamp);
Army memory army = raid_geld.getArmy(player1);
assertEq(army.moloch_denier.level, 0);
assertEq(army.apprentice.level, 0);
assertEq(army.anointed.level, 0);
assertEq(army.champion.level, 0);
}
function test_03_is_registered() public {
bool is_registered = raid_geld.isRegistered(player1);
assertEq(is_registered, false);
vm.startPrank(player1);
// Making sure event is emitted when player is registered
vm.expectEmit(address(raid_geld));
emit PlayerRegistered(player1, raid_geld.INITIAL_GELD());
registerPlayer();
is_registered = raid_geld.isRegistered(player1);
assertEq(is_registered, true);
}
function test_04_add_unit() public {
vm.startPrank(player1);
// Making sure event is emitted when player is registered
vm.expectEmit(address(raid_geld));
emit PlayerRegistered(player1, raid_geld.INITIAL_GELD());
registerPlayer();
vm.expectRevert();
// units should be in range
raid_geld.addUnit(100, 1);
// Player should have enough tokens to burn to get units
vm.expectRevert();
raid_geld.addUnit(1, 100);
Army memory army = raid_geld.getArmy(player1);
uint256 unit_level = army.moloch_denier.level;
uint256 balance = raid_geld.balanceOf(player1);
uint256 income_per_sec = army.profit_per_second;
uint256 cost = RaidGeldUtils.calculateUnitPrice(0, 0, 1);
uint256 playerBalance = raid_geld.balanceOf(address(player1));
// Making sure event is emitted when player adds a unit
vm.expectEmit(address(raid_geld));
emit UnitAdded(address(player1), 0, 1, cost, playerBalance - cost, 1, 0, 0, 0);
// Add 1 unit
raid_geld.addUnit(0, 1);
uint256 unitPrice = RaidGeldUtils.calculateUnitPrice(0, 0, 1);
// Check that those tokens were burnt
// WARN: In addUnit will mint additional tokens but they are not calculated
// into this because the test doesnt move the blockchain so no extra tokens
// are minted to the user
uint256 newBalance = raid_geld.balanceOf(player1);
assertEq(newBalance, balance - unitPrice);
army = raid_geld.getArmy(player1);
// Check that unit level increased
uint256 new_unit_level = army.moloch_denier.level;
assertEq(new_unit_level, unit_level + 1);
// Check that user income per second increased
uint256 new_income_per_sec = army.profit_per_second;
assertLt(income_per_sec, new_income_per_sec);
}
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 cost = RaidGeldUtils.calculateUnitPrice(0, 0, 1);
uint256 playerBalance = raid_geld.balanceOf(address(player1));
// Making sure event is emitted when player adds a unit
vm.expectEmit(address(raid_geld));
emit UnitAdded(address(player1), 0, 1, cost, playerBalance - cost, 1, 0, 0, 0);
// bought 1 moloch_denier
raid_geld.addUnit(0, 1);
vm.warp(block.timestamp + 15);
uint256 balance = raid_geld.balanceOf(player1);
Army memory army = raid_geld.getArmy(player1);
Player memory player = raid_geld.getPlayer(player1);
uint256 amountMinted = army.profit_per_second * 15;
// Making sure event is emitted when player performs a raid
vm.expectEmit(address(raid_geld));
emit RaidPerformed(address(player1), player.total_minted + amountMinted, balance + amountMinted);
// Trigger raid funds minting
raid_geld.raid();
// New balance should be larger
uint256 newBalance = raid_geld.balanceOf(player1);
player = raid_geld.getPlayer(player1);
uint256 last_raided_at = player.last_raided_at;
assertLt(balance, newBalance);
// After wait time passes raid should bring in profits again
vm.warp(block.timestamp + 15);
amountMinted = army.profit_per_second * 15;
emit RaidPerformed(address(player1), player.total_minted + amountMinted, balance + amountMinted);
raid_geld.raid();
// Balance should reflect that
uint256 newestBalance = raid_geld.balanceOf(player1);
player = raid_geld.getPlayer(player1);
uint256 last_raided_at_2 = player.last_raided_at;
assertLt(newBalance, newestBalance);
assertLt(last_raided_at, last_raided_at_2);
}
function test_06_attack_boss() public {
// Let some time pass so we dont start at block timestamp 0
vm.warp(120);
// Register player 1
vm.startPrank(player1);
registerPlayerWithDaoToken();
raid_geld.addUnit(0, 1);
uint256 initialDaoTreasuryBalance = raid_geld.daoToken().balanceOf(DAO_TREASURY);
uint256 initialDaoBalance = raid_geld.daoToken().balanceOf(player1);
uint256 initialContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld));
Boss memory boss = raid_geld.getBoss(player1);
// assert boss is initialized
assertEq(boss.level, 0);
// make sure variants shuffled
assertNotEq(boss.variants[1], boss.variants[2]);
// Make a lot of time pass so user deffo has GELD to attack the boss
vm.warp(1200000);
bool[2] memory results = raid_geld.battleWithBoss();
// Should almost always defeat first boss
assertEq(results[0], true);
// First boss doesnt grant a new prestige level (ascension)
assertEq(results[1], false);
uint256 daoTreasuryBalance = raid_geld.daoToken().balanceOf(DAO_TREASURY);
uint256 afterBossDaoBalance = raid_geld.daoToken().balanceOf(player1);
uint256 afterBossContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld));
LastBossResult memory bossResult = raid_geld.getLastBossResult(player1);
assertEq(bossResult.battled_at, block.timestamp);
assertEq(bossResult.reward, afterBossDaoBalance - initialDaoBalance);
assertEq(bossResult.level, boss.level);
assertEq(bossResult.variant, boss.variants[boss.level]);
assertEq(bossResult.prestigeGained, false);
// User should receive funs, contract should lose them
assertLt(initialDaoTreasuryBalance, daoTreasuryBalance);
assertLt(initialDaoBalance, afterBossDaoBalance);
assertGt(initialContractBalance, afterBossContractBalance);
// Boss should level up
boss = raid_geld.getBoss(player1);
assertEq(boss.level, 1);
Player memory player = raid_geld.getPlayer(player1);
// Players total rewards should increase
assertGt(player.total_rewards, 0);
}
function test_07_attack_boss_fail() public {
// Let some time pass so we dont start at block timestamp 0
vm.warp(120);
// Register player 1
vm.startPrank(player1);
registerPlayerWithDaoToken();
raid_geld.addUnit(0, 1);
Boss memory boss = raid_geld.getBoss(player1);
bool[2] memory results = raid_geld.battleWithBoss();
// Should lose with just starting GELD
assertEq(results[0], false);
// First boss doesnt grant a new prestige level (ascension)
assertEq(results[1], false);
Player memory player = raid_geld.getPlayer(player1);
Army memory army = raid_geld.getArmy(player1);
// player sessions should end
assertEq(player.has_active_session, false);
// player should lose all geld
assertEq(raid_geld.balanceOf(player1), 0);
// Units should reset
assertEq(army.moloch_denier.level, 0);
LastBossResult memory bossResult = raid_geld.getLastBossResult(player1);
assertEq(bossResult.battled_at, block.timestamp);
assertEq(bossResult.reward, 0);
assertEq(bossResult.level, boss.level);
assertEq(bossResult.variant, boss.variants[boss.level]);
assertEq(bossResult.prestigeGained, false);
}
function test_08_player_who_lost_can_restart() public {
// Let some time pass so we dont start at block timestamp 0
vm.warp(120);
uint256 balance1 = raid_geld.daoToken().balanceOf(address(raid_geld));
// Register player 1
vm.startPrank(player1);
registerPlayerWithDaoToken();
raid_geld.addUnit(0, 1);
uint256 balance2 = raid_geld.daoToken().balanceOf(address(raid_geld));
// Contract gets DAO Tokens with first register
assertLt(balance1, balance2);
bool[2] memory results = raid_geld.battleWithBoss();
// Should lose with just starting GELD
assertEq(results[0], false);
Player memory player = raid_geld.getPlayer(player1);
// player sessions should end
assertEq(player.has_active_session, false);
assertEq(player.is_registered, true);
registerPlayerWithDaoToken();
player = raid_geld.getPlayer(player1);
assertEq(player.has_active_session, true);
uint256 balance3 = raid_geld.daoToken().balanceOf(address(raid_geld));
assertLt(balance2, balance3);
}
function test_09_player_can_gain_prestige(uint256 prevrandao) public {
vm.assume(prevrandao < type(uint256).max - 1e4);
vm.startPrank(player1);
stdstore.target(DAO_TOKEN).sig("balanceOf(address)").with_key(player1).checked_write(10000000 ether);
stdstore.target(DAO_TOKEN).sig("balanceOf(address)").with_key(address(raid_geld)).checked_write(10000000 ether);
bool success = false;
uint256 tries = 0;
uint256 streak = 0;
for (uint i = 0; i < 1000; i++) {
vm.prevrandao(prevrandao + i * 59);
bool[2] memory results;
bool alreadyLost = false;
uint256 newStreak = 0;
tries += 1;
console.log("NEW TRY, number: ", tries);
registerPlayerWithDaoToken();
// Give bajillion GELD to player so player can battle boss after boss
stdstore.target(address(raid_geld)).sig("balanceOf(address)").with_key(player1).checked_write(10000000 ether);
for (uint j = 0; j < 6; j++) {
vm.prevrandao(prevrandao + j * 7 + i * 59);
newStreak += 1;
results = raid_geld.battleWithBoss();
if (results[0] == false) {
alreadyLost = true;
break;
}
}
if (alreadyLost) {
if (newStreak > streak) {
streak = newStreak;
}
continue;
}
results = raid_geld.battleWithBoss();
if (results[0] == true && results[1] == true) {
success = true;
Player memory player = raid_geld.getPlayer(player1);
LastBossResult memory bossResult = raid_geld.getLastBossResult(player1);
assertEq(bossResult.prestigeGained, true);
vm.assertEq(player.prestige_level, 1);
vm.assertEq(player.n_runs, tries);
break;
}
}
require(success, "Player should eventually succeed");
}
function test_10_dao_seeds_uniV3_pool() public {
vm.prank(owner);
raid_geld.setDaoAddress(address(this));
assertEq(raid_geld.DAO(), address(this));
uint256 DAO_DAO_BALANCE = 10000000 ether;
uint256 DAO_TOKEN_LIQUIDITY = 10000 ether;
uint256 GELD_LIQUIDITY = DAO_TOKEN_LIQUIDITY * 10;
stdstore.target(DAO_TOKEN).sig("balanceOf(address)").with_key(address(this)).checked_write(DAO_DAO_BALANCE);
assertEq(raid_geld.daoToken().balanceOf(address(this)), DAO_DAO_BALANCE);
vm.expectRevert("Not DAO or pool already deployed");
vm.prank(owner);
raid_geld.deploySwapPool(GELD_LIQUIDITY, DAO_TOKEN_LIQUIDITY);
// Deploying pool should work, done by DAO
raid_geld.daoToken().approve(address(raid_geld), DAO_TOKEN_LIQUIDITY);
raid_geld.deploySwapPool(GELD_LIQUIDITY, DAO_TOKEN_LIQUIDITY);
address token0 = IUniswapV3Pool(raid_geld.pool()).token0();
address token1 = IUniswapV3Pool(raid_geld.pool()).token1();
if (DAO_TOKEN < address(raid_geld)) {
assertEq(token0, DAO_TOKEN);
assertEq(token1, address(raid_geld));
} else {
assertEq(token0, address(raid_geld));
assertEq(token1, DAO_TOKEN);
}
}
}