1
0
forked from mico/idle_moloch

Connected wagmi to contract, changed Geld -> RaidGeld and minor contract changes

This commit is contained in:
mic0 2024-10-22 13:36:49 +02:00
parent bcc79beed1
commit bbf2a1874f
Signed by: mico
GPG Key ID: A3F8023524CF1C8D
12 changed files with 219 additions and 51 deletions

7
.gas-snapshot Normal file
View File

@ -0,0 +1,7 @@
GeldTest:test_00_no_fallback() (gas: 16801)
GeldTest:test_01_no_receive() (gas: 17274)
GeldTest:test_02_registration() (gas: 160943)
GeldTest:test_03_fundsCanBeWithdrawn() (gas: 161313)
GeldTest:test_04_onlyOwnerCanWithdraw() (gas: 147226)
GeldTest:test_05_raid() (gas: 184786)
GeldTest:test_06_is_registered() (gas: 148625)

2
app/package-lock.json generated
View File

@ -14,7 +14,7 @@
"next": "^14.2.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"viem": "2.17.0",
"viem": "^2.17.0",
"wagmi": "^2.12.17"
},
"devDependencies": {

View File

@ -15,7 +15,7 @@
"next": "^14.2.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"viem": "2.17.0",
"viem": "^2.17.0",
"wagmi": "^2.12.17"
},
"devDependencies": {

View File

@ -1,12 +1,13 @@
import React from "react"
import styles from '../styles/Background.module.css';
import Tower from "./Tower";
const Background = () => {
return <div className={styles.frame}>
<div className={`${styles.air} ${styles.background_asset}`} />
<div className={`${styles.clouds_small} ${styles.background_asset}`} />
<div className={`${styles.clouds_large} ${styles.background_asset}`} />
<div className={`${styles.tower} ${styles.background_asset}`} />
<Tower />
<div className={`${styles.mountains} ${styles.background_asset}`} />
<div className={`${styles.village} ${styles.background_asset}`} />
<div className={`${styles.tavern_keeper} ${styles.person} ${styles.static_keeper}`} />

View File

@ -1,12 +1,57 @@
import React from "react"
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import styles from "../styles/Header.module.css"
import { usePlayer } from "../providers/PlayerProvider";
import { useAccount } from 'wagmi';
import dynamic from "next/dynamic";
import { formatUnits } from "viem";
const Header = () => {
return <header className={styles.header}>
<h1 className={styles.title}>mico's Slayery</h1>
<p className={styles.counter}>1213123 million GELD</p>
<p className={styles.counter_per_seconds}>per second: 55.000 thousand</p>
const { isConnected } = useAccount();
const { isRegistered, register, balance } = usePlayer();
const [count, setCount] = useState("0")
const [perSecond, setPerSecond] = useState("0")
useEffect(() => {
if (balance != null) {
setCount(formatUnits(balance, 4))
}
}, [balance])
const title = useMemo(() => {
return isRegistered ? `SLAY THE MOLOCH` :
!isConnected ? "Connect your wallet traveler ☝️ and then ..." :
"Click here to start 😈"
}, [isConnected, isRegistered])
const subtitle = useMemo(() => {
if (isRegistered) {
return <p className={styles.counter}>{count} GELD</p>
} else {
return <p className={styles.counter}>SLAY THE MOLOCH</p>
}
}, [isRegistered, count])
const perSecondParagraph = useMemo(() => {
return (isRegistered) ?
<p className={styles.counter_per_seconds}>per second: {perSecond}</p>
: null
}, [isRegistered, perSecond])
const onRegister = useCallback(() => {
if (isRegistered) return
register();
}, [isRegistered, register])
return <header onClick={onRegister} className={styles.header}>
{count.current} {balance}
<h1 className={styles.title}>{title}</h1>
{subtitle}
{perSecondParagraph}
</header>
}
export default Header
// export default Header
export default dynamic(() => Promise.resolve(Header), {
ssr: false,
});

View File

@ -0,0 +1,7 @@
import styles from '../styles/Background.module.css';
const Tower = () => {
return <div className={`${styles.tower} ${styles.background_asset}`} />
}
export default Tower

View File

@ -6,6 +6,7 @@ import { WagmiProvider } from 'wagmi';
import { RainbowKitProvider, midnightTheme } from '@rainbow-me/rainbowkit';
import { config } from '../wagmi';
import { Texturina } from 'next/font/google'
import PlayerProvider from '../providers/PlayerProvider';
const client = new QueryClient();
const font = Texturina({ weight: ['400'], subsets: ["latin"] })
@ -23,7 +24,9 @@ function MyApp({ Component, pageProps }: AppProps) {
font-family: ${font.style.fontFamily};
}
`}</style>
<Component {...pageProps} />
<PlayerProvider>
<Component {...pageProps} />
</PlayerProvider>
</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>

View File

@ -0,0 +1,91 @@
import React, { createContext, ReactNode, useCallback, useContext } from 'react'
import { useAccount, useReadContract, useWriteContract } from 'wagmi'
import contractAbi from "../../../out/RaidGeld.sol/RaidGeld.json"
import { parseEther } from 'viem'
const contractAddress = "0xbd06B0878888bf4c6895704fa603a5ADf7e65c66"
const abi = contractAbi.abi
export interface PlayerContextType {
isRegistered: boolean,
user: null | string,
balance: bigint,
register: () => void;
}
const PlayerContext = createContext<PlayerContextType>({
isRegistered: false,
user: null,
balance: BigInt(0),
register: () => { },
});
const PlayerProvider = ({ children }: { children: ReactNode }) => {
const { address, isConnected } = useAccount();
const { writeContract } = useWriteContract();
const { data: isRegistered } = useReadContract({
address: contractAddress,
abi,
functionName: 'isRegistered',
args: [address],
query: {
enabled: isConnected,
refetchInterval: 5,
}
});
console.log("IS REGISTERED", isRegistered, address)
const { data: balance, error, refetch: refetchBalance } = useReadContract({
address: contractAddress,
abi,
functionName: 'balanceOf',
args: [address],
query: {
refetchInterval: 5,
enabled: isConnected
}
});
console.log(balance, error)
const { data: playerData, refetch: refetchPlayerData } = useReadContract({
address: contractAddress,
abi,
functionName: 'getPlayer',
args: [address],
query: {
enabled: isConnected,
refetchInterval: 15
}
});
const register = useCallback(() => {
console.log("START")
const a = writeContract({
abi,
address: contractAddress,
functionName: 'register',
value: parseEther("0.0005"),
})
console.log(a)
}, [writeContract])
return (
<PlayerContext.Provider value={{
isRegistered: isRegistered as boolean,
user: null,
balance: balance as bigint,
register
}}>
{children}
</PlayerContext.Provider>
);
}
export const usePlayer = () => {
return useContext(PlayerContext);
}
export default PlayerProvider

View File

@ -50,7 +50,7 @@
height: 240px;
top: 200px;
animation: thunder_hue_hard 12s linear infinite;
transition: all ease-in-out 0.2s;
transition: all ease-in-out 0.05s;
&:hover {
cursor: pointer;
transform: scale(1.05, 1.1);

View File

@ -2,16 +2,16 @@
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {Geld} from "../src/Geld.sol";
import {RaidGeld} from "../src/RaidGeld.sol";
contract GeldScript is Script {
Geld public geld;
contract RaidGeldScript is Script {
RaidGeld public raidgeld;
function setUp() public {}
function run() public {
vm.startBroadcast();
geld = new Geld();
raidgeld = new RaidGeld();
vm.stopBroadcast();
}
}

View File

@ -21,7 +21,7 @@ struct Player {
uint256 last_raided_at;
}
contract Geld is ERC20, Ownable {
contract RaidGeld is ERC20, Ownable {
uint8 constant DECIMALS = 4;
uint256 public constant BUY_IN_AMOUNT = 0.0005 ether;
uint256 public constant INITIAL_GELD = 50 * 10 ** DECIMALS;
@ -37,7 +37,7 @@ contract Geld is ERC20, Ownable {
_;
}
constructor() ERC20("Geld", "GELD") Ownable(msg.sender) {}
constructor() ERC20("Raid Geld", "GELD") Ownable(msg.sender) {}
// This effectively registers the user
function register() external payable {
@ -83,13 +83,18 @@ contract Geld is ERC20, Ownable {
}
// Function to get Player struct
function getPlayer() public view onlyPlayer returns (Player memory) {
return players[msg.sender];
function getPlayer(address addr) public view onlyPlayer returns (Player memory) {
return players[addr];
}
// Function to get Army struct
function getArmy() public view onlyPlayer returns (Army memory) {
return armies[msg.sender];
function getArmy(address addr) public view onlyPlayer 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;
}
receive() external payable {

View File

@ -2,10 +2,10 @@
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Geld, Army, Player} from "../src/Geld.sol";
import {RaidGeld, Army, Player} from "../src/RaidGeld.sol";
contract GeldTest is Test {
Geld public geld;
contract raid_geldTest is Test {
RaidGeld public raid_geld;
address public player1;
address public player2;
address public owner;
@ -18,47 +18,47 @@ contract GeldTest is Test {
vm.deal(player1, 10 ether);
vm.deal(player2, 10 ether);
vm.prank(owner);
geld = new Geld();
raid_geld = new RaidGeld();
}
function registerPlayer() private {
geld.register{value: geld.BUY_IN_AMOUNT()}();
raid_geld.register{value: raid_geld.BUY_IN_AMOUNT()}();
}
function test_00_no_fallback() public {
vm.expectRevert();
// Send Ether with some data to trigger fallback
address(geld).call{value: 0.1 ether}("0x1234");
address(raid_geld).call{value: 0.1 ether}("0x1234");
}
function test_01_no_receive() public {
vm.startPrank(player1);
vm.expectRevert();
payable(address(geld)).transfer(0.1 ether);
payable(address(raid_geld)).transfer(0.1 ether);
}
function test_02_registration() public {
vm.startPrank(player1);
uint256 initialBalance = address(geld).balance;
uint256 initialTotalMinted = geld.total_minted();
uint256 initialBalance = address(raid_geld).balance;
uint256 initialTotalMinted = raid_geld.total_minted();
// Send registration fee ETH to the contract
registerPlayer();
// Check that initial GELD is received by the player
assertEq(geld.balanceOf(player1), geld.INITIAL_GELD());
// Check that initialraid_geld.is received by the player
assertEq(raid_geld.balanceOf(player1), raid_geld.INITIAL_GELD());
// Verify the contract balance is updated
assertEq(address(geld).balance, initialBalance + geld.BUY_IN_AMOUNT());
assertEq(address(raid_geld).balance, initialBalance + raid_geld.BUY_IN_AMOUNT());
// Verify player is set initially
Player memory player = geld.getPlayer();
assertEq(player.total_minted, geld.INITIAL_GELD());
Player memory player = raid_geld.getPlayer(player1);
assertEq(player.total_minted, raid_geld.INITIAL_GELD());
assertEq(player.created_at, block.timestamp);
assertEq(player.last_raided_at, 0);
Army memory army = geld.getArmy();
Army memory army = raid_geld.getArmy(player1);
assertEq(army.moloch_denier.level, 0);
assertEq(army.apprentice.level, 0);
@ -66,7 +66,7 @@ contract GeldTest is Test {
assertEq(army.champion.level, 0);
// Verify that total_minted is updated
assertEq(geld.total_minted(), initialTotalMinted + geld.INITIAL_GELD());
assertEq(raid_geld.total_minted(), initialTotalMinted + raid_geld.INITIAL_GELD());
}
function test_03_fundsCanBeWithdrawn() public {
@ -78,14 +78,14 @@ contract GeldTest is Test {
// Switch back to owner and withdraw funds
vm.startPrank(owner);
geld.withdraw();
raid_geld.withdraw();
uint256 newBalance = owner.balance;
uint256 newContractBalance = address(geld).balance;
uint256 newContractBalance = address(raid_geld).balance;
// contract balance should be empty
assertEq(newContractBalance, 0);
// owner should have the extra funds
assertEq(newBalance, initialBalance + geld.BUY_IN_AMOUNT());
assertEq(newBalance, initialBalance + raid_geld.BUY_IN_AMOUNT());
}
function test_04_onlyOwnerCanWithdraw() public {
@ -95,7 +95,7 @@ contract GeldTest is Test {
// attempt to withdraw with player 1, it should fail
vm.expectRevert();
geld.withdraw();
raid_geld.withdraw();
}
function test_05_raid() public {
@ -106,30 +106,39 @@ contract GeldTest is Test {
vm.startPrank(player1);
registerPlayer();
uint256 balance = geld.balanceOf(player1);
uint256 total_minted = geld.total_minted();
uint256 balance = raid_geld.balanceOf(player1);
uint256 total_minted = raid_geld.total_minted();
// Trigger raid funds minting
geld.raid();
raid_geld.raid();
// New balance should be larger
uint256 newBalance = geld.balanceOf(player1);
uint256 newTotalMinted = geld.total_minted();
uint256 newBalance = raid_geld.balanceOf(player1);
uint256 newTotalMinted = raid_geld.total_minted();
assertLt(balance, newBalance);
assertLt(total_minted, newTotalMinted);
// Expect fail if we raid again, we need to wait a bit
vm.expectRevert();
geld.raid();
raid_geld.raid();
// After wait time passes raid should work again
vm.warp(block.timestamp + geld.RAID_WAIT());
geld.raid();
vm.warp(block.timestamp + raid_geld.RAID_WAIT());
raid_geld.raid();
// Balance should reflect that
uint256 newestBalance = geld.balanceOf(player1);
uint256 newestTotalMinted = geld.total_minted();
uint256 newestBalance = raid_geld.balanceOf(player1);
uint256 newestTotalMinted = raid_geld.total_minted();
assertLt(newTotalMinted, newestTotalMinted);
assertLt(newBalance, newestBalance);
}
function test_06_is_registered() public {
bool is_registered = raid_geld.isRegistered(player1);
assertEq(is_registered, false);
vm.startPrank(player1);
registerPlayer();
is_registered = raid_geld.isRegistered(player1);
assertEq(is_registered, true);
}
}