forked from mico/idle_moloch
156 lines
5.3 KiB
Solidity
156 lines
5.3 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.13;
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import {RaidGeldUtils} from "../src/RaidGeldUtils.sol";
|
|
import {Army, Player, Raider} from "../src/RaidGeldStructs.sol";
|
|
|
|
contract RaidGeld is ERC20, Ownable {
|
|
uint256 public constant MANTISSA = 1e4;
|
|
|
|
uint256 public constant BUY_IN_AMOUNT = 0.00005 ether;
|
|
uint256 public constant INITIAL_GELD = 50 * MANTISSA;
|
|
uint256 public constant RAID_WAIT = 15 seconds;
|
|
|
|
mapping(address => Player) private players;
|
|
mapping(address => Army) private armies;
|
|
|
|
ERC20 public daoToken;
|
|
address public pool;
|
|
|
|
// Modifier for functions that should only be available to registered players
|
|
modifier onlyPlayer() {
|
|
require(players[msg.sender].created_at != 0, "Not an initiated player");
|
|
_;
|
|
}
|
|
|
|
constructor(address _daoToken, address _pool) ERC20("Raid Geld", "GELD") Ownable(msg.sender) {
|
|
daoToken = ERC20(_daoToken);
|
|
pool = _pool;
|
|
}
|
|
|
|
// This effectively registers the user
|
|
function register() external payable {
|
|
require(players[msg.sender].created_at == 0, "Whoops, player already exists :)");
|
|
require(msg.value == BUY_IN_AMOUNT, "Incorrect buy in amount");
|
|
|
|
// Mint some starting tokens to the player
|
|
_mint(msg.sender, INITIAL_GELD);
|
|
|
|
// Set initial states
|
|
players[msg.sender] =
|
|
Player({total_minted: INITIAL_GELD, created_at: block.timestamp, last_raided_at: block.timestamp});
|
|
armies[msg.sender] = Army({
|
|
moloch_denier: Raider({level: 0}),
|
|
apprentice: Raider({level: 0}),
|
|
anointed: Raider({level: 0}),
|
|
champion: Raider({level: 0}),
|
|
profit_per_second: 0
|
|
});
|
|
}
|
|
|
|
// Override for default number of decimals
|
|
function decimals() public view virtual override returns (uint8) {
|
|
return 4;
|
|
}
|
|
|
|
// Allows the owner to withdraw
|
|
function withdraw() external onlyOwner {
|
|
payable(owner()).transfer(address(this).balance);
|
|
}
|
|
|
|
// Manual minting for itchy fingers
|
|
function raid() external onlyPlayer {
|
|
require(block.timestamp >= players[msg.sender].last_raided_at + RAID_WAIT, "Tried minting too soon");
|
|
performRaid(msg.sender);
|
|
}
|
|
|
|
// Helper so we can use it when buying units too
|
|
function performRaid(address player) private {
|
|
uint256 time_past = block.timestamp - players[player].last_raided_at;
|
|
|
|
uint256 new_geld = armies[player].profit_per_second * time_past;
|
|
|
|
// TODO: Pink noise, make it so sometimes its better than expected
|
|
|
|
_mint(player, new_geld);
|
|
players[player].last_raided_at = block.timestamp;
|
|
players[player].total_minted = new_geld;
|
|
}
|
|
|
|
// Function to get Player struct
|
|
function getPlayer(address addr) public view returns (Player memory) {
|
|
return players[addr];
|
|
}
|
|
|
|
// Function to get Army struct
|
|
function getArmy(address addr) public view returns (Army memory) {
|
|
return armies[addr];
|
|
}
|
|
|
|
// Quick fn to check if user is registered
|
|
function isRegistered(address addr) public view returns (bool) {
|
|
return players[addr].created_at != 0;
|
|
}
|
|
|
|
// Add a unit to your army
|
|
function addUnit(uint8 unit, uint16 n_units) external onlyPlayer {
|
|
require(unit <= 3, "Unknown unit");
|
|
|
|
Army storage army = armies[msg.sender];
|
|
uint16 currentLevel = 0;
|
|
if (unit == 0) {
|
|
// moloch_denier
|
|
currentLevel = army.moloch_denier.level;
|
|
} else if (unit == 1) {
|
|
// apprentice
|
|
currentLevel = army.apprentice.level;
|
|
} else if (unit == 2) {
|
|
// anointed
|
|
currentLevel = army.anointed.level;
|
|
} else if (unit == 3) {
|
|
// champion
|
|
currentLevel = army.champion.level;
|
|
}
|
|
|
|
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");
|
|
|
|
// 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
|
|
|
|
// then burn the cost of the new army
|
|
_burn(msg.sender, cost);
|
|
|
|
// Increase level
|
|
if (unit == 0) {
|
|
// moloch_denier
|
|
army.moloch_denier.level += n_units;
|
|
} else if (unit == 1) {
|
|
// apprentice
|
|
army.apprentice.level += n_units;
|
|
} else if (unit == 2) {
|
|
// anointed
|
|
army.anointed.level += n_units;
|
|
} else if (unit == 3) {
|
|
// champion
|
|
army.champion.level += n_units;
|
|
}
|
|
|
|
// update profite per second
|
|
army.profit_per_second = RaidGeldUtils.calculateProfitsPerSecond(army);
|
|
}
|
|
|
|
receive() external payable {
|
|
revert("No plain Ether accepted, use register() function to check in :)");
|
|
}
|
|
|
|
// Revert any non-function-call Ether transfers or calls to non-existent functions
|
|
fallback() external payable {
|
|
revert("No fallback calls accepted");
|
|
}
|
|
}
|