Skip to content

Commit

Permalink
🥇👓 ↝ Initialising Planet Profile/Cover page and added staking contrac…
Browse files Browse the repository at this point in the history
…t & integration, styling, just missing a few assets. Signal-K/Silfur#28 && Signal-K/Silfur#31
  • Loading branch information
Gizmotronn committed Feb 19, 2023
1 parent 8f906d0 commit 5ce4247
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 35 deletions.
34 changes: 0 additions & 34 deletions README.md

This file was deleted.

18 changes: 18 additions & 0 deletions components/Planets/PlanetCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { useEffect, useState } from "react";
import Card from "../Card";

import { useSupabaseClient } from "@supabase/auth-helpers-react";

export function PlanetCard ({ activeTab }) {
const supabase = useSupabaseClient();

return (
<div>
{activeTab === 'planet' && (
<div><Card noPadding={false}>
Planet Name
</Card></div>
)};
</div>
);
};
42 changes: 42 additions & 0 deletions components/Stake/ApproxRewards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useState, useEffect } from "react";

import { useAddress } from "@thirdweb-dev/react";
import { SmartContract } from "@thirdweb-dev/sdk";
import { ethers } from "ethers";
import ContractMappingResponse from "../../constants/contractMappingResponse";

type Props = { helperContract: SmartContract<any>; };

export default function ApproxRewards ({ helperContract }: Props ) { // Calls contract to estimate the rewards owed to the authenticated player/user
const address = useAddress();
const everyMillisecondAmount = parseInt(
(10_000_000_000_000 / 2.1).toFixed(0) // Assumes each block (on EVM) takes ~2.1 seconds to be mined. Begins when component isMounted
);

const [amount, setAmount] = useState<number>(0);
const [multiplier, setMultiplier] = useState<number>(0);

useEffect(() => {
(async () => {
if (!address) return;
const p = ( await helperContract.call( 'playerHelper', address, )) as ContractMappingResponse;
if (p.isData) { // If a multitool owned by the player IS staked/equipped
setMultiplier(p.value.toNumber() + 1); // A better multitool (derived as tokenId of multitool contract) gives better rewards
} else { setMultiplier(0); };
})();
}, [address, helperContract]);

useEffect(() => { // Update the amount in state based on everyMillisecondAmount
const interval = setInterval(() => { setAmount( amount + everyMillisecondAmount ); }, 100); // update token amount (earned from staking)
return () => clearInterval(interval); // Clear when the component unmounts
}, [amount, everyMillisecondAmount]);

return (
<p style={{ width: 370, overflow: 'hidden' }}>
Earned this session: {" "}
<b>
{ethers.utils.formatEther((amount * multiplier).toFixed(0)) || "Error..."}
</b>
</p>
);
}
4 changes: 4 additions & 0 deletions components/Stake/LoadingSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import React from "react";
import styles from '../../styles/Staking-P2E/planetInteraction.module.css';

export default function LoadingSection () { return <div className={styles.container}>Loading...</div>; };
32 changes: 32 additions & 0 deletions components/Stake/MintContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";
import styles from '../../styles/Staking-P2E/Home.module.css';

import { useAddress, useClaimNFT, useEditionDrop, Web3Button } from "@thirdweb-dev/react";
import { PLANETS_ADDRESS } from "../../constants/contractAddresses";

export default function MintContainer () {
const editionDrop = useEditionDrop(PLANETS_ADDRESS);
const { mutate: claim } = useClaimNFT(editionDrop);
const address = useAddress();

return (
<div className={styles.collectionContainer}>
<h1>Edition drop</h1>
<p>Claim your planet NFT to start playing</p>
<div className={`${styles.nftBox} ${styles.spacerBottom}`}>
<img src='./mine.gif' style={{ height: 200 }} />
</div>
<Web3Button contractAddress={PLANETS_ADDRESS}
action={() => {
claim({
quantity: 1,
to: address!,
tokenId: 0, // Claim the first nft/planet in the collection. This mutate function will be updated, with the specific value being generated from our Flask API
});
}}
accentColor="#f5f"
colorMode='dark'
>Claim the first planet!</Web3Button>
</div>
);
}
62 changes: 62 additions & 0 deletions components/Stake/OwnedGear.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import styles from '../../styles/Staking-P2E/planetInteraction.module.css';

import { ThirdwebNftMedia, useAddress, useOwnedNFTs, Web3Button } from "@thirdweb-dev/react";
import { EditionDrop, SmartContract } from "@thirdweb-dev/sdk";

import LoadingSection from "./LoadingSection";
import { HELPER_ADDRESS } from "../../constants/contractAddresses";

type Props = { multitoolContract: EditionDrop, helperContract: SmartContract<any>; };

export default function OwnedGear ({ multitoolContract, helperContract }: Props) { // Shows the multitools in a user's wallet and allows them to stake/equip said multitools
const address = useAddress();
const { data: ownedMultitools, isLoading } = useOwnedNFTs( // Which nfts does the user hold from the multitools contract
multitoolContract,
address,
);

if (isLoading) {
return <LoadingSection />
}

async function equip(id: string) {
if (!address) return;
const hasApproval = await multitoolContract.isApproved( // Is the contract approved by the account to be able to transfer the multitool?
address,
HELPER_ADDRESS
);

if (!hasApproval) {
await multitoolContract.setApprovalForAll(HELPER_ADDRESS, true);
};

await helperContract.call("stake", id);

window.location.reload();
}

return (
<>
<div className={styles.nftBoxGrid}>
{ownedMultitools?.map((p) => (
<div className={styles.nftBox} key={p.metadata.id.toString()}>
<ThirdwebNftMedia
metadata={p.metadata}
className={`${styles.nftMedia} ${styles.spacerTop}`}
height={"64"}
/>
<h3>{p.metadata.name}</h3>

<Web3Button
contractAddress={HELPER_ADDRESS}
action={() => equip(p.metadata.id)}
>
Equip
</Web3Button>
</div>
))}
</div>
</>
);
}
49 changes: 49 additions & 0 deletions components/Stake/Rewards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import styles from '../../styles/Staking-P2E/planetInteraction.module.css';

import { ThirdwebNftMedia, useAddress, useMetadata, useContractRead, useTokenBalance, Web3Button } from "@thirdweb-dev/react";
import { SmartContract, Token } from "@thirdweb-dev/sdk";
import { ethers } from "ethers";
import { HELPER_ADDRESS } from "../../constants/contractAddresses"; // Create a param/argument so that the helper contract can be changed per page like the rewards/planet (aka character) contracts

import ApproxRewards from "./ApproxRewards";

type Props = { helperContract: SmartContract<any>; rewardsContract: Token; };

export default function Rewards({ helperContract, rewardsContract }: Props ) { // Shows the token metadata, amount of tokens in wlalet, claimable amount (reward)
const address = useAddress();
const { data: tokenMetadata } = useMetadata(rewardsContract);
const { data: currentBalance } = useTokenBalance(rewardsContract, address);
const { data: unclaimedAmount } = useContractRead(
helperContract,
'calculateRewards',
address
);

return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<p>Your <b>Minerals</b></p>
{tokenMetadata && ( // If exists/loaded
<ThirdwebNftMedia
// @ts-ignore
metadata={tokenMetadata}
height={'48'}
/>
)}
<p className={styles.noGapBottom}>
Balance: <b>{currentBalance?.displayValue}</b>
</p>
<p>
Unclaimed: {" "}
<b>{unclaimedAmount && ethers.utils.formatUnits(unclaimedAmount)}</b>
</p>
<ApproxRewards helperContract={helperContract} />
<div className={styles.smallMargin}>
<Web3Button
contractAddress={HELPER_ADDRESS}
action={(contract) => contract.call('claim')}
>Claim Rewards</Web3Button>
</div>
</div>
);
}
26 changes: 26 additions & 0 deletions components/Stake/Shop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { useEffect } from "react";
import styles from '../../styles/Staking-P2E/planetInteraction.module.css';

import { ThirdwebNftMedia, useNFTs } from "@thirdweb-dev/react";
import { EditionDrop } from "@thirdweb-dev/sdk";

import { ShopItem } from ".";

type Props = { multitoolContract: EditionDrop; };

export default function Shop ({ multitoolContract }: Props ) { // Shows all available multitools, their price, and a button to purchase them
const { data: availableMultitools } = useNFTs(multitoolContract);
return (
<>
<div className={styles.nftBoxGrid}>
{availableMultitools?.map((p) => (
<ShopItem
multitoolContract={multitoolContract}
item={p}
key={p.metadata.id.toString()}
/>
))}
</div>
</>
)
}
45 changes: 45 additions & 0 deletions components/Stake/ShopItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react";
import styles from '../../styles/Staking-P2E/planetInteraction.module.css';

import { ThirdwebNftMedia, useActiveClaimCondition, Web3Button } from "@thirdweb-dev/react";
import { ethers } from "ethers";
import { NFT, EditionDrop } from "@thirdweb-dev/sdk";

import { MULTITOOLS_ADDRESS } from "../../constants/contractAddresses";

type Props = { multitoolContract: EditionDrop; item: NFT; };

export default function ShopItem ({ item, multitoolContract }: Props ) {
const { data: claimCondition } = useActiveClaimCondition(
multitoolContract,
item.metadata.id,
);

return (
<div className={styles.nftBox} key={item.metadata.id.toString()}>
<ThirdwebNftMedia
metadata={item.metadata}
className={`${styles.nftMedia} ${styles.spacerTop}`}
height={"64"}
/>
<h3>{item.metadata.name}</h3>
<p>
Price:{" "}
<b>
{claimCondition && ethers.utils.formatUnits(claimCondition?.price)}{" "}
Minerals
</b>
</p>

<div className={styles.smallMargin}>
<Web3Button
colorMode="dark"
contractAddress={MULTITOOLS_ADDRESS}
action={(contract) => contract.erc1155.claim(item.metadata.id, 1)}
onSuccess={() => alert("Purchased!")}
onError={(error) => alert(error)}
>Buy</Web3Button>
</div>
</div>
);
};
8 changes: 8 additions & 0 deletions pages/planets/planet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React, { useState, useEffect } from "react";
import { useRouter } from "next/router";

import Layout, { ProfileLayout } from "../../components/Layout";
import Card from "../../components/Card";

// import { Database } from "../../utils/database.types"; // Use this for later when we are drawing from the Planets table
// type Planets = Database['public']['Tables']['planets']['Row'];
2 changes: 1 addition & 1 deletion pages/posts/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Layout, { ProfileLayout } from "../../components/Layout";
import Card from "../../components/Card";
import {useRouter} from "next/router";
import { useRouter } from "next/router";
import React, { useEffect, useState} from "react";
import { Database } from "../../utils/database.types";
import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react";
Expand Down
49 changes: 49 additions & 0 deletions pages/stake/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { NextPage } from "next";
import styles from '../../styles/Staking-P2E/planetInteraction.module.css';
import { useRouter } from "next/router";

import { ConnectWallet, useAddress, /* useEditionDrop, */ useOwnedNFTs, useContract } from "@thirdweb-dev/react";

import MintContainer from "../../components/Stake/MintContainer";
import { PLANETS_ADDRESS } from "../../constants/contractAddresses";

const StakingHome: NextPage = () => {
const { contract: editionDrop} = useContract(PLANETS_ADDRESS, 'edition-drop');
const address = useAddress();
const router = useRouter();
const { data: ownedNfts, isLoading, isError, } = useOwnedNFTs(editionDrop, address);
if (!address) { // Enable users to connect their wallet if there isn't a connected wallet
return (
<div className={styles.container}>
<ConnectWallet />
</div>
);
}

if (isLoading) {
return <div>Loading</div>;
}

if (!ownedNfts || isError) {
return <div>Error</div>;
}

if (ownedNfts.length === 0) { // If the connected wallet has 0 NFTs in the planet collection
return (
<div className={styles.container}>
<MintContainer />
</div>
)
}

return ( // Show this if the connected address has an NFT from the planet collection
<div className={styles.container}>
<button
className={`${styles.mainButton} ${styles.spacerBottom}`}
onClick={() => router.push(`/play`)}
>Planet interaction</button>
</div>
);
};

export default StakingHome;
Loading

0 comments on commit 5ce4247

Please sign in to comment.