From e616c54fdb92a18cea2932cd444e8ec9adc41703 Mon Sep 17 00:00:00 2001 From: rasguy92 Date: Wed, 29 Nov 2023 17:58:28 +0700 Subject: [PATCH] feat: successfully migrate coinflip to new abis --- index.html | 1 + src/components/CoinFlip/animations.ts | 23 +- .../CreateGameCard/CreateCoinFlipGameCard.tsx | 24 ++- .../CreateGameCard/CreateDiceGameCard.tsx | 3 +- src/components/CreateGameCard/apiSlice.ts | 8 +- src/components/Dice/animations.ts | 1 + .../JoinGameCard/JoinCoinFlipGameCard.tsx | 44 ++-- .../JoinGameCard/JoinDiceGamecard.tsx | 30 ++- src/components/JoinGameCard/index.tsx | 17 -- src/components/PlayGamePlayerCard/index.tsx | 204 ++++++++++++++---- .../PlayCoinFlipGameSection.tsx | 44 ++-- .../PlayGameSection/PlayDiceGameSection.tsx | 6 +- .../PlayGameSection/blockchainApiSlice.ts | 8 +- src/components/PlayGameSection/index.tsx | 36 ++-- src/containers/JoinGamePage/index.tsx | 12 +- src/hooks/useFirebaseData.ts | 2 +- src/models/wega.ts | 1 + src/utils/abis.ts | 50 ++++- 18 files changed, 345 insertions(+), 169 deletions(-) diff --git a/index.html b/index.html index 229b908..e9b1cbd 100644 --- a/index.html +++ b/index.html @@ -19,6 +19,7 @@ // } else { // document.documentElement.classList.remove('dark'); // } + const htmlElement = document.documentElement; if(htmlElement && !htmlElement.classList.contains('dark')) htmlElement.classList.add('dark'); diff --git a/src/components/CoinFlip/animations.ts b/src/components/CoinFlip/animations.ts index eaaafb0..a861326 100644 --- a/src/components/CoinFlip/animations.ts +++ b/src/components/CoinFlip/animations.ts @@ -1,20 +1,24 @@ import { useLayoutEffect, useState } from 'react'; import { gsap } from 'gsap'; +// eslint-disable-next-line no-unused-vars +type Callback = (...args: any[]) => void | null; + export function useRoll(coinRef: any) { const [trigger, setStrigger] = useState(false); const [rolled, setRolled] = useState(false); const [animationTarget, setAnimationTarget] = useState(undefined); function roll(to: string) { - const callb = () => { - gsap.fromTo("g#all-sides", { - ease: "power4.inOut", - duration: 0.5, - y: "0", - }, {y: to}) + const callb: Callback = () => { + gsap.set("g#all-sides", { y: "0" }) + gsap.fromTo("g#all-sides", { + ease: "power4.inOut", + duration: 0.5, + y: "0", + }, {y: to}) + setTimeout(() => setRolled(true), 2000); } - gsap.set("g#all-sides", {y: "0"}) const tl = gsap.timeline({ repeatDelay: 0, onComplete: callb }) tl.to("g#all-sides", { repeat: 10, @@ -22,12 +26,9 @@ export function useRoll(coinRef: any) { y: "300", }) } - const triggerRoll = (rollDestination: number, isGameOver: boolean) => { + const triggerRoll = (rollDestination: number) => { setAnimationTarget(String(rollDestination * 100)); setStrigger(s => !s); - if(isGameOver){ - setTimeout(() => setRolled(true), 4000); - } }; useLayoutEffect(()=> { diff --git a/src/components/CreateGameCard/CreateCoinFlipGameCard.tsx b/src/components/CreateGameCard/CreateCoinFlipGameCard.tsx index c97e9ef..26cdbda 100644 --- a/src/components/CreateGameCard/CreateCoinFlipGameCard.tsx +++ b/src/components/CreateGameCard/CreateCoinFlipGameCard.tsx @@ -33,14 +33,14 @@ import { ErrorMessage } from '@hookform/error-message'; import { ArrowDownIcon, StarLoaderIcon } from '../../assets/icons'; import tw from 'twin.macro'; import { useForm } from 'react-hook-form'; -import { useCreateGameParams, useNavigateTo, useWegaStore } from '../../hooks'; +import { useCreateGameParams, useNavigateTo, useWegaStore, useTokenUSDValue } from '../../hooks'; import { useCreateWagerAndDepositMutation, useAllowanceQuery, useApproveERC20Mutation, } from './blockchainApiSlice'; -import { useCreateGameMutation } from '../../containers/App/api'; -import { toastSettings, toBigIntInWei, escrowConfig, parseTopicDataFromEventLog } from '../../utils'; +import { useCreateGameMutation, useGetRandomNumberQuery } from './apiSlice'; +import { toastSettings, toBigIntInWei, escrowConfig, parseTopicDataFromEventLog, convertBytesToNumber } from '../../utils'; import Button from '../../common/Button'; import { ToggleWagerBadge } from '../../common/ToggleWagerBadge'; import { useFormReveal } from './animations'; @@ -67,6 +67,8 @@ export const CreateCoinFlipGameCard = ({ const { wallet, network, user} = useWegaStore(); const formRef = useRef(null); const detailsBlock = useRef(null) + const randomnessQuery = useGetRandomNumberQuery(undefined); + const [currentWagerType] = useState(wagerType); const [currentCurrencyType, setCurrentCurrencyType] = useState(currencyType); const [currentCoinSide, setCurrentCoinSide] = useState(CoinSideTypes[CoinSideTypesEnum.HEADS]); @@ -85,6 +87,7 @@ export const CreateCoinFlipGameCard = ({ }); // approval for allowance + const wagerUSDValue = useTokenUSDValue(currentCurrencyType, watch('wager')); const isWagerApproved = (allowance: number, wagerAmount: number) => allowance >= wagerAmount; const allowanceQuery = useAllowanceQuery({ spender: escrowConfig.address[network?.id as keyof typeof escrowConfig.address], @@ -113,20 +116,20 @@ export const CreateCoinFlipGameCard = ({ await approveERC20({ spender: escrowConfig.address[network?.id as keyof typeof escrowConfig.address], wagerAsBigint: toBigIntInWei(wager), tokenAddress }).unwrap(); } - const receipt = await createWagerAndDeposit({ tokenAddress, wagerAsBigint: toBigIntInWei(wager), gameType }).unwrap(); - const parsedTopicData = parseTopicDataFromEventLog(receipt.logs[3], ['event GameCreation(bytes32 indexed escrowHash, uint256 indexed nonce, address creator, string name)']); - + const receipt = await createWagerAndDeposit({ tokenAddress, wagerAsBigint: toBigIntInWei(wager), gameType, randomness: [convertBytesToNumber(randomnessQuery.data?.randomness)] }).unwrap(); + const parsedTopicData = parseTopicDataFromEventLog(receipt.logs[2], ['event GameCreation(bytes32 indexed escrowHash, uint256 indexed nonce, address creator, string name)']); await createGame({ gameType, players: [ { uuid: playerUuid } ], creatorUuid: playerUuid, + networkId: network?.id as number, wager: { wagerType: currentWagerType.toUpperCase() as AllPossibleWagerTypes, wagerHash: parsedTopicData?.escrowHash, tokenAddress, wagerAmount: parseEther(String(wager)).toString(), wagerCurrency: currentCurrencyType, - nonce: parsedTopicData?.nonce.toNumber(), + nonce: Number(parsedTopicData?.nonce), }, gameAttributes: [{ key: "players[0].flipChoice", value: currentCoinSide.toString()}] }).unwrap(); @@ -145,8 +148,10 @@ export const CreateCoinFlipGameCard = ({ const navigateToGameUi = useNavigateTo() useEffect(() => { + console.log(gameType); if(wallet){ allowanceQuery.refetch(); + randomnessQuery.refetch(); if(createGameStatus === 'fulfilled' && createGameResponse) { navigateToGameUi(`/${gameType.toLowerCase()}/play/${createGameResponse.uuid}`, 1500, { replace: true, state: { gameId: createGameResponse.id, gameUuid: createGameResponse.uuid } }); @@ -155,12 +160,11 @@ export const CreateCoinFlipGameCard = ({ }, [ watch('wager'), tokenAddress, - isWagerApproved, createGameStatus, createGameResponse ]); - return ( + return tokenAddress && playerAddress && playerUuid && randomnessQuery.data && (
{message} } /> - 00,00 USD + {wagerUSDValue.loading ? 'loading...' : wagerUSDValue.value} USD Balance: { isWagerbalanceLoading ? "Retrieving balance..." : userWagerBalance?.formatted + ' ' + userWagerBalance?.symbol } diff --git a/src/components/CreateGameCard/CreateDiceGameCard.tsx b/src/components/CreateGameCard/CreateDiceGameCard.tsx index f724bd3..0a520b9 100644 --- a/src/components/CreateGameCard/CreateDiceGameCard.tsx +++ b/src/components/CreateGameCard/CreateDiceGameCard.tsx @@ -128,6 +128,7 @@ export const CreateDiceGameCard = ({ gameType, players: [ { uuid: playerUuid } ], creatorUuid: playerUuid, + networkId: network?.id as number, wager: { wagerType: currentWagerType.toUpperCase() as AllPossibleWagerTypes, wagerHash: parsedTopicData?.escrowHash, @@ -166,7 +167,7 @@ export const CreateDiceGameCard = ({ createGameStatus, createGameResponse ]); - return ( + return tokenAddress && playerAddress && playerUuid && randomnessQuery.data && ( ({ - createGame: builder.mutation & Pick & Pick & Pick & Pick & Partial>({ - query: ({ wager, players, gameType, creatorUuid, gameAttributes }: { + createGame: builder.mutation & Pick & Pick & Pick & Pick & Pick & Partial>({ + query: ({ wager, players, gameType, creatorUuid, gameAttributes, networkId }: { wager: Wager, players: Player[], gameType: AllPossibleWegaTypes, creatorUuid: string; gameAttributes?: WegaAttributes; + networkId: number; }) => ({ url: '/games', method: 'POST', - body: { wager, players, gameType, creatorUuid, gameAttributes } + body: { wager, players, gameType, creatorUuid, gameAttributes , networkId } }), }), getRandomNumber: builder.query({ diff --git a/src/components/Dice/animations.ts b/src/components/Dice/animations.ts index 365091b..432b575 100644 --- a/src/components/Dice/animations.ts +++ b/src/components/Dice/animations.ts @@ -13,6 +13,7 @@ export function useRoll(diceRef: any, onBegin?: Callback, onEnd?: Callback) { function roll(to: string) { + console.log('ROLLING TO', to); if(onBegin) onBegin(); const callb = () => { gsap.fromTo("g#all-sides", { diff --git a/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx b/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx index a1c70be..d07ac6d 100644 --- a/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx +++ b/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx @@ -27,21 +27,26 @@ import { renderGameTypeBadge, BADGE_TEXTS } from "../../common/JoinableGameBar"; +import { + useConnectModal, +} from '@rainbow-me/rainbowkit'; import { joiResolver } from '@hookform/resolvers/joi'; import { ErrorMessage } from '@hookform/error-message'; import { ArrowDownIcon, StarLoaderIcon } from '../../assets/icons'; import tw from 'twin.macro'; import { useForm } from 'react-hook-form'; import { useBalance } from 'wagmi'; -import { useNavigateTo } from '../../hooks'; +import { useNavigateTo, useCreateGameParams, useWegaStore, useTokenUSDValue } from '../../hooks'; import { useDepositAndJoinCoinflipMutation } from './blockchainApiSlice'; import { useAllowanceQuery, useApproveERC20Mutation } from '../CreateGameCard/blockchainApiSlice'; import toast from 'react-hot-toast'; -import { toastSettings, escrowConfig, toBigIntInWei } from '../../utils'; +import { toastSettings, escrowConfig, toBigIntInWei, convertBytesToNumber } from '../../utils'; import Button from '../../common/Button'; import { useFormReveal } from '../CreateGameCard/animations'; -import { useJoinGameMutation, useUpdateGameMutation } from '../../containers/App/api'; +import { useJoinGameMutation, useUpdateGameMutation } from './apiSlice'; import { ToggleCoinFlipSides } from '../../common/ToggleCoinFlipSides'; +import { useGetRandomNumberQuery } from '../CreateGameCard/apiSlice'; + export interface JoinCoinFlipGameCardProps extends React.Attributes, React.AllHTMLAttributes { @@ -62,21 +67,20 @@ export interface JoinCoinFlipGameCardProps extends React.Attributes, React.AllHT const JoinCoinFlipGameCard = ({ wagerType, currencyType, - tokenAddress, - playerAddress, gameUuid, - playerUuid, gameType, wagerAmount, escrowId, gameId, gameAttributes, - network, // eslint-disable-next-line @typescript-eslint/no-unused-vars css, ...rest }: JoinCoinFlipGameCardProps) => { - + const { openConnectModal } = useConnectModal(); + const { wallet, user, network } = useWegaStore(); + const randomnessQuery = useGetRandomNumberQuery(undefined); + const {tokenAddress, playerAddress, playerUuid} = useCreateGameParams({ wallet, user, network}); // TODO change to generic name const formRef = useRef(null); const detailsBlock = useRef(null) const [currentWagerType] = useState(wagerType); @@ -84,7 +88,7 @@ const JoinCoinFlipGameCard = ({ const {revealed, triggerRevealAnimation} = useFormReveal(false, formRef, detailsBlock); const [currentCoinSide] = useState(gameAttributes && Number(gameAttributes[0].value) === 1 ? 2 : 1); - const { register, formState: { errors }, handleSubmit } = useForm({ + const { register, formState: { errors }, handleSubmit, watch } = useForm({ mode: 'onChange', resolver: joiResolver(createGameSchema('wager', wagerAmount)) , reValidateMode: 'onChange', @@ -94,9 +98,10 @@ const JoinCoinFlipGameCard = ({ }); // approval for allowance + const wagerUSDValue = useTokenUSDValue(currentCurrencyType, watch('wager')); const isWagerApproved = (allowance: number, wagerAmount: number) => allowance >= wagerAmount; const allowanceQuery = useAllowanceQuery({ - spender: escrowConfig.address[network.id as keyof typeof escrowConfig.address], + spender: escrowConfig.address[network?.id as keyof typeof escrowConfig.address], owner: playerAddress, tokenAddress, }); @@ -115,10 +120,10 @@ const JoinCoinFlipGameCard = ({ const handleDepositWagerClick = async () => { try { if(!isWagerApproved(allowanceQuery.data, wagerAmount)) { - await approveERC20({ spender: escrowConfig.address[network.id as keyof typeof escrowConfig.address], wagerAsBigint: toBigIntInWei(wagerAmount), tokenAddress }).unwrap(); + await approveERC20({ spender: escrowConfig.address[network?.id as keyof typeof escrowConfig.address], wagerAsBigint: toBigIntInWei(wagerAmount), tokenAddress }).unwrap(); } const playerChoices = [Number(gameAttributes[0].value), currentCoinSide]; - await depositAndJoinCoinflip({escrowHash: escrowId, playerChoices }).unwrap(); + await depositAndJoinCoinflip({escrowHash: escrowId, playerChoices, randomness: [convertBytesToNumber(randomnessQuery.data?.randomness)] }).unwrap(); await joinGame({ newPlayerUuid: playerUuid, gameUuid }).unwrap(); await updateGame({ uuid: gameUuid, @@ -145,7 +150,10 @@ const JoinCoinFlipGameCard = ({ const navigateToGameUi = useNavigateTo() useEffect(() => { allowanceQuery.refetch(); - }, [tokenAddress, wagerAmount, isWagerApproved]); + }, [ + tokenAddress, + wagerAmount + ]); return ( {message} } /> - 00,00 USD + {wagerUSDValue.loading ? 'loading...' : wagerUSDValue.value} USD Balance: { isWagerbalanceLoading ? "Retrieving balance..." : userWagerBalance?.formatted + ' ' + userWagerBalance?.symbol } @@ -186,6 +194,14 @@ const JoinCoinFlipGameCard = ({ {/* */} { + (!wallet && openConnectModal) ? : */} { + (!wallet && openConnectModal) ? : } diff --git a/src/components/PlayGameSection/PlayDiceGameSection.tsx b/src/components/PlayGameSection/PlayDiceGameSection.tsx index ce82b5f..60ef44f 100644 --- a/src/components/PlayGameSection/PlayDiceGameSection.tsx +++ b/src/components/PlayGameSection/PlayDiceGameSection.tsx @@ -77,7 +77,7 @@ const PlayDiceGameSection: React.FC= ({ ); useEffect(() => { - if(rolled){ + if(rolled) { if(gameInfo.currentTurn >= maxTurns) { if(winners.length > 1) { showModal(MODAL_TYPES.WINNER_DECLARATION_WINNER_MODAL, { @@ -116,7 +116,7 @@ const PlayDiceGameSection: React.FC= ({ wallet?.address, gameInfo?.currentTurn, gameInfo?.currentRound, - rolled + rolled, ]); return players && gameInfo && {/* orbs */} @@ -130,6 +130,7 @@ const PlayDiceGameSection: React.FC= ({
{/* player card */} = ({ {/* searching for opponent box */} ({ - getGameResults: builder.query({ - query: ({ gameType, escrowHash, player }) => ({ + getGameResults: builder.query({ + query: ({ gameType, escrowHash, players }) => ({ functionName: 'gameResults', contract: ContractTypes.GAMECONTROLLER, method: 'READ', - args: [gameType.toUpperCase(), escrowHash, player] + args: [gameType.toUpperCase(), escrowHash, players] }), - transformResponse: (response: bigint[]) => response.map(r => Number(r)) + transformResponse: (response: bigint[]) => response.map(r => typeof r === 'object' ? Number(r) : [Number(r)]) }), getGameWinners: builder.query({ query: ({ gameType, escrowHash }) => ({ diff --git a/src/components/PlayGameSection/index.tsx b/src/components/PlayGameSection/index.tsx index 26dbeb7..71a5bfa 100644 --- a/src/components/PlayGameSection/index.tsx +++ b/src/components/PlayGameSection/index.tsx @@ -1,11 +1,9 @@ import { useEffect } from 'react'; -import { useGetSet } from 'react-use'; -import { useAppDispatch } from '../../hooks'; import PlayDiceGameSection from './PlayDiceGameSection'; import PlayCoinFlipGameSection from './PlayCoinFlipGameSection'; import { useGetGameWinnersQuery, - playGameBlockchainApiSlice + useGetGameResultsQuery, } from './blockchainApiSlice' import { @@ -52,26 +50,24 @@ const PlayGameSection: React.FC = ({ playerFlipChoices, ...rest }) => { - const dispatch = useAppDispatch(); - const [gameResults, setGameResults] = useGetSet(undefined); + // TODO // convert to hook const { gameType, wager: { wagerHash: escrowHash } } = game; - const getGameResultsOfAllPlayers = async () => { - const results = (await Promise.all(players.map(async (player) => dispatch(playGameBlockchainApiSlice.endpoints.getGameResults.initiate({ - gameType, - escrowHash: escrowHash as HexishString, - player: player.walletAddress as HexishString - }))))).map((result: any) => result.data); - setGameResults(results as unknown as number[][]); - } - const { data: winners } = useGetGameWinnersQuery({ + const gameResultsQuery = useGetGameResultsQuery({ + gameType, + escrowHash: escrowHash as HexishString, + players: players.map(player => player?.walletAddress) as HexishString[] + }); + const { data: winners, refetch: refetchGameWinners, isLoading: isWinnersQueryLoading } = useGetGameWinnersQuery({ gameType, escrowHash: escrowHash as HexishString, - }); + },); useEffect(() => { - getGameResultsOfAllPlayers(); + gameResultsQuery.refetch(); + refetchGameWinners() }, [players.length]); + const renderGame = () => { let Comp; if(!game) { @@ -79,11 +75,11 @@ const PlayGameSection: React.FC = ({ } else { Comp = GAME_COMPONENTS[game.gameType.toUpperCase()]; if(children) { - return gameResults() && winners.length > 0 ? = ({ ...rest }}>{children} : } else { - return gameResults() && winners.length ? { - const { user, network, wallet } = useWegaStore(); const params = useParams(); const { game } = useFirebaseData(params.id as string); - return (network?.id && wallet && user.uuid && game) ? ( + return game ? ( <> Join - {BADGE_TEXTS[game.gameType]} @@ -31,14 +29,10 @@ const JoinGamePage = () => { Match wager }> -