// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import {Test, console} from "forge-std/Test.sol"; import {Army, Raider} from "../src/RaidGeldStructs.sol"; import "../src/RaidGeldUtils.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import {RaidGeld} from "../src/RaidGeld.sol"; import {Constants} from "../src/Constants.sol"; contract raid_geldTest is Test, Constants { RaidGeld public raid_geld; address public owner; function setUp() public { owner = address(0x126); vm.deal(owner, 10 ether); vm.prank(owner); // raid_geld = new RaidGeld(DAO_TOKEN, POOL, BAAL, NFT_POSITION_MANAGER); raid_geld.weth().deposit{value: 5 ether}(); } function test_0_unit_price() public pure { // buying 1 unit of moloch_denier uint256 basePriceMolochDenier = RaidGeldUtils.calculateUnitPrice(0, 0, 1); assertEq(basePriceMolochDenier, RaidGeldUtils.MOLOCH_DENIER_BASE_COST); // buying 3 units // has to be a bit more than 3 * 38 = 114 uint256 price = RaidGeldUtils.calculateUnitPrice(0, 0, 3); assertGt(price, 3 * basePriceMolochDenier); uint256 basePriceChamp = RaidGeldUtils.calculateUnitPrice(0, 0, 3); // buying 3 units of champions // has to be a bit more than 4 * 38 * 3 = 114 price = RaidGeldUtils.calculateUnitPrice(3, 0, 3); assertGt(price, 3 * basePriceChamp); // buying 12 units of champions when u already have 10 price = RaidGeldUtils.calculateUnitPrice(3, 10, 12); // price should scale up assertGt(price, basePriceChamp * 12); } function test_1_profits_per_second(uint16 _dLvl, uint16 _apLvl, uint16 _anLvl, uint16 _cLvl) public pure { Army memory army = Army({ moloch_denier: Raider({level: 1}), apprentice: Raider({level: 0}), anointed: Raider({level: 0}), champion: Raider({level: 0}), profit_per_second: 0 // irrelevant for this test }); uint256 profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army); assertEq(profits_per_second, RaidGeldUtils.MOLOCH_DENIER_PROFIT); army = Army({ moloch_denier: Raider({level: _dLvl}), apprentice: Raider({level: _apLvl}), anointed: Raider({level: _anLvl}), champion: Raider({level: _cLvl}), profit_per_second: 0 // irrelevant for this test }); profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army); uint256 expected = _dLvl * RaidGeldUtils.MOLOCH_DENIER_PROFIT + _apLvl * RaidGeldUtils.APPRENTICE_PROFIT + _anLvl * RaidGeldUtils.ANOINTED_PROFIT + _cLvl * RaidGeldUtils.CHAMPION_PROFIT; assertEq(profits_per_second, expected); } function getConfidenceInterval(uint8 bossLevel) internal pure returns (uint256 lower, uint256 upper) { /* man i struggled getting this math with just integers so its easier to just do it like this (precalculated): z = 1.96 (95%) Probability | Expected | Variance | Margin | Lower Bound | Upper Bound 99% 990 0.00099 62 928 1052 89% 890 0.000979 61 829 951 80% 800 0.00016 49 751 849 70% 700 0.00021 45 655 745 62% 620 0.0002356 48 572 668 51% 510 0.0002499 49 461 559 40% 400 0.00024 48 352 448 */ if (bossLevel == 0) { return (928, 1000); } else if (bossLevel == 1) { return (829, 951); } else if (bossLevel == 2) { return (751, 849); } else if (bossLevel == 3) { return (655, 745); } else if (bossLevel == 4) { return (572, 668); } else if (bossLevel == 5) { return (461, 559); } else if (bossLevel == 6) { return (352, 448); } } function test_21_random(uint256 seed, uint256 min, uint256 max) public pure { vm.assume(min < max); vm.assume(max < type(uint256).max); uint256 random = RaidGeldUtils.random(seed, min, max); vm.assertTrue(random >= min && random <= max, "random() fell outside its range"); } function test_22_random_range(uint256 min, uint256 max) public { vm.assume(max > min); vm.assume(max - min < 200); vm.assume(max < type(uint256).max); uint256 range = max - min + 1; bool[] memory seen = new bool[](range); uint256 count = 0; for (uint256 i = 0; i < 100000; i++) { uint256 result = RaidGeldUtils.random(block.prevrandao + i, min, max); uint256 index = result - min; if (!seen[index]) { seen[index] = true; count++; } if (count == range) break; } assertEq(count, range, "Not all values in the interval were generated"); } function test_3_calculateBossFight_probabilities() public { uint256[7] memory probabilities = [uint256(99), uint256(89), uint256(80), uint256(70), uint256(62), uint256(51), uint256(40)]; uint256 totalRuns = 1000; console.log("Checking boss rolls (1000 times)"); for (uint8 bossLevel = 0; bossLevel < 7; bossLevel++) { console.log("Boss level: ", bossLevel); uint256 bossPower = RaidGeldUtils.getBossPower(bossLevel); uint256 geldBurnt = bossPower; uint256 successCount = 0; for (uint256 testRun = 0; testRun < totalRuns; testRun++) { uint256 prevrandao = uint256(keccak256(abi.encodePacked(block.prevrandao, testRun * 1e9))); // Unique seed each time if (RaidGeldUtils.calculateBossFight(bossLevel, geldBurnt, prevrandao)) { successCount++; } } (uint256 lowerProb, uint256 upperProb) = getConfidenceInterval(bossLevel); console.log("expected at least wins", lowerProb); console.log("expected at most wins", upperProb); console.log("actual wins", successCount); console.log("----------"); vm.assertTrue(successCount >= lowerProb && successCount <= upperProb, "Success rate not within expected range"); } } function test_4_print_boss_rewards() public { uint256 total = 0; uint256 buyInAmount = 400e18; uint256 baseReward = buyInAmount - buyInAmount * raid_geld.SACRIFICE_SHARE() / raid_geld.MANTISSA(); for (uint8 i = 0; i < 7; i++) { uint256 wholeReward = RaidGeldUtils.calculateBossReward(i, baseReward); uint256 baalShare = wholeReward * raid_geld.SACRIFICE_SHARE() / raid_geld.MANTISSA(); uint256 reward = wholeReward - baalShare; console.log("Reward", i,reward); total += reward; } console.log("Total", total); } function test_5_last_boss_is_unique(uint256 _randomSeed) public { uint8[7] memory bosses = RaidGeldUtils.generate_boss_variants(_randomSeed); vm.assertEq(bosses[6], 6); } }