Skip to content

Commit

Permalink
feat: add smart contract for nft
Browse files Browse the repository at this point in the history
  • Loading branch information
rthomare committed Jun 6, 2023
1 parent 1ceb1b2 commit eb3b14f
Show file tree
Hide file tree
Showing 17 changed files with 388 additions and 51 deletions.
5 changes: 0 additions & 5 deletions examples/alchemy-daapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,8 @@
"next": "^13.4.1",
"react": "18.2.0",
"react-dom": "18.2.0",
<<<<<<< HEAD
"viem": "latest",
"wagmi": "latest",
=======
"viem": "^0.3.30",
"wagmi": "^1.0.5",
>>>>>>> 07da5f0 (feat: clean up components for profile and nft fetching)
"zod": "^3.21.4"
},
"devDependencies": {
Expand Down
74 changes: 74 additions & 0 deletions examples/alchemy-daapp/src/clients/appState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useAccount } from "wagmi";
import { useNFTsQuery } from "./nfts";
import { useEffect, useState } from "react";

export type AppState =
| {
state: "LOADING";
eoaAddress: undefined;
scwAddress: undefined;
}
| {
state: "UNCONNECTED";
eoaAddress: undefined;
scwAddress: undefined;
}
| {
state: "NO_SCW";
eoaAddress: string;
scwAddress: undefined;
}
| {
state: "HAS_SCW";
eoaAddress: string;
scwAddress: string;
};

export function useAppState(): AppState {
const { address, isConnected } = useAccount();
const nfts = useNFTsQuery(address);
const [state, setState] = useState<AppState>({
state: "UNCONNECTED",
eoaAddress: undefined,
scwAddress: undefined,
});
useEffect(() => {
if (!isConnected || !address) {
setState({
state: "UNCONNECTED",
eoaAddress: undefined,
scwAddress: undefined,
});
return;
}

if (nfts.isLoading) {
setState({
state: "LOADING",
eoaAddress: undefined,
scwAddress: undefined,
});
return;
}
const scwNFT = nfts?.data?.ownedNfts.find((value) => {
return value.contract.address === "0x0000000000000000";
});
const scwAttribute = scwNFT?.metadata.attributes?.find((attribute) => {
attribute.trait_type === "SCW";
});
if (!scwNFT || !scwAttribute) {
setState({
state: "NO_SCW",
eoaAddress: address as `0x${string}`,
scwAddress: undefined,
});
} else {
setState({
state: "HAS_SCW",
eoaAddress: address as `0x${string}`,
scwAddress: scwAttribute.value!,
});
}
}, [address, isConnected, nfts]);
return state;
}
17 changes: 9 additions & 8 deletions examples/alchemy-daapp/src/clients/nfts.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {useQuery} from "@tanstack/react-query";
import {getNFTs} from "../http/endpoints";
import {OwnedNFTsResponse} from "../declarations/api";
import { useQuery } from "@tanstack/react-query";
import { getNFTs } from "../http/endpoints";

export function useNFTsQuery(ethAddress?: string) {
return useQuery(["nfts", ethAddress], () => {
if (ethAddress) {
return getNFTs(ethAddress);
} else {
return Promise.resolve({data: []} as unknown as OwnedNFTsResponse);
}
return ethAddress
? getNFTs(ethAddress)
: Promise.resolve({
ownedNfts: [],
totalCount: 0,
blockHash: "0x000000000000",
});
});
}
2 changes: 1 addition & 1 deletion examples/alchemy-daapp/src/clients/onboarding.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useCallback, useState} from "react";
import { useCallback, useState } from "react";

export interface OnboardingContext {
accountAddress?: string;
Expand Down
10 changes: 7 additions & 3 deletions examples/alchemy-daapp/src/components/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { Box, Flex } from "@chakra-ui/react";
import Link from "next/link";
import Image from "next/image";
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { useAppState } from "~/clients/appState";

export default function NavigationBar() {
const { state } = useAppState();
return (
<Flex
padding="20px 10px"
Expand All @@ -15,9 +17,11 @@ export default function NavigationBar() {
<Link href="/">
<Image width={300} height={100} src="/logo.svg" alt="logo" />
</Link>
<Box position="absolute" right="20px" top="20px">
<ConnectButton />
</Box>
{state !== "UNCONNECTED" && (
<Box position="absolute" right="20px" top="20px">
<ConnectButton />
</Box>
)}
</Flex>
);
}
18 changes: 18 additions & 0 deletions examples/alchemy-daapp/src/components/connect/ConnectPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Center, Heading, Text, VStack } from "@chakra-ui/react";
import { ConnectButton } from "@rainbow-me/rainbowkit";

export function ConnectPage() {
return (
<Center>
<VStack gap={4}>
<Heading size="lg">Welcome to the Alchemy exmple D🅰️🅰️pp!</Heading>
<Text align="center">
We're excited for you to start using account abstraction!! <br />
Click below to connect your wallet, and create your own account
abstrated smart contract wallet.
</Text>
<ConnectButton />
</VStack>
</Center>
);
}
14 changes: 7 additions & 7 deletions examples/alchemy-daapp/src/components/profile/NFTs.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {memo} from "react";
import { memo } from "react";
import NFT from "./NFT";
import {useNFTsQuery} from "../../clients/nfts";
import {LoadingScreen} from "../../screens/LoadingScreen";
import {ErrorScreen} from "../../screens/ErrorScreen";
import {BoxProps, Grid, Text} from "@chakra-ui/react";
import { useNFTsQuery } from "../../clients/nfts";
import { LoadingScreen } from "../../screens/LoadingScreen";
import { ErrorScreen } from "../../screens/ErrorScreen";
import { BoxProps, Grid, Text } from "@chakra-ui/react";

interface NFTsProps extends BoxProps {
address?: string;
address: string;
}

const NFTs = memo(function Achievements({address, ...boxProps}: NFTsProps) {
const NFTs = memo(function Achievements({ address, ...boxProps }: NFTsProps) {
const ownedNFTsQuery = useNFTsQuery(address);
if (!address) {
return <Text size="sm">No Address to Assoicate Achievements</Text>;
Expand Down
19 changes: 14 additions & 5 deletions examples/alchemy-daapp/src/components/profile/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "@chakra-ui/react";
import { memo, useCallback } from "react";
import NFTs from "./NFTs";
import { useAppState } from "../../clients/appState";

const ProfileAttribute = ({
label,
Expand Down Expand Up @@ -52,7 +53,8 @@ const ProfileDetailCard = ({
</VStack>
);

function UnMemoProfilePage({ address }: { address: string }) {
function UnMemoProfilePage() {
const { state, eoaAddress, scwAddress } = useAppState();
const copyAddressTextToClipboard = useCallback((address: string) => {
return async () => {
if ("clipboard" in navigator && address) {
Expand All @@ -63,14 +65,21 @@ function UnMemoProfilePage({ address }: { address: string }) {
};
}, []);

if (state !== "HAS_SCW") {
return null;
}

return (
<HStack gap={5} alignItems="flex-start" padding={25}>
<VStack alignItems="center" gap={5} w="300px">
<Avatar size="2xl" />
<VStack alignItems="start" gap={5}>
<Box cursor="pointer" onClick={copyAddressTextToClipboard(address)}>
<Box
cursor="pointer"
onClick={copyAddressTextToClipboard(eoaAddress!)}
>
<ProfileAttribute
value={`${address?.substring(0, 15)}...`}
value={`${eoaAddress?.substring(0, 15)}...`}
label="Owner Address"
/>
</Box>
Expand All @@ -80,7 +89,7 @@ function UnMemoProfilePage({ address }: { address: string }) {
>
<ProfileAttribute
label="Smart Contract Address"
value={`${address?.substring(0, 15)}...`}
value={`${scwAddress?.substring(0, 15)}...`}
/>
</Box>
</VStack>
Expand All @@ -90,7 +99,7 @@ function UnMemoProfilePage({ address }: { address: string }) {
<Heading size="sm" margin={0} fontWeight="semibold" color="gray.500">
NFTs
</Heading>
<NFTs maxH="225px" overflowY="auto" address={address} />
<NFTs maxH="225px" overflowY="auto" address={eoaAddress} />
</ProfileDetailCard>
</VStack>
</HStack>
Expand Down
17 changes: 11 additions & 6 deletions examples/alchemy-daapp/src/context/Wallet.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import "@rainbow-me/rainbowkit/styles.css";

import { getDefaultWallets, RainbowKitProvider } from "@rainbow-me/rainbowkit";
import {
connectorsForWallets,
RainbowKitProvider,
} from "@rainbow-me/rainbowkit";
import { configureChains, createConfig, WagmiConfig } from "wagmi";
import { polygonMumbai } from "wagmi/chains";
import { jsonRpcProvider } from "wagmi/providers/jsonRpc";
import { injectedWallet } from "@rainbow-me/rainbowkit/wallets";

const { chains, publicClient } = configureChains(
[polygonMumbai],
Expand All @@ -18,11 +22,12 @@ const { chains, publicClient } = configureChains(
]
);

const { connectors } = getDefaultWallets({
appName: "Alchemy DAApp",
projectId: "alchemy-daapp",
chains,
});
const connectors = connectorsForWallets([
{
groupName: "Recommended",
wallets: [injectedWallet({ chains })],
},
]);

const wagmiConfig = createConfig({
autoConnect: true,
Expand Down
14 changes: 0 additions & 14 deletions examples/alchemy-daapp/src/screens/ProfileScreen.tsx

This file was deleted.

22 changes: 20 additions & 2 deletions examples/alchemy-daapp/src/screens/RootScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import ProfileScreen from "./ProfileScreen";
import NavigationBar from "../components/NavigationBar";
import { ChakraProvider } from "@chakra-ui/react";
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient } from "../clients/query";
import { ToastContainer } from "~/utils/toast";
import { WalletContext } from "~/context/Wallet";
import { OnboardingPage } from "../components/onboarding/OnboardingPage";
import { useAppState } from "~/clients/appState";
import { ProfilePage } from "~/components/profile/ProfilePage";
import { ConnectPage } from "~/components/connect/ConnectPage";
import { LoadingScreen } from "./LoadingScreen";

function LandingScreen() {
const { state } = useAppState();
switch (state) {
case "HAS_SCW":
return <ProfilePage />;
case "NO_SCW":
return <OnboardingPage />;
case "UNCONNECTED":
return <ConnectPage />;
case "LOADING":
return <LoadingScreen />;
}
}

export default function RootScreen() {
return (
<QueryClientProvider client={queryClient}>
<ChakraProvider>
<WalletContext>
<NavigationBar />
<ProfileScreen />
<LandingScreen />
</WalletContext>
</ChakraProvider>
<ToastContainer />
Expand Down
7 changes: 7 additions & 0 deletions examples/contracts/DAAppNFT/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[default]
src = 'src'
out = 'out'
libs = ['lib']
remappings = ['ds-test/=lib/ds-test/src/']

# See more config options https://github.com/gakonst/foundry/tree/master/config
5 changes: 5 additions & 0 deletions examples/contracts/DAAppNFT/migrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Version 1

Deployer: 0x15a411507e901f26965f0ebcd3155834b058a6b2
Deployed to: 0xb7b9424ef3d1b9086b7e53276c4aad68a1dd971c
Transaction hash: 0x46f95ea67e2b103f607884a87fd25d53d619eaa7d67437f1d7cb51f7c99b6de2
2 changes: 2 additions & 0 deletions examples/contracts/DAAppNFT/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
openzeppelin-contracts/=lib/openzeppelin-contracts/
forge-std/=lib/forge-std/src/
Loading

0 comments on commit eb3b14f

Please sign in to comment.