436 lines
16 KiB
Solidity
436 lines
16 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} 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);
|
|
stdstore.target(DAO_TOKEN).sig("balanceOf(address)").with_key(_acc).checked_write(1000 ether);
|
|
}
|
|
|
|
function registerPlayer() private {
|
|
raid_geld.register_eth{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.register_dao();
|
|
}
|
|
|
|
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 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.battle_with_boss();
|
|
// Should almost always defeat first boss
|
|
assertEq(results[0], true);
|
|
// First boss doesnt grant a new prestige level (ascension)
|
|
assertEq(results[1], false);
|
|
|
|
uint256 afterBossDaoBalance = raid_geld.daoToken().balanceOf(player1);
|
|
uint256 afterBossContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld));
|
|
|
|
// User should receive funs, contract should lose them
|
|
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);
|
|
|
|
bool[2] memory results = raid_geld.battle_with_boss();
|
|
// 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);
|
|
}
|
|
|
|
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.battle_with_boss();
|
|
// 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.battle_with_boss();
|
|
if (results[0] == false) {
|
|
alreadyLost = true;
|
|
break;
|
|
}
|
|
}
|
|
if (alreadyLost) {
|
|
if (newStreak > streak) {
|
|
streak = newStreak;
|
|
}
|
|
continue;
|
|
}
|
|
results = raid_geld.battle_with_boss();
|
|
if (results[0] == true && results[1] == true) {
|
|
success = true;
|
|
Player memory player = raid_geld.getPlayer(player1);
|
|
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);
|
|
}
|
|
|
|
}
|
|
}
|