Skip to content

Commit

Permalink
- create the full claiming flow
Browse files Browse the repository at this point in the history
- add metadata fetcher some hardcoded adventurers
- process encrypted nft images and display
- create a reveal system for the stats with some animations
- add connect controller and delegation flow
-- this currently has an issue with popping up braavos as well as controller for the tx
  • Loading branch information
starknetdev committed Aug 19, 2024
1 parent 4892130 commit d6a4538
Show file tree
Hide file tree
Showing 32 changed files with 1,044 additions and 199 deletions.
Binary file modified bun.lockb
Binary file not shown.
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
"preview": "vite preview"
},
"dependencies": {
"@cartridge/connector": "^0.3.38",
"@cartridge/connector": "^0.3.40",
"@fontsource/vt323": "^5.0.13",
"@starknet-react/core": "^2.9.0",
"buffer": "^6.0.3",
"canvas-confetti": "^1.9.3",
"class-variance-authority": "^0.7.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand All @@ -23,6 +24,8 @@
},
"devDependencies": {
"@eslint/js": "^9.8.0",
"@types/canvas-confetti": "^1.6.4",
"@types/node": "^22.4.1",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
Expand All @@ -35,6 +38,7 @@
"tailwindcss": "^3.4.9",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.0",
"vite": "^5.4.0"
"vite": "^5.4.0",
"vite-plugin-mkcert": "^1.17.6"
}
}
Binary file added public/animations/coin-sprites.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions public/icons/cartridge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/chevron.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions public/icons/twitter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/scenes/fountain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/scenes/opening.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
210 changes: 35 additions & 175 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,105 +1,39 @@
import React from "react";
import { Button } from "./components/Button";
import { useUiSounds, soundSelector } from "./hooks/useUiSound";
import { useAccount, useConnect, useDisconnect } from "@starknet-react/core";
import { getWalletConnectors } from "./lib/connectors";
import { CompleteIcon } from "./components/Icons";
import { COLLECTIONS_MAP } from "./lib/constants";
import { displayAddress } from "./lib/utils";
import { useEffect } from "react";
import Head from "./head";
import Claim from "./containers/Claim";
import Claimed from "./containers/Claimed";
import Claiming from "./containers/Claiming";
import { useUIStore } from "./hooks/useUIStore";
import { networkConfig } from "./lib/networkConfig";
import { fetchAdventurerMetadata } from "./api/fetchMetadata";
import { Network } from "./lib/types";

const App = () => {
const { play: clickPlay } = useUiSounds(soundSelector.click);

const { connectors, connect } = useConnect();
const { disconnect } = useDisconnect();
const { address } = useAccount();

const walletConnectors = getWalletConnectors(connectors);

const unclaimedCollectionsByOwnerData = [
{
token: "0x1",
tokenId: 1,
claimed: false,
},
{
token: "0x1",
tokenId: 2,
claimed: false,
},
{
token: "0x2",
tokenId: 1,
claimed: false,
},
];

const getCollectionFreeGames = (token: string) => {
return unclaimedCollectionsByOwnerData.filter(
(item) => item.token === token
);
};

const renderCollection = (token: string, image: string, alt: string) => {
const freeGames = getCollectionFreeGames(token);
return (
<div className="flex flex-col gap-2 items-center justify-center relative">
<span className="absolute w-full h-full bg-terminal-black opacity-70 z-10" />
<span className="absolute w-1/2 z-20">
<CompleteIcon />
</span>
<span className="relative h-20 w-20 border border-terminal-green">
<img src={image} alt={alt} className="w-full h-full" />
</span>
{freeGames.length > 0 && (
<span className="w-full absolute top-24 flex flex-row bg-terminal-green text-terminal-black rounded-lg justify-center">
{`${freeGames.length}
Game${freeGames.length > 1 ? "s" : ""}
`}
</span>
)}
</div>
);
};

const collectionsData = [
{
token: COLLECTIONS_MAP["Duck"],
alt: "Duck",
image: "/Duck.png",
},
{
token: COLLECTIONS_MAP["Blobert"],
alt: "Blobert",
image: "/Blobert.png",
},
{
token: COLLECTIONS_MAP["Everai"],
alt: "Everai",
image: "/Everai.png",
},
{
token: COLLECTIONS_MAP["Pixel Banners"],
alt: "Pixel Banners",
image: "/Pixel-Banners.png",
},
{
token: COLLECTIONS_MAP["StarkID"],
alt: "StarkID",
image: "/StarkID.png",
},
{
token: COLLECTIONS_MAP["Realms"],
alt: "Realms",
image: "/Realms.png",
},
{
token: COLLECTIONS_MAP["Open Division"],
alt: "Open Division",
image: "/Open-Division.png",
},
];
const { claimed, claiming, setAdventurersMetadata, setClaiming, setClaimed } =
useUIStore();

const adventurers = [99, 100, 101, 102, 103, 106];
const network: Network = import.meta.env.VITE_NETWORK;

useEffect(() => {
if (claiming) {
const fetchImages = async () => {
const adventurersMetadata = await Promise.all(
adventurers.map((adventurer) =>
fetchAdventurerMetadata(
networkConfig[network!].gameAddress,
adventurer,
networkConfig[network!].rpcUrl
)
)
);
setAdventurersMetadata(adventurersMetadata);
setClaiming(false);
setClaimed(true);
};
fetchImages();
}
}, [claiming]);

return (
<html lang="en">
Expand All @@ -113,82 +47,8 @@ const App = () => {
alt="crt green mask"
className="absolute w-full pointer-events-none crt-frame hidden sm:block"
/>
<div className="min-h-screen w-full flex flex-col items-center bg-terminal-black sm:pt-8 sm:p-8 lg:p-10 2xl:p-20">
<div className="w-full h-full flex flex-col items-center gap-5 py-10 sm:gap-20 sm:p-0">
<h1 className="m-0 uppercase text-4xl sm:text-6xl text-center">
Mainnet Tournament Claim
</h1>
<div className="flex flex-col">
<div className="flex flex-row items-center justify-between">
<p className="text-2xl uppercase">Collections</p>
{!address && (
<p className="text-2xl uppercase">
{displayAddress(address ?? "")}
</p>
)}
</div>
<div className="grid grid-cols-4 gap-2 gap-y-14 sm:flex sm:flex-row sm:items-center sm:gap-5 h-[200px] sm:h-[100px]">
{collectionsData.map((collection) =>
renderCollection(
collection.token,
collection.image,
collection.alt
)
)}
</div>
<div className="w-full h-[200px] sm:h-[300px] flex flex-col border border-terminal-green items-center gap-10 p-5 mt-20">
{!address ? (
<>
<p className="text-2xl uppercase">Check Eligibility</p>
<div className="hidden sm:flex flex-row gap-2">
{walletConnectors.map((connector, index) => (
<Button
size={"lg"}
disabled={address !== undefined}
onClick={() => {
disconnect();
connect({ connector });
}}
key={index}
>
{`Login With ${connector.id}`}
</Button>
))}
</div>
<div className="sm:hidden flex text-2xl">
Claim Games on Desktop
</div>
</>
) : (
<div className="flex flex-col gap-5">
<p className="text-2xl uppercase">
10 Free Games Claimable
</p>
<Button
size={"lg"}
disabled={address === undefined}
onClick={() => {
clickPlay();
}}
>
Claim
</Button>
<Button
size={"lg"}
disabled={address === undefined}
onClick={() => {
disconnect();
clickPlay();
}}
>
Disconnect
</Button>
</div>
)}
</div>
</div>
</div>
</div>
{claimed ? <Claimed /> : <Claim />}
{claiming && <Claiming />}
</body>
</html>
);
Expand Down
53 changes: 53 additions & 0 deletions src/api/fetchMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Buffer } from "buffer";

export const fetchAdventurerMetadata = async (
gameAddress: string,
tokenId: number,
rpcUrl: string
) => {
const response = await fetch(rpcUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "starknet_call",
params: [
{
contract_address: gameAddress,
entry_point_selector:
"0x0226ad7e84c1fe08eb4c525ed93cccadf9517670341304571e66f7c4f95cbe54", // token_uri
calldata: [tokenId.toString(16), "0x0"],
},
"pending",
],
id: 0,
}),
});

const data = await response.json();

if (response.ok) {
console.log("Beast fetched successfully");
} else {
console.error("Error in response:", data);
}

// Step 1: Convert hex strings to a single string
const hexString = data.result.slice(1).join("").slice(2); // Remove '0x' prefix

// Step 2: Convert hex to ASCII
const fullString = hexString
.match(/.{1,2}/g)!
.map((hex: any) => String.fromCharCode(parseInt(hex, 16)))
.join("");

const encodedString = fullString.split(",")[1];

const decodedString = Buffer.from(encodedString, "base64").toString("utf-8");

const jsonData = JSON.parse(decodedString);

return jsonData;
};
Loading

0 comments on commit d6a4538

Please sign in to comment.