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", "next": "^14.2.10",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"viem": "2.17.0", "viem": "^2.17.0",
"wagmi": "^2.12.17" "wagmi": "^2.12.17"
}, },
"devDependencies": { "devDependencies": {

View File

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

View File

@ -1,12 +1,13 @@
import React from "react" import React from "react"
import styles from '../styles/Background.module.css'; import styles from '../styles/Background.module.css';
import Tower from "./Tower";
const Background = () => { const Background = () => {
return <div className={styles.frame}> return <div className={styles.frame}>
<div className={`${styles.air} ${styles.background_asset}`} /> <div className={`${styles.air} ${styles.background_asset}`} />
<div className={`${styles.clouds_small} ${styles.background_asset}`} /> <div className={`${styles.clouds_small} ${styles.background_asset}`} />
<div className={`${styles.clouds_large} ${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.mountains} ${styles.background_asset}`} />
<div className={`${styles.village} ${styles.background_asset}`} /> <div className={`${styles.village} ${styles.background_asset}`} />
<div className={`${styles.tavern_keeper} ${styles.person} ${styles.static_keeper}`} /> <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 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 = () => { const Header = () => {
return <header className={styles.header}> const { isConnected } = useAccount();
<h1 className={styles.title}>mico's Slayery</h1> const { isRegistered, register, balance } = usePlayer();
<p className={styles.counter}>1213123 million GELD</p> const [count, setCount] = useState("0")
<p className={styles.counter_per_seconds}>per second: 55.000 thousand</p> 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> </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 { RainbowKitProvider, midnightTheme } from '@rainbow-me/rainbowkit';
import { config } from '../wagmi'; import { config } from '../wagmi';
import { Texturina } from 'next/font/google' import { Texturina } from 'next/font/google'
import PlayerProvider from '../providers/PlayerProvider';
const client = new QueryClient(); const client = new QueryClient();
const font = Texturina({ weight: ['400'], subsets: ["latin"] }) const font = Texturina({ weight: ['400'], subsets: ["latin"] })
@ -23,7 +24,9 @@ function MyApp({ Component, pageProps }: AppProps) {
font-family: ${font.style.fontFamily}; font-family: ${font.style.fontFamily};
} }
`}</style> `}</style>
<Component {...pageProps} /> <PlayerProvider>
<Component {...pageProps} />
</PlayerProvider>
</RainbowKitProvider> </RainbowKitProvider>
</QueryClientProvider> </QueryClientProvider>
</WagmiProvider> </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; height: 240px;
top: 200px; top: 200px;
animation: thunder_hue_hard 12s linear infinite; animation: thunder_hue_hard 12s linear infinite;
transition: all ease-in-out 0.2s; transition: all ease-in-out 0.05s;
&:hover { &:hover {
cursor: pointer; cursor: pointer;
transform: scale(1.05, 1.1); transform: scale(1.05, 1.1);

View File

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

View File

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

View File

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