diff --git a/app/src/components/BossInfo.tsx b/app/src/components/BossInfo.tsx index eede405..4ee06ae 100644 --- a/app/src/components/BossInfo.tsx +++ b/app/src/components/BossInfo.tsx @@ -24,7 +24,7 @@ const bossToName: Record = { 6: "Greedy", } -const bossToReward: Record = { +export const bossToReward: Record = { 0: BigInt("200000000000000000"), 1: BigInt("28274420000000000000"), 2: BigInt("174191628800000000000"), diff --git a/app/src/components/BossOutcomeModal.tsx b/app/src/components/BossOutcomeModal.tsx new file mode 100644 index 0000000..3f1fd33 --- /dev/null +++ b/app/src/components/BossOutcomeModal.tsx @@ -0,0 +1,31 @@ +import { formatUnits } from "viem"; +import { BossLevel, usePlayer } from "../providers/PlayerProvider"; +import styles from "../styles/Modal.module.css"; +import { bossToReward } from "./BossInfo"; + +interface BossOutcomeModalProps { + setIsOpen: (val: boolean) => void, + outcome: boolean, + ascended: boolean, + bossLevel: BossLevel +} + +const BossOutcomeModal = ({ setIsOpen, outcome, ascended, bossLevel }: BossOutcomeModalProps) => { + const text = outcome ? "and you won! 🤩" : "and you lost 😔"; + const rewardAmount = parseFloat(parseFloat(formatUnits(bossToReward[bossLevel], 18).toString()).toFixed(4)); + const rewardText = + ascended ?

You won {rewardAmount} RGCVII and ASCENDED!!!. This means you beat the bosses and gained a Prestige level. Your GELD is now forfeit, but your legend lives on.

+ : outcome ?

You won {rewardAmount} RGCVII

+ :

Your GELD is now forfeit. Try again 💪 we know you can do it!

+ + return
+

You battled a boss

+

{text}

+ {rewardText} +
+ +
+
+} + +export default BossOutcomeModal diff --git a/app/src/providers/PlayerProvider.tsx b/app/src/providers/PlayerProvider.tsx index 6941033..8562031 100644 --- a/app/src/providers/PlayerProvider.tsx +++ b/app/src/providers/PlayerProvider.tsx @@ -61,6 +61,7 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => { const { address, isConnected } = useAccount(); const { writeContract, error } = useWriteContract(); const [[txHash, callbackFn], setHashAndCallback] = useState<[Hash | null, () => void]>([null, () => { }]) + const [bossJustBattled, setBossJustBattled] = useState(false); useEffect(() => { console.warn(error) @@ -133,11 +134,12 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => { abi, address: contractAddress, functionName: 'register_eth', - value: parseEther("0.0005"), + value: parseEther("0.00005"), }, { onSuccess: (hash) => { setHashAndCallback([hash, resetHashAndCallback]) - } + }, + onError: () => resetHashAndCallback() }) } else if (arg === "RGCVII") { writeContract({ @@ -156,10 +158,12 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => { }, { onSuccess: (hash) => { setHashAndCallback([hash, resetHashAndCallback]) - } + }, + onError: () => resetHashAndCallback() }) ]) - } + }, + onError: () => resetHashAndCallback() }); } }, [writeContract, resetHashAndCallback]) @@ -172,7 +176,8 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => { }, { onSuccess: (hash) => { setHashAndCallback([hash, resetHashAndCallback]) - } + }, + onError: () => resetHashAndCallback() }) }, [writeContract, resetHashAndCallback]) @@ -190,8 +195,13 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => { abi, address: contractAddress, functionName: 'battle_with_boss', + }, { + onSuccess: (hash) => { + setHashAndCallback([hash, () => resetHashAndCallback()]) + }, + onError: () => resetHashAndCallback() }) - }, [writeContract]) + }, [writeContract, resetHashAndCallback]) return ( { }}> {children} {txHash && } + {bossJustBattled && } ); } diff --git a/app/src/wagmi.ts b/app/src/wagmi.ts index 332811c..9cf52cd 100644 --- a/app/src/wagmi.ts +++ b/app/src/wagmi.ts @@ -2,7 +2,6 @@ import { getDefaultConfig } from '@rainbow-me/rainbowkit'; import { base, baseSepolia, - sepolia, foundry } from 'wagmi/chains'; @@ -13,7 +12,6 @@ export const config = getDefaultConfig({ baseSepolia, foundry, base, - ...(process.env.NEXT_PUBLIC_ENABLE_TESTNETS === 'true' ? [sepolia] : []), ], ssr: true, }); diff --git a/src/RaidGeld.sol b/src/RaidGeld.sol index 6f69b2e..ed70fa9 100644 --- a/src/RaidGeld.sol +++ b/src/RaidGeld.sol @@ -5,7 +5,7 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import {RaidGeldUtils} from "../src/RaidGeldUtils.sol"; -import {Army, Player, Raider, Boss} from "../src/RaidGeldStructs.sol"; +import {Army, Player, Raider, Boss, LastBossResult} from "../src/RaidGeldStructs.sol"; import "../src/Constants.sol"; contract RaidGeld is ERC20, Ownable, Constants { @@ -16,6 +16,7 @@ contract RaidGeld is ERC20, Ownable, Constants { mapping(address => Player) private players; mapping(address => Army) private armies; mapping(address => Boss) private bosses; + mapping(address => LastBossResult) private lastBossResults; // WETH IWETH public immutable weth = IWETH(WETH); @@ -44,6 +45,7 @@ contract RaidGeld is ERC20, Ownable, Constants { event DaoTokenBuyInAmountSet(address indexed owner, uint256 oldAmount, uint256 newAmount); event PrestigeGained(address indexed player, uint32 prestigeLevel); event BossDefeated(address indexed player, uint8 bossLevel, uint256 earnings); + event BossBattle(address indexed player, uint8 bossLevel, bool hasWon); // Modifier for functions that should only be available to registered players modifier onlyPlayer() { @@ -106,6 +108,9 @@ contract RaidGeld is ERC20, Ownable, Constants { profit_per_second: 0 }); bosses[_playerAddress] = Boss({level: 0, variants: RaidGeldUtils.generate_boss_variants(block.prevrandao)}); + + // dont change lastBossResult + // that only changes after boss battles and on init } // New player want to register with ETH @@ -171,6 +176,11 @@ contract RaidGeld is ERC20, Ownable, Constants { return bosses[addr]; } + // Function to get the Boss struct + function getLastBossResult(address addr) public view returns (LastBossResult memory) { + return lastBossResults[addr]; + } + // Quick fn to check if user is registered function isRegistered(address addr) public view returns (bool) { return players[addr].created_at != 0; @@ -248,6 +258,14 @@ contract RaidGeld is ERC20, Ownable, Constants { ? RaidGeldUtils.getBossPower(boss_to_attack.level) : balanceOf(msg.sender); bool hasWonBattle = RaidGeldUtils.calculateBossFight(boss_to_attack.level, geld_to_burn, block.prevrandao); + emit BossBattle(msg.sender, boss_to_attack.level, hasWonBattle); + lastBossResults[msg.sender] = LastBossResult({ + battled_at: block.timestamp, + level: boss_to_attack.level, + variant: boss_to_attack.variants[boss_to_attack.level], + prestigeGained: false, + reward: 0 + }); if (hasWonBattle) { // Burn geld, send some sweet DAO Token and continue _burn(msg.sender, geld_to_burn); @@ -256,11 +274,14 @@ contract RaidGeld is ERC20, Ownable, Constants { daoToken.transfer(msg.sender, reward); emit BossDefeated(msg.sender, boss_to_attack.level, reward); + lastBossResults[msg.sender].reward = reward; + if (boss_to_attack.level == 6) { // User ascends! Moloch is defeated, user can start a new run players[msg.sender].prestige_level += 1; emit PrestigeGained(msg.sender, players[msg.sender].prestige_level); player_dies(msg.sender); + lastBossResults[msg.sender].prestigeGained = true; return [hasWonBattle, true /* New prestige level! */ ]; } else { // else go to next boss @@ -268,6 +289,7 @@ contract RaidGeld is ERC20, Ownable, Constants { } } else { // Whoops u died, boss defeated you + lastBossResults[msg.sender].reward = 0; player_dies(msg.sender); } return [hasWonBattle, false /* hasnt gotten prestige level */ ]; diff --git a/src/RaidGeldStructs.sol b/src/RaidGeldStructs.sol index f30deed..e045916 100644 --- a/src/RaidGeldStructs.sol +++ b/src/RaidGeldStructs.sol @@ -28,3 +28,11 @@ struct Boss { uint8 level; uint8[7] variants; } + +struct LastBossResult { + uint256 battled_at; + uint256 reward; + uint8 level; + uint8 variant; + bool prestigeGained; +} diff --git a/test/RaidGeld.t.sol b/test/RaidGeld.t.sol index 1d32a09..54dda56 100644 --- a/test/RaidGeld.t.sol +++ b/test/RaidGeld.t.sol @@ -3,7 +3,7 @@ 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 {RaidGeld, Army, Player, Boss, LastBossResult} from "../src/RaidGeld.sol"; import "../src/RaidGeldUtils.sol"; import {Constants} from "../src/Constants.sol"; @@ -283,6 +283,13 @@ contract raid_geldTest is Test, Constants { uint256 afterBossDaoBalance = raid_geld.daoToken().balanceOf(player1); uint256 afterBossContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld)); + LastBossResult memory bossResult = raid_geld.getLastBossResult(player1); + + assertEq(bossResult.battled_at, block.timestamp); + assertEq(bossResult.reward, afterBossDaoBalance - initialDaoBalance); + assertEq(bossResult.level, boss.level); + assertEq(bossResult.variant, boss.variants[boss.level]); + assertEq(bossResult.prestigeGained, false); // User should receive funs, contract should lose them assertLt(initialDaoBalance, afterBossDaoBalance); @@ -306,6 +313,7 @@ contract raid_geldTest is Test, Constants { registerPlayerWithDaoToken(); raid_geld.addUnit(0, 1); + Boss memory boss = raid_geld.getBoss(player1); bool[2] memory results = raid_geld.battle_with_boss(); // Should lose with just starting GELD assertEq(results[0], false); @@ -321,6 +329,13 @@ contract raid_geldTest is Test, Constants { assertEq(raid_geld.balanceOf(player1), 0); // Units should reset assertEq(army.moloch_denier.level, 0); + + LastBossResult memory bossResult = raid_geld.getLastBossResult(player1); + assertEq(bossResult.battled_at, block.timestamp); + assertEq(bossResult.reward, 0); + assertEq(bossResult.level, boss.level); + assertEq(bossResult.variant, boss.variants[boss.level]); + assertEq(bossResult.prestigeGained, false); } function test_08_player_who_lost_can_restart() public { @@ -388,6 +403,8 @@ contract raid_geldTest is Test, Constants { if (results[0] == true && results[1] == true) { success = true; Player memory player = raid_geld.getPlayer(player1); + LastBossResult memory bossResult = raid_geld.getLastBossResult(player1); + assertEq(bossResult.prestigeGained, true); vm.assertEq(player.prestige_level, 1); vm.assertEq(player.n_runs, tries); break;