diff --git a/src/RaidGeld.sol b/src/RaidGeld.sol index 31282f4..c3d2afa 100644 --- a/src/RaidGeld.sol +++ b/src/RaidGeld.sol @@ -107,9 +107,6 @@ contract RaidGeld is ERC20, Ownable { function addUnit(uint8 unit, uint16 n_units) external onlyPlayer { require(unit <= 3, "Unknown unit"); - // First trigger a raid so player receives what he is due at to this moment - performRaid(msg.sender); - Army storage army = armies[msg.sender]; uint16 currentLevel = 0; if (unit == 0) { @@ -128,6 +125,13 @@ contract RaidGeld is ERC20, Ownable { uint256 cost = RaidGeldUtils.calculateUnitPrice(unit, currentLevel, n_units) * 10 ** decimals(); require(balanceOf(msg.sender) > cost, "Not enough GELD to add this much"); + + // TODO: Since we are first minting then burning the token, this could be simplified + // by first calculating the difference and then minting / burning in just one operation + + // First trigger a raid so player receives what he is due at to this moment + performRaid(msg.sender); + // then burn the cost of the new army _burn(msg.sender, cost); // Increase level diff --git a/src/RaidGeldUtils.sol b/src/RaidGeldUtils.sol index c47a3ba..5a702c7 100644 --- a/src/RaidGeldUtils.sol +++ b/src/RaidGeldUtils.sol @@ -6,14 +6,14 @@ import {Army} from "../src/RaidGeld.sol"; library RaidGeldUtils { function calculateUnitPrice(uint8 unit, uint16 currentLevel, uint16 units) internal pure returns (uint256) { require(unit <= 3, "No matching unit found"); - uint256 rollingPriceCalculation = uint256(unit) * 38; - uint256 price = 0; + uint256 rollingPriceCalculation = uint256(unit + 1) * 38; + uint256 price = rollingPriceCalculation; // Each level costs 15% more than previous uint256 PERCENT_INCREASE = 115; for (uint256 i = 1; i < currentLevel + units; i++) { rollingPriceCalculation = rollingPriceCalculation * PERCENT_INCREASE / 100; - if (i > currentLevel) { + if (i >= currentLevel) { price += rollingPriceCalculation; } } diff --git a/test/RaidGeld.t.sol b/test/RaidGeld.t.sol index b73650b..6f31f6e 100644 --- a/test/RaidGeld.t.sol +++ b/test/RaidGeld.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.13; import {Test, console} from "forge-std/Test.sol"; import {RaidGeld, Army, Player} from "../src/RaidGeld.sol"; +import "../src/RaidGeldUtils.sol"; contract raid_geldTest is Test { RaidGeld public raid_geld; @@ -113,23 +114,26 @@ contract raid_geldTest is Test { vm.expectRevert(); raid_geld.addUnit(1, 100); - // Add 2 units 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; - raid_geld.addUnit(0, 2); - - // TODO: maybe try to test against exact values we want here + + // Add 1 unit + raid_geld.addUnit(0, 1); + uint256 unitPrice = RaidGeldUtils.calculateUnitPrice(0, 0, 1) * 10 ** 4; // 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); - assertGt(newBalance, balance - 2 * 10 ** 4); + 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 + 2); + assertEq(new_unit_level, unit_level + 1); // Check that user income per second increased uint256 new_income_per_sec = army.profit_per_second; @@ -143,7 +147,9 @@ contract raid_geldTest is Test { // Register player 1 vm.startPrank(player1); registerPlayer(); - raid_geld.addUnit(1, 2); + + // bought 1 moloch_denier + raid_geld.addUnit(0, 1); uint256 balance = raid_geld.balanceOf(player1); diff --git a/test/RaidGeldUtils.t.sol b/test/RaidGeldUtils.t.sol new file mode 100644 index 0000000..2c377ed --- /dev/null +++ b/test/RaidGeldUtils.t.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Army, Raider} from "../src/RaidGeld.sol"; +import "../src/RaidGeldUtils.sol"; + +contract raid_geldTest is Test { + function test_0_unit_price() public { + // buying 1 unit of moloch_denier + uint256 basePriceMolochDenier = RaidGeldUtils.calculateUnitPrice(0, 0, 1); + assertEq(basePriceMolochDenier, 38); + + // 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() public { + 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, 1); + + army = Army({ + moloch_denier: Raider({ level: 2}), + apprentice: Raider({ level: 0}), + anointed: Raider({ level: 10}), + champion: Raider({ level: 1}), + profit_per_second: 0 // irrelevant for this test + }); + profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army); + assertEq(profits_per_second, 640); + } +}