diff --git a/ui/.env b/ui/.env
index 51abe46d9..1060dd74f 100644
--- a/ui/.env
+++ b/ui/.env
@@ -1 +1,2 @@
-NEXT_PUBLIC_NETWORK="mainnet"
\ No newline at end of file
+NEXT_PUBLIC_NETWORK="mainnet"
+NEXT_PUBLIC_TOURNAMENT_ENDED="false"
\ No newline at end of file
diff --git a/ui/public/blobert.png b/ui/public/blobert.png
new file mode 100644
index 000000000..13b9d7e88
Binary files /dev/null and b/ui/public/blobert.png differ
diff --git a/ui/public/insert-blobert-hover.png b/ui/public/insert-blobert-hover.png
new file mode 100644
index 000000000..c803a2067
Binary files /dev/null and b/ui/public/insert-blobert-hover.png differ
diff --git a/ui/public/insert-blobert.png b/ui/public/insert-blobert.png
new file mode 100644
index 000000000..89e30b0ce
Binary files /dev/null and b/ui/public/insert-blobert.png differ
diff --git a/ui/src/app/components/notifications/SpecialBeast.tsx b/ui/src/app/components/notifications/SpecialBeast.tsx
index 7c2eef350..ae5c5d5b9 100644
--- a/ui/src/app/components/notifications/SpecialBeast.tsx
+++ b/ui/src/app/components/notifications/SpecialBeast.tsx
@@ -1,13 +1,13 @@
-import { useState, useEffect } from "react";
import { fetchBeastImage } from "@/app/api/fetchMetadata";
-import Image from "next/image";
-import useUIStore from "@/app/hooks/useUIStore";
-import { Contract } from "starknet";
-import { processBeastName } from "@/app/lib/utils";
+import { Button } from "@/app/components/buttons/Button";
import TwitterShareButton from "@/app/components/buttons/TwitterShareButtons";
import useAdventurerStore from "@/app/hooks/useAdventurerStore";
-import { Button } from "@/app/components/buttons/Button";
+import useUIStore from "@/app/hooks/useUIStore";
import { networkConfig } from "@/app/lib/networkConfig";
+import { processBeastName } from "@/app/lib/utils";
+import Image from "next/image";
+import { useEffect, useState } from "react";
+import { Contract } from "starknet";
interface SpecialBeastProps {
beastsContract: Contract;
@@ -52,7 +52,8 @@ export const SpecialBeast = ({ beastsContract }: SpecialBeastProps) => {
const beastUrl =
(networkConfig[network!].beastsViewer ?? "") +
"/" +
- specialBeast?.tokenId?.toString();
+ specialBeast?.tokenId?.toString() +
+ "?beast_origin=client";
return (
diff --git a/ui/src/app/components/start/AdventurersList.tsx b/ui/src/app/components/start/AdventurersList.tsx
index 4ebf0d2aa..4aeefa544 100644
--- a/ui/src/app/components/start/AdventurersList.tsx
+++ b/ui/src/app/components/start/AdventurersList.tsx
@@ -1,32 +1,32 @@
-import { useState, useEffect, useRef, useCallback, useMemo } from "react";
-import {
- Contract,
- AccountInterface,
- validateAndParseAddress,
- constants,
-} from "starknet";
-import { StarknetIdNavigator } from "starknetid.js";
-import { useProvider } from "@starknet-react/core";
import { Button } from "@/app/components/buttons/Button";
-import useAdventurerStore from "@/app/hooks/useAdventurerStore";
import {
+ CartridgeIcon,
+ ClockIcon,
CoinIcon,
HeartIcon,
SkullIcon,
- ClockIcon,
- CartridgeIcon,
StarknetIdIcon,
} from "@/app/components/icons/Icons";
-import useUIStore from "@/app/hooks/useUIStore";
-import { useQueriesStore } from "@/app/hooks/useQueryStore";
import LootIconLoader from "@/app/components/icons/Loader";
-import useCustomQuery from "@/app/hooks/useCustomQuery";
+import { AdventurerListCard } from "@/app/components/start/AdventurerListCard";
import { getAdventurersByOwner } from "@/app/hooks/graphql/queries";
+import useAdventurerStore from "@/app/hooks/useAdventurerStore";
+import useCustomQuery from "@/app/hooks/useCustomQuery";
import useNetworkAccount from "@/app/hooks/useNetworkAccount";
-import { indexAddress, padAddress, calculateLevel } from "@/app/lib/utils";
-import { Adventurer } from "@/app/types";
-import { AdventurerListCard } from "@/app/components/start/AdventurerListCard";
+import { useQueriesStore } from "@/app/hooks/useQueryStore";
import useTransactionCartStore from "@/app/hooks/useTransactionCartStore";
+import useUIStore from "@/app/hooks/useUIStore";
+import { calculateLevel, indexAddress, padAddress } from "@/app/lib/utils";
+import { Adventurer } from "@/app/types";
+import { useProvider } from "@starknet-react/core";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import {
+ AccountInterface,
+ constants,
+ Contract,
+ validateAndParseAddress,
+} from "starknet";
+import { StarknetIdNavigator } from "starknetid.js";
export interface AdventurerListProps {
isActive: boolean;
@@ -58,7 +58,7 @@ export const AdventurersList = ({
constants.StarknetChainId.SN_MAIN
);
const [selectedIndex, setSelectedIndex] = useState(-1);
- const [showZeroHealth, setShowZeroHealth] = useState(true);
+ const [showZeroHealth, setShowZeroHealth] = useState(false);
const [isTransferOpen, setIsTransferOpen] = useState(false);
const [adventurerForTransfer, setAdventurerForTransfer] =
useState
(null);
diff --git a/ui/src/app/components/start/CreateAdventurer.tsx b/ui/src/app/components/start/CreateAdventurer.tsx
index f55892778..b72e30b19 100644
--- a/ui/src/app/components/start/CreateAdventurer.tsx
+++ b/ui/src/app/components/start/CreateAdventurer.tsx
@@ -1,9 +1,9 @@
-import React, { useState, useEffect, useCallback } from "react";
-import { Contract } from "starknet";
-import { FormData } from "@/app/types";
import { AdventurerName } from "@/app/components/start/AdventurerName";
-import { WeaponSelect } from "@/app/components/start/WeaponSelect";
import { Spawn } from "@/app/components/start/Spawn";
+import { WeaponSelect } from "@/app/components/start/WeaponSelect";
+import { FormData } from "@/app/types";
+import React, { useCallback, useEffect, useState } from "react";
+import { Contract } from "starknet";
export interface CreateAdventurerProps {
isActive: boolean;
@@ -11,6 +11,7 @@ export interface CreateAdventurerProps {
spawn: (...args: any[]) => any;
lordsBalance?: bigint;
goldenTokenData: any;
+ blobertsData: any;
gameContract: Contract;
getBalances: () => Promise;
mintLords: (lordsAmount: number) => Promise;
@@ -23,6 +24,7 @@ export const CreateAdventurer = ({
spawn,
lordsBalance,
goldenTokenData,
+ blobertsData,
gameContract,
getBalances,
mintLords,
@@ -119,6 +121,7 @@ export const CreateAdventurer = ({
handleBack={handleBack}
lordsBalance={lordsBalance}
goldenTokenData={goldenTokenData}
+ blobertsData={blobertsData}
gameContract={gameContract}
getBalances={getBalances}
mintLords={mintLords}
@@ -144,6 +147,7 @@ export const CreateAdventurer = ({
handleBack={handleBack}
lordsBalance={lordsBalance}
goldenTokenData={goldenTokenData}
+ blobertsData={blobertsData}
gameContract={gameContract}
getBalances={getBalances}
mintLords={mintLords}
diff --git a/ui/src/app/components/start/Spawn.tsx b/ui/src/app/components/start/Spawn.tsx
index 40d442d2a..13e0f79af 100644
--- a/ui/src/app/components/start/Spawn.tsx
+++ b/ui/src/app/components/start/Spawn.tsx
@@ -25,12 +25,14 @@ export interface SpawnProps {
spawn: (
formData: FormData,
goldenTokenId: string,
+ blobertTokenId: string,
revenueAddresses: string[],
costToPlay?: number
) => Promise;
handleBack: () => void;
lordsBalance?: bigint;
goldenTokenData: any;
+ blobertsData: any;
gameContract: Contract;
getBalances: () => Promise;
mintLords: (lordsAmount: number) => Promise;
@@ -43,6 +45,7 @@ export const Spawn = ({
handleBack,
lordsBalance,
goldenTokenData,
+ blobertsData,
gameContract,
getBalances,
mintLords,
@@ -50,7 +53,8 @@ export const Spawn = ({
}: SpawnProps) => {
const [paymentInitiated, setPaymentInitiated] = useState(false);
const [formFilled, setFormFilled] = useState(false);
- const [usableToken, setUsableToken] = useState("0");
+ const [usableGoldenToken, setUsableGoldenToken] = useState("0");
+ const [usableBlobertToken, setUsableBlobertToken] = useState("0");
const [isHoveringLords, setIsHoveringLords] = useState(false);
const [showPaymentDetails, setShowPaymentDetails] = useState(false);
const isWrongNetwork = useUIStore((state) => state.isWrongNetwork);
@@ -76,6 +80,7 @@ export const Spawn = ({
await spawn(
formData,
"0",
+ "0",
networkConfig[network!].revenueAddresses,
lordsGameCost
);
@@ -84,8 +89,8 @@ export const Spawn = ({
}
};
- const tokens = goldenTokenData?.getERC721Tokens;
- const goldenTokens: number[] = tokens?.map(
+ const goldenTokens = goldenTokenData?.getERC721Tokens;
+ const goldenTokenIds: number[] = goldenTokens?.map(
(token: GameToken) => token.token_id
);
@@ -97,7 +102,26 @@ export const Spawn = ({
CallData.compile(["0", tokenId.toString()])
);
if (canPlay) {
- setUsableToken(tokenId.toString());
+ setUsableGoldenToken(tokenId.toString());
+ break;
+ }
+ }
+ };
+
+ const blobertTokens = blobertsData?.tokens;
+ const blobertTokenIds: number[] = blobertTokens?.map(
+ (token: any) => token.tokenId
+ );
+
+ const getUsableBlobertToken = async (tokenIds: number[]) => {
+ // Loop through contract calls to see if the token is usable, if none then return 0
+ for (let tokenId of tokenIds) {
+ const canPlay = await gameContract.call(
+ "free_game_available",
+ CallData.compile(["1", tokenId.toString()])
+ );
+ if (canPlay) {
+ setUsableBlobertToken(tokenId.toString());
break;
}
}
@@ -106,11 +130,16 @@ export const Spawn = ({
const { play: spawnPlay } = useUiSounds(soundSelector.spawn);
const { play: coinPlay } = useUiSounds(soundSelector.coin);
+ const tournamentEnded = process.env.NEXT_PUBLIC_TOURNAMENT_ENDED === "true";
+
useEffect(() => {
- getUsableGoldenToken(goldenTokens ?? []);
+ getUsableGoldenToken(goldenTokenIds ?? []);
+ if (tournamentEnded) {
+ getUsableBlobertToken(blobertTokenIds ?? []);
+ }
}, []);
- const handlePayment = async (goldenToken: boolean) => {
+ const handlePayment = async (goldenToken: boolean, blobertToken: boolean) => {
spawnPlay();
coinPlay();
resetNotification();
@@ -118,7 +147,8 @@ export const Spawn = ({
try {
await spawn(
formData,
- goldenToken ? usableToken : "0",
+ goldenToken ? usableGoldenToken : "0",
+ blobertToken && tournamentEnded ? usableBlobertToken : "0",
networkConfig[network!].revenueAddresses,
lordsGameCost
);
@@ -190,11 +220,10 @@ export const Spawn = ({
onMouseEnter={() => setIsHoveringLords(true)}
onMouseLeave={() => setIsHoveringLords(false)}
onClick={() => {
- if (usableToken !== "0") {
- handlePayment(true);
- } else {
- handlePayment(false);
- }
+ handlePayment(
+ usableGoldenToken !== "0",
+ usableBlobertToken !== "0"
+ );
}}
>
@@ -206,11 +235,19 @@ export const Spawn = ({
: "bg-terminal-green/20"
}`}
>
- {usableToken !== "0" ? (
+ {usableGoldenToken !== "0" ? (
+
+ ) : usableBlobertToken !== "0" ? (
+
+
@@ -225,10 +262,14 @@ export const Spawn = ({
)}
{!paymentInitiated && (
-
-
diff --git a/ui/src/app/containers/AdventurerScreen.tsx b/ui/src/app/containers/AdventurerScreen.tsx
index ca39b690d..325d45e2b 100644
--- a/ui/src/app/containers/AdventurerScreen.tsx
+++ b/ui/src/app/containers/AdventurerScreen.tsx
@@ -19,6 +19,7 @@ interface AdventurerScreenProps {
spawn: (
formData: FormData,
goldenTokenId: string,
+ blobertTokenId: string,
revenueAddresses: string[],
costToPlay?: number
) => Promise;
@@ -26,6 +27,7 @@ interface AdventurerScreenProps {
lordsBalance?: bigint;
gameContract: Contract;
goldenTokenData: any;
+ blobertsData: any;
getBalances: () => Promise;
mintLords: (lordsAmount: number) => Promise;
costToPlay: bigint;
@@ -47,6 +49,7 @@ export default function AdventurerScreen({
lordsBalance,
gameContract,
goldenTokenData,
+ blobertsData,
getBalances,
mintLords,
costToPlay,
@@ -136,6 +139,7 @@ export default function AdventurerScreen({
spawn={spawn}
lordsBalance={lordsBalance}
goldenTokenData={goldenTokenData}
+ blobertsData={blobertsData}
gameContract={gameContract}
getBalances={getBalances}
mintLords={mintLords}
diff --git a/ui/src/app/hooks/graphql/queries.ts b/ui/src/app/hooks/graphql/queries.ts
index d64bca395..886c5d125 100644
--- a/ui/src/app/hooks/graphql/queries.ts
+++ b/ui/src/app/hooks/graphql/queries.ts
@@ -468,26 +468,42 @@ const getCollectionsTotals = gql`
}
`;
+const getOwnerTokens = gql`
+ query getOwnerTokens($token: HexValue, $owner: HexValue) {
+ tokens(
+ where: { token: { eq: $token }, nftOwnerAddress: { eq: $owner } }
+ limit: 1000
+ ) {
+ hash
+ nftOwnerAddress
+ timestamp
+ token
+ tokenId
+ }
+ }
+`;
+
export {
- getLatestDiscoveries,
- getLastBeastDiscovery,
+ getAdventurerById,
+ getAdventurerCounts,
+ getAdventurerRank,
getAdventurersByOwner,
getAdventurersByOwnerCount,
- getAdventurerById,
getAdventurersInList,
- getBeast,
- getKilledBeasts,
+ getAliveAdventurersByXPPaginated,
+ getAliveAdventurersCount,
getBattlesByBeast,
+ getBeast,
+ getCollectionsTotals,
+ getDeadAdventurersByXPPaginated,
getDiscoveriesAndBattlesByAdventurerPaginated,
- getLatestMarketItems,
+ getDiscoveryBattleCount,
+ getGoldenTokensByOwner,
getItemsByAdventurer,
- getDeadAdventurersByXPPaginated,
- getAliveAdventurersByXPPaginated,
+ getKilledBeasts,
+ getLastBeastDiscovery,
+ getLatestDiscoveries,
+ getLatestMarketItems,
+ getOwnerTokens,
getScoresInList,
- getGoldenTokensByOwner,
- getAdventurerCounts,
- getAliveAdventurersCount,
- getDiscoveryBattleCount,
- getAdventurerRank,
- getCollectionsTotals,
};
diff --git a/ui/src/app/lib/clients.ts b/ui/src/app/lib/clients.ts
index 78bc632cf..ee4642755 100644
--- a/ui/src/app/lib/clients.ts
+++ b/ui/src/app/lib/clients.ts
@@ -1,18 +1,4 @@
import { ApolloClient, InMemoryCache } from "@apollo/client";
-// import { setContext } from "@apollo/client/link/context";
-// import { Network } from "@/app/hooks/useUIStore";
-
-// const createAuthLink = () =>
-// setContext((_, { headers }) => {
-// return {
-// headers: {
-// ...headers,
-// "Cache-Control": "no-cache, no-store, must-revalidate",
-// Pragma: "no-cache",
-// Expires: "0",
-// },
-// };
-// });
export const goldenTokenClient = (GQLUrl: string) => {
return new ApolloClient({
@@ -36,16 +22,6 @@ export const goldenTokenClient = (GQLUrl: string) => {
};
export const gameClient = (GQLUrl: string) => {
- // const httpLink = createHttpLink({
- // uri: `/api/graphql-proxy?api=${network}`,
- // fetchOptions: {
- // next: { revalidate: 0 },
- // cache: "no-store",
- // },
- // });
-
- // const authLink = createAuthLink();
-
return new ApolloClient({
uri: GQLUrl,
defaultOptions: {
diff --git a/ui/src/app/lib/networkConfig.ts b/ui/src/app/lib/networkConfig.ts
index 55cfa537e..698a0d77d 100644
--- a/ui/src/app/lib/networkConfig.ts
+++ b/ui/src/app/lib/networkConfig.ts
@@ -14,6 +14,7 @@ export const networkConfig = {
"0x041b6ffc02ce30c6e941f1b34244ef8af0b3e8a70f5528476a7a68765afd6b39",
goldenTokenAddress:
"0x07626660faba349aad9ad2aaa0ff8645c079fa8e043a168d640d92472806eeac",
+ tournamentWinnerAddress: "0x0",
revenueAddresses: [
"0x0314924118945405ac0bcd6181457712795c0effc29d8dd3be86d3f3ec62adc1",
],
@@ -45,6 +46,8 @@ export const networkConfig = {
"0x0158160018d590d93528995b340260e65aedd76d28a686e9daa5c4e8fad0c5dd",
goldenTokenAddress:
"0x04f5e296c805126637552cf3930e857f380e7c078e8f00696de4fc8545356b1d",
+ tournamentWinnerAddress:
+ "0x00539f522b29ae9251dbf7443c7a950cf260372e69efab3710a11bf17a9599f1",
revenueAddresses: [
"0x036cE487952f25878a0158bA4A0C2Eb5eb66f0282567163a4B893A0EA5847D2d",
"0x0616E6a5F9b1f86a0Ece6E965B2f3b27E3D784be79Cb2F6304D92Db100C7D29E",
@@ -77,6 +80,7 @@ export const networkConfig = {
lordsAddress: "0x0",
beastsAddress: "0x0",
goldenTokenAddress: "0x0",
+ tournamentWinnerAddress: "0x0",
revenueAddresses: ["0x0"],
pragmaAddress: "0x0",
rendererAddress: "0x0",
@@ -105,6 +109,7 @@ export const networkConfig = {
lordsAddress: "0x0",
beastsAddress: "0x0",
goldenTokenAddress: "0x0",
+ tournamentWinnerAddress: "0x0",
revenueAddresses: ["0x0"],
pragmaAddress: "0x0",
rendererAddress: "0x0",
diff --git a/ui/src/app/lib/utils/syscalls.ts b/ui/src/app/lib/utils/syscalls.ts
index 1772e5245..e45a0d385 100644
--- a/ui/src/app/lib/utils/syscalls.ts
+++ b/ui/src/app/lib/utils/syscalls.ts
@@ -411,7 +411,8 @@ export function createSyscalls({
dollarPrice: bigint,
freeVRF: boolean,
costToPlay?: number,
- goldenTokenId?: string
+ goldenTokenId?: string,
+ blobertTokenId?: string
) => [
...(freeVRF
? []
@@ -426,7 +427,7 @@ export function createSyscalls({
],
},
]),
- ...(goldenTokenId === "0"
+ ...(goldenTokenId === "0" && blobertTokenId === "0"
? [
{
contractAddress: lordsContract?.address ?? "",
@@ -450,6 +451,7 @@ export function createSyscalls({
const spawn = async (
formData: FormData,
goldenTokenId: string,
+ blobertTokenId: string,
revenueAddresses: string[],
costToPlay?: number
) => {
@@ -466,7 +468,7 @@ export function createSyscalls({
goldenTokenId,
"0", // delay_stat_reveal
rendererContractAddress,
- "0",
+ blobertTokenId,
"0",
],
};
@@ -482,7 +484,7 @@ export function createSyscalls({
if (!enoughEth && !freeVRF) {
return handleInsufficientFunds("eth");
}
- if (!enoughLords && goldenTokenId === "0") {
+ if (!enoughLords && goldenTokenId === "0" && blobertTokenId === "0") {
return handleInsufficientFunds("lords");
}
@@ -491,7 +493,8 @@ export function createSyscalls({
dollarPrice,
freeVRF,
costToPlay,
- goldenTokenId
+ goldenTokenId,
+ blobertTokenId
);
}
diff --git a/ui/src/app/page.tsx b/ui/src/app/page.tsx
index ef0a7ec25..2de3cb5a3 100644
--- a/ui/src/app/page.tsx
+++ b/ui/src/app/page.tsx
@@ -39,6 +39,7 @@ import {
getLastBeastDiscovery,
getLatestDiscoveries,
getLatestMarketItems,
+ getOwnerTokens,
} from "@/app/hooks/graphql/queries";
import useAdventurerStore from "@/app/hooks/useAdventurerStore";
import useControls from "@/app/hooks/useControls";
@@ -51,7 +52,7 @@ import useTransactionCartStore from "@/app/hooks/useTransactionCartStore";
import useTransactionManager from "@/app/hooks/useTransactionManager";
import useUIStore, { ScreenPage } from "@/app/hooks/useUIStore";
import { fetchBalances, fetchEthBalance } from "@/app/lib/balances";
-import { goldenTokenClient } from "@/app/lib/clients";
+import { gameClient, goldenTokenClient } from "@/app/lib/clients";
import { VRF_WAIT_TIME } from "@/app/lib/constants";
import { networkConfig } from "@/app/lib/networkConfig";
import {
@@ -473,6 +474,11 @@ function Home() {
};
}, [address]);
+ const gameClientInstance = useMemo(
+ () => gameClient(networkConfig[network!].lsGQLURL),
+ [network]
+ );
+
const goldenTokenClientInstance = useMemo(
() => goldenTokenClient(networkConfig[network!].tokensGQLURL),
[network]
@@ -483,6 +489,20 @@ function Home() {
variables: goldenTokenVariables,
});
+ const blobertTokenVariables = useMemo(() => {
+ return {
+ token: indexAddress(
+ networkConfig[network!].tournamentWinnerAddress.toLowerCase()
+ ),
+ owner: indexAddress(address ?? "").toLowerCase(),
+ };
+ }, [address, network]);
+
+ const { data: blobertsData } = useQuery(getOwnerTokens, {
+ client: gameClientInstance,
+ variables: blobertTokenVariables,
+ });
+
const handleSwitchAdventurer = useCallback(
async (adventurerId: number) => {
setIsLoading();
@@ -871,6 +891,7 @@ function Home() {
lordsBalance={lordsBalance}
gameContract={gameContract!}
goldenTokenData={goldenTokenData}
+ blobertsData={blobertsData}
getBalances={getBalances}
mintLords={mintLords}
costToPlay={costToPlay}