Compare commits
5 Commits
ab43dc31a4
...
4bcf23a9ac
| Author | SHA1 | Date | |
|---|---|---|---|
| 4bcf23a9ac | |||
| 7a32129f7e | |||
| a5f1bc7a57 | |||
|
|
49b7110a6c | ||
|
|
738378d93e |
@ -8,14 +8,12 @@ const calculateBalance = (balance: bigint, perSecond: bigint, lastRaidedAt: bigi
|
|||||||
(new Date()).getTime() - parseInt(lastRaidedAt.toString()) * 1000;
|
(new Date()).getTime() - parseInt(lastRaidedAt.toString()) * 1000;
|
||||||
return (
|
return (
|
||||||
balance +
|
balance +
|
||||||
(BigInt(millisecondsSinceLastRaid) * (perSecond * BigInt(10000) /* decimals */)
|
(BigInt(millisecondsSinceLastRaid) * perSecond
|
||||||
/ BigInt(1000) /* deduct milliseconds*/))
|
/ BigInt(1000) /* deduct milliseconds*/))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const toReadable = (value: bigint, applyTokenDivision?: boolean) => {
|
export const toReadable = (value: bigint) => {
|
||||||
if (applyTokenDivision) {
|
value = value / BigInt(10000);
|
||||||
value = value / BigInt(10000);
|
|
||||||
}
|
|
||||||
const suffixes = [
|
const suffixes = [
|
||||||
{ value: BigInt('1000'), suffix: 'thousand' },
|
{ value: BigInt('1000'), suffix: 'thousand' },
|
||||||
{ value: BigInt('1000000'), suffix: 'million' },
|
{ value: BigInt('1000000'), suffix: 'million' },
|
||||||
@ -64,8 +62,8 @@ const Counter = () => {
|
|||||||
balance,
|
balance,
|
||||||
army?.profit_per_second ?? BigInt(0),
|
army?.profit_per_second ?? BigInt(0),
|
||||||
player?.last_raided_at ?? BigInt(0)
|
player?.last_raided_at ?? BigInt(0)
|
||||||
), true);
|
));
|
||||||
availableBalance.current = toReadable(balance, true);
|
availableBalance.current = toReadable(balance);
|
||||||
render();
|
render();
|
||||||
}, 100);
|
}, 100);
|
||||||
return () => clearInterval(tickInterval)
|
return () => clearInterval(tickInterval)
|
||||||
|
|||||||
@ -91,7 +91,7 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(player)
|
console.log(player, army)
|
||||||
|
|
||||||
const register = useCallback(() => {
|
const register = useCallback(() => {
|
||||||
writeContract({
|
writeContract({
|
||||||
|
|||||||
@ -4,29 +4,13 @@ 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";
|
||||||
import {RaidGeldUtils} from "../src/RaidGeldUtils.sol";
|
import {RaidGeldUtils} from "../src/RaidGeldUtils.sol";
|
||||||
|
import {Army, Player, Raider} from "../src/RaidGeldStructs.sol";
|
||||||
struct Raider {
|
|
||||||
uint16 level;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Army {
|
|
||||||
Raider moloch_denier;
|
|
||||||
Raider apprentice;
|
|
||||||
Raider anointed;
|
|
||||||
Raider champion;
|
|
||||||
uint256 profit_per_second;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Player {
|
|
||||||
uint256 total_minted;
|
|
||||||
uint256 created_at;
|
|
||||||
uint256 last_raided_at;
|
|
||||||
}
|
|
||||||
|
|
||||||
contract RaidGeld is ERC20, Ownable {
|
contract RaidGeld is ERC20, Ownable {
|
||||||
uint8 constant DECIMALS = 4;
|
uint256 public constant MANTISSA = 1e4;
|
||||||
|
|
||||||
uint256 public constant BUY_IN_AMOUNT = 0.00005 ether;
|
uint256 public constant BUY_IN_AMOUNT = 0.00005 ether;
|
||||||
uint256 public constant INITIAL_GELD = 50 * 10 ** DECIMALS;
|
uint256 public constant INITIAL_GELD = 50 * MANTISSA;
|
||||||
uint256 public constant RAID_WAIT = 15 seconds;
|
uint256 public constant RAID_WAIT = 15 seconds;
|
||||||
|
|
||||||
mapping(address => Player) private players;
|
mapping(address => Player) private players;
|
||||||
@ -79,8 +63,8 @@ contract RaidGeld is ERC20, Ownable {
|
|||||||
// Helper so we can use it when buying units too
|
// Helper so we can use it when buying units too
|
||||||
function performRaid(address player) private {
|
function performRaid(address player) private {
|
||||||
uint256 time_past = block.timestamp - players[player].last_raided_at;
|
uint256 time_past = block.timestamp - players[player].last_raided_at;
|
||||||
|
|
||||||
uint256 new_geld = armies[player].profit_per_second * time_past * 10 ** decimals();
|
uint256 new_geld = armies[player].profit_per_second * time_past;
|
||||||
|
|
||||||
// TODO: Pink noise, make it so sometimes its better than expected
|
// TODO: Pink noise, make it so sometimes its better than expected
|
||||||
|
|
||||||
@ -124,15 +108,15 @@ contract RaidGeld is ERC20, Ownable {
|
|||||||
currentLevel = army.champion.level;
|
currentLevel = army.champion.level;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 cost = RaidGeldUtils.calculateUnitPrice(unit, currentLevel, n_units) * 10 ** decimals();
|
uint256 cost = RaidGeldUtils.calculateUnitPrice(unit, currentLevel, n_units);
|
||||||
|
// First trigger a raid so player receives what he is due at to this moment
|
||||||
|
performRaid(msg.sender);
|
||||||
require(balanceOf(msg.sender) > cost, "Not enough GELD to add this much");
|
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
|
// 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
|
// 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
|
// then burn the cost of the new army
|
||||||
performRaid(msg.sender);
|
|
||||||
// then burn the cost of the new army
|
|
||||||
_burn(msg.sender, cost);
|
_burn(msg.sender, cost);
|
||||||
|
|
||||||
// Increase level
|
// Increase level
|
||||||
|
|||||||
20
src/RaidGeldStructs.sol
Normal file
20
src/RaidGeldStructs.sol
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.13;
|
||||||
|
|
||||||
|
struct Raider {
|
||||||
|
uint16 level;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Army {
|
||||||
|
Raider moloch_denier;
|
||||||
|
Raider apprentice;
|
||||||
|
Raider anointed;
|
||||||
|
Raider champion;
|
||||||
|
uint256 profit_per_second;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Player {
|
||||||
|
uint256 total_minted;
|
||||||
|
uint256 created_at;
|
||||||
|
uint256 last_raided_at;
|
||||||
|
}
|
||||||
@ -1,18 +1,25 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.13;
|
pragma solidity ^0.8.13;
|
||||||
|
|
||||||
import {Army} from "../src/RaidGeld.sol";
|
import {Army} from "../src/RaidGeldStructs.sol";
|
||||||
|
|
||||||
library RaidGeldUtils {
|
library RaidGeldUtils {
|
||||||
|
uint256 public constant PRECISION = 10000;
|
||||||
|
uint256 constant BASE_PRICE = 380000;
|
||||||
|
uint256 constant PRICE_FACTOR = 11500;
|
||||||
|
uint256 constant APPRENTICE_PROFIT = 61000;
|
||||||
|
uint256 constant ANOINTED_PROFIT = 6 * 64000;
|
||||||
|
uint256 constant CHAMPION_PROFIT = 67000 * 61000 * 64000 / PRECISION / PRECISION;
|
||||||
|
|
||||||
function calculateUnitPrice(uint8 unit, uint16 currentLevel, uint16 units) internal pure returns (uint256) {
|
function calculateUnitPrice(uint8 unit, uint16 currentLevel, uint16 units) internal pure returns (uint256) {
|
||||||
require(unit <= 3, "No matching unit found");
|
require(unit <= 3, "No matching unit found");
|
||||||
uint256 rollingPriceCalculation = uint256(unit + 1) * 38;
|
|
||||||
|
uint256 rollingPriceCalculation = uint256(unit + 1) * BASE_PRICE;
|
||||||
uint256 price = rollingPriceCalculation;
|
uint256 price = rollingPriceCalculation;
|
||||||
|
|
||||||
// Each level costs 15% more than previous
|
// Each level costs 15% more than previous
|
||||||
uint256 PERCENT_INCREASE = 115;
|
|
||||||
for (uint256 i = 1; i < currentLevel + units; i++) {
|
for (uint256 i = 1; i < currentLevel + units; i++) {
|
||||||
rollingPriceCalculation = rollingPriceCalculation * PERCENT_INCREASE / 100;
|
rollingPriceCalculation = rollingPriceCalculation * PRICE_FACTOR / PRECISION;
|
||||||
if (i >= currentLevel) {
|
if (i >= currentLevel) {
|
||||||
price += rollingPriceCalculation;
|
price += rollingPriceCalculation;
|
||||||
}
|
}
|
||||||
@ -22,10 +29,15 @@ library RaidGeldUtils {
|
|||||||
|
|
||||||
function calculateProfitsPerSecond(Army memory army) internal pure returns (uint256) {
|
function calculateProfitsPerSecond(Army memory army) internal pure returns (uint256) {
|
||||||
// Each next unit scales progressivelly better
|
// Each next unit scales progressivelly better
|
||||||
uint256 moloch_denier_profit = army.moloch_denier.level;
|
|
||||||
uint256 apprentice_profit = army.apprentice.level * 61 / 10;
|
uint256 moloch_denier_profit = army.moloch_denier.level * PRECISION;
|
||||||
uint256 anointed_profit = army.anointed.level * 6 * 64 / 10;
|
|
||||||
uint256 champion_profit = army.champion.level * 61 / 10 * 64 / 10 * 67 / 10;
|
uint256 apprentice_profit = army.apprentice.level * APPRENTICE_PROFIT;
|
||||||
|
|
||||||
|
uint256 anointed_profit = army.anointed.level * ANOINTED_PROFIT;
|
||||||
|
|
||||||
|
uint256 champion_profit = army.champion.level * CHAMPION_PROFIT;
|
||||||
|
|
||||||
return moloch_denier_profit + apprentice_profit + anointed_profit + champion_profit;
|
return moloch_denier_profit + apprentice_profit + anointed_profit + champion_profit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ pragma solidity ^0.8.13;
|
|||||||
|
|
||||||
import {Test, console} from "forge-std/Test.sol";
|
import {Test, console} from "forge-std/Test.sol";
|
||||||
import {RaidGeld, Army, Player} from "../src/RaidGeld.sol";
|
import {RaidGeld, Army, Player} from "../src/RaidGeld.sol";
|
||||||
import "../src/RaidGeldUtils.sol";
|
import "../src/RaidGeldUtils.sol";
|
||||||
|
|
||||||
contract raid_geldTest is Test {
|
contract raid_geldTest is Test {
|
||||||
RaidGeld public raid_geld;
|
RaidGeld public raid_geld;
|
||||||
@ -118,14 +118,13 @@ contract raid_geldTest is Test {
|
|||||||
uint256 unit_level = army.moloch_denier.level;
|
uint256 unit_level = army.moloch_denier.level;
|
||||||
uint256 balance = raid_geld.balanceOf(player1);
|
uint256 balance = raid_geld.balanceOf(player1);
|
||||||
uint256 income_per_sec = army.profit_per_second;
|
uint256 income_per_sec = army.profit_per_second;
|
||||||
|
|
||||||
// Add 1 unit
|
// Add 1 unit
|
||||||
raid_geld.addUnit(0, 1);
|
raid_geld.addUnit(0, 1);
|
||||||
uint256 unitPrice = RaidGeldUtils.calculateUnitPrice(0, 0, 1) * 10 ** 4;
|
uint256 unitPrice = RaidGeldUtils.calculateUnitPrice(0, 0, 1);
|
||||||
|
|
||||||
// Check that those tokens were burnt
|
// Check that those tokens were burnt
|
||||||
// WARN: In addUnit will mint additional tokens but they are not calculated
|
// 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
|
// into this because the test doesnt move the blockchain so no extra tokens
|
||||||
// are minted to the user
|
// are minted to the user
|
||||||
uint256 newBalance = raid_geld.balanceOf(player1);
|
uint256 newBalance = raid_geld.balanceOf(player1);
|
||||||
assertEq(newBalance, balance - unitPrice);
|
assertEq(newBalance, balance - unitPrice);
|
||||||
@ -134,7 +133,6 @@ contract raid_geldTest is Test {
|
|||||||
// Check that unit level increased
|
// Check that unit level increased
|
||||||
uint256 new_unit_level = army.moloch_denier.level;
|
uint256 new_unit_level = army.moloch_denier.level;
|
||||||
assertEq(new_unit_level, unit_level + 1);
|
assertEq(new_unit_level, unit_level + 1);
|
||||||
|
|
||||||
// Check that user income per second increased
|
// Check that user income per second increased
|
||||||
uint256 new_income_per_sec = army.profit_per_second;
|
uint256 new_income_per_sec = army.profit_per_second;
|
||||||
assertLt(income_per_sec, new_income_per_sec);
|
assertLt(income_per_sec, new_income_per_sec);
|
||||||
|
|||||||
@ -3,14 +3,14 @@ pragma solidity ^0.8.13;
|
|||||||
|
|
||||||
import {Test, console} from "forge-std/Test.sol";
|
import {Test, console} from "forge-std/Test.sol";
|
||||||
import {Army, Raider} from "../src/RaidGeld.sol";
|
import {Army, Raider} from "../src/RaidGeld.sol";
|
||||||
import "../src/RaidGeldUtils.sol";
|
import "../src/RaidGeldUtils.sol";
|
||||||
|
|
||||||
contract raid_geldTest is Test {
|
contract raid_geldTest is Test {
|
||||||
function test_0_unit_price() pure public {
|
function test_0_unit_price() public pure {
|
||||||
// buying 1 unit of moloch_denier
|
// buying 1 unit of moloch_denier
|
||||||
uint256 basePriceMolochDenier = RaidGeldUtils.calculateUnitPrice(0, 0, 1);
|
uint256 basePriceMolochDenier = RaidGeldUtils.calculateUnitPrice(0, 0, 1);
|
||||||
assertEq(basePriceMolochDenier, 38);
|
assertEq(basePriceMolochDenier, RaidGeldUtils.BASE_PRICE);
|
||||||
|
|
||||||
// buying 3 units
|
// buying 3 units
|
||||||
// has to be a bit more than 3 * 38 = 114
|
// has to be a bit more than 3 * 38 = 114
|
||||||
uint256 price = RaidGeldUtils.calculateUnitPrice(0, 0, 3);
|
uint256 price = RaidGeldUtils.calculateUnitPrice(0, 0, 3);
|
||||||
@ -28,25 +28,27 @@ contract raid_geldTest is Test {
|
|||||||
assertGt(price, basePriceChamp * 12);
|
assertGt(price, basePriceChamp * 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_1_profits_per_second() pure public {
|
function test_1_profits_per_second(uint16 _dLvl, uint16 _apLvl, uint16 _anLvl, uint16 _cLvl) public {
|
||||||
Army memory army = Army({
|
Army memory army = Army({
|
||||||
moloch_denier: Raider({ level: 1}),
|
moloch_denier: Raider({level: 1}),
|
||||||
apprentice: Raider({ level: 0}),
|
apprentice: Raider({level: 0}),
|
||||||
anointed: Raider({ level: 0}),
|
anointed: Raider({level: 0}),
|
||||||
champion: Raider({ level: 0}),
|
champion: Raider({level: 0}),
|
||||||
profit_per_second: 0 // irrelevant for this test
|
profit_per_second: 0 // irrelevant for this test
|
||||||
});
|
});
|
||||||
uint256 profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army);
|
uint256 profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army);
|
||||||
assertEq(profits_per_second, 1);
|
assertEq(profits_per_second, RaidGeldUtils.PRECISION);
|
||||||
|
|
||||||
army = Army({
|
army = Army({
|
||||||
moloch_denier: Raider({ level: 2}),
|
moloch_denier: Raider({level: _dLvl}),
|
||||||
apprentice: Raider({ level: 0}),
|
apprentice: Raider({level: _apLvl}),
|
||||||
anointed: Raider({ level: 10}),
|
anointed: Raider({level: _anLvl}),
|
||||||
champion: Raider({ level: 1}),
|
champion: Raider({level: _cLvl}),
|
||||||
profit_per_second: 0 // irrelevant for this test
|
profit_per_second: 0 // irrelevant for this test
|
||||||
});
|
});
|
||||||
profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army);
|
profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army);
|
||||||
assertEq(profits_per_second, 640);
|
uint256 expected = _dLvl * RaidGeldUtils.PRECISION + _apLvl * RaidGeldUtils.APPRENTICE_PROFIT
|
||||||
|
+ _anLvl * RaidGeldUtils.ANOINTED_PROFIT + _cLvl * RaidGeldUtils.CHAMPION_PROFIT;
|
||||||
|
assertEq(profits_per_second, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user