diff --git a/.gitignore b/.gitignore index ed3a6878..878779b6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ coverage .next/ out/ build +dist # misc .DS_Store diff --git a/apps/blockchain/.gitignore b/apps/blockchain/.gitignore new file mode 100644 index 00000000..20870856 --- /dev/null +++ b/apps/blockchain/.gitignore @@ -0,0 +1,14 @@ +node_modules +.env +coverage +coverage.json +typechain +typechain-types +temp + +#Hardhat files +cache +artifacts + +deployments +contracts/**/target/**/* diff --git a/apps/web/.env.example b/apps/web/.env.example index c97c71dd..4faa2faa 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -13,6 +13,6 @@ # https://www.prisma.io/docs/reference/database-reference/connection-urls#env # DATABASE_URL="file:./db.sqlite" ALCHEMY_API_KEY= -L2_NETWORK= NEXT_PUBLIC_L1_BRIDGE_ADDRESS= -WALLETCONNECT_PROJECT_ID= \ No newline at end of file +NEXT_PUBLIC_L2_BRIDGE_ADDRESS= +NEXT_PUBLIC_ARK_API_DOMAIN= \ No newline at end of file diff --git a/apps/web/.eslintrc.cjs b/apps/web/.eslintrc.cjs index e4998b6f..9b4ccff8 100644 --- a/apps/web/.eslintrc.cjs +++ b/apps/web/.eslintrc.cjs @@ -6,6 +6,7 @@ const config = { overrides: [ { extends: [ + "plugin:perfectionist/recommended-natural", "plugin:@typescript-eslint/recommended-requiring-type-checking", ], files: ["*.ts", "*.tsx"], diff --git a/apps/web/abi/abi.json b/apps/web/abi/abi.json deleted file mode 100644 index 988f2c1f..00000000 --- a/apps/web/abi/abi.json +++ /dev/null @@ -1,126 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "starknetCore_", "type": "address" } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "inputs": [], - "name": "deposit", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "l1_contract_address", - "type": "address" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "forceWithdraw", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "l2GatewayAddress", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "selector", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "name": "setL2GatewayAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "name": "setSelector", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "starknetCore", - "outputs": [ - { - "internalType": "contract IStarknetMessaging", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "string", "name": "text", "type": "string" }], - "name": "strToUint", - "outputs": [ - { "internalType": "uint256", "name": "res", "type": "uint256" } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/apps/web/app/components/ConnectModal.tsx b/apps/web/app/components/ConnectModal.tsx deleted file mode 100644 index 1e581455..00000000 --- a/apps/web/app/components/ConnectModal.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import { - useAccount as useStarknetAccount, - useConnectors, -} from "@starknet-react/core"; -import { - useConnect, - useDisconnect, - useAccount as useEthereumAccount, -} from "wagmi"; -import { useEffect } from "react"; -import { XMarkIcon } from "@heroicons/react/24/solid"; -import * as Dialog from "@radix-ui/react-dialog"; - -import Image from "next/image"; -import { - CONNECTOR_LABELS_BY_ID, - type Chain, - WALLET_LOGOS_BY_ID, -} from "../helpers"; - -interface ConnectorButtonProps { - id: string; - onClick: () => void; -} - -function ConnectorButton({ id, onClick }: ConnectorButtonProps) { - return ( - - ); -} - -function EthereumConnectorList() { - const { isConnected } = useEthereumAccount(); - const { connect, connectors } = useConnect(); - const { disconnect } = useDisconnect(); - - return ( - <> - {isConnected ? ( - - ) : ( - <> - - Choose your Ethereum wallet - -
- {connectors.map((connector) => { - return ( - connect({ connector })} - id={connector.id} - /> - ); - })} -
- - )} - - ); -} - -function StarknetConnectorList() { - const { isConnected } = useStarknetAccount(); - const { connect, connectors, refresh, disconnect } = useConnectors(); - - useEffect(() => { - const interval = setInterval(refresh, 5000); - return () => clearInterval(interval); - }, [refresh]); - - return ( - <> - {isConnected ? ( - - ) : ( - <> - - Choose your Starknet wallet - -
- {connectors.map((connector) => { - return ( - connect(connector)} - id={connector.id()} - /> - ); - })} -
- - )} - - ); -} - -interface ConnectModalProps { - isOpen: boolean; - onOpenChange: (open: boolean) => void; - chain: Chain; -} - -const CHAIN_TO_CONNECTOR_LIST = { - Ethereum: EthereumConnectorList, - Starknet: StarknetConnectorList, -}; - -// TODO @YohanTz: Handle disconnect / loading states etc once the whole flow is ready -export default function ConnectModal({ - isOpen, - onOpenChange, - chain, -}: ConnectModalProps) { - const ConnectorList = CHAIN_TO_CONNECTOR_LIST[chain]; - - return ( - <> - - -
- {/* TODO @YohanTz: Extract magic values like this somewhere (top-[5.75rem]) */} - event.preventDefault()} - > -
- - - -
- -
- - - - ); -} diff --git a/apps/web/app/components/Footer.tsx b/apps/web/app/components/Footer.tsx deleted file mode 100644 index f9ac466d..00000000 --- a/apps/web/app/components/Footer.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import Image from 'next/image'; - -export default function Header() { - return ( -
- - Built by{' '} - Screenshot Logo - -
- ); -} diff --git a/apps/web/app/components/Header.tsx b/apps/web/app/components/Header.tsx deleted file mode 100644 index a31a7041..00000000 --- a/apps/web/app/components/Header.tsx +++ /dev/null @@ -1,38 +0,0 @@ -"use client"; - -import ConnectStarkNetButton from "./ConnectStarkNetButton"; -import ConnectEthereumButton from "./ConnectEthereumButton"; -import { useState } from "react"; -import { type Chain } from "../helpers"; - -export default function Header() { - const [openedModal, setOpenedModal] = useState(undefined); - - function openModal(chain: Chain) { - setOpenedModal(chain); - } - - function closeModal() { - setOpenedModal(undefined); - } - - return ( -
-
Starklane
-
- { - open ? openModal("Ethereum") : closeModal(); - }} - /> - { - open ? openModal("Starknet") : closeModal(); - }} - /> -
-
- ); -} diff --git a/apps/web/app/components/NftCard.tsx b/apps/web/app/components/NftCard.tsx deleted file mode 100644 index b7738de0..00000000 --- a/apps/web/app/components/NftCard.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import Image from "next/image"; - -type NftCardProps = { - image?: string; - isSelected: boolean; - onClick: () => void; - title: string; -} & ( - | { - cardType: "collection"; - numberOfNfts: number; - } - | { cardType: "nft"; numberOfNfts?: never } -); - -export default function NftCard({ - cardType, - image, - isSelected, - numberOfNfts, - onClick, - title, -}: NftCardProps) { - return ( -
- {cardType === "collection" && ( - <> -
-
- - )} - {/* TODO @YohanTz: handle focus visible style properly */} - -
- ); -} diff --git a/apps/web/app/components/NftTransferDrawer.tsx b/apps/web/app/components/NftTransferDrawer.tsx deleted file mode 100644 index 26272d78..00000000 --- a/apps/web/app/components/NftTransferDrawer.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import Image from "next/image"; -import { useAccount as useEthereumAccount } from "wagmi"; -import { useAccount as useStarknetAccount } from "@starknet-react/core"; - -import { api } from "~/utils/api"; -import { - CHAIN_LOGOS_BY_NAME, - WALLET_LOGOS_BY_ID, - type Chain, - CONNECTOR_LABELS_BY_ID, -} from "../helpers"; -import TargetChainButton from "./TargetChainButton"; -import { useMemo } from "react"; -import { useIsSSR } from "~/hooks/useIsSSR"; - -interface ChainTransferSummaryProps { - chain: Chain; - connectorId?: string; - shortAddress: string; - targetChain: Chain; -} - -function ChainTransferSummary({ - chain, - connectorId, - shortAddress, - targetChain, -}: ChainTransferSummaryProps) { - const isTargetChain = chain === targetChain; - const isSSR = useIsSSR(); - - return ( -
-
- {`${chain} -
- - {isTargetChain ? "To" : "From"} - -
{chain}
-
-
- {!isSSR && connectorId !== undefined && ( -
- {`${ -
- - Wallet - -
{shortAddress}
-
-
- )} -
- ); -} - -interface NftTansferDrawerProps { - selectedNftIds: Array; - targetChain: Chain; -} - -export default function NftTransferDrawer({ - selectedNftIds, - targetChain, -}: NftTansferDrawerProps) { - const { address: ethereumAddress, connector: ethereumConnector } = - useEthereumAccount(); - const { address: starknetAddress, connector: starknetConnector } = - useStarknetAccount(); - - // TODO @YohanTz: Support both sides - const { data: nfts } = api.nfts.getL1NftsByCollection.useQuery( - { - address: ethereumAddress ?? "", - }, - { enabled: ethereumAddress !== undefined } - ); - - const shortEthereumAddress = useMemo( - () => - ethereumAddress - ? `${ethereumAddress.slice(0, 6)}...${ethereumAddress.slice(-4)}` - : "", - [ethereumAddress] - ); - - const shortStarknetAddress = useMemo( - () => - starknetAddress - ? `${starknetAddress.slice(0, 6)}...${starknetAddress.slice(-4)}` - : "", - [starknetAddress] - ); - - const selectedNfts = selectedNftIds.map((selectedNftId) => - nfts?.raw.find((nft) => nft.id === selectedNftId) - ); - - return ( -
- {/* TODO @YohanTz: Extract magic values like this somewhere (top-[5.75rem]) */} -
-

Your assets to transfer

-

- You need to confirm the transaction in your wallet to start the - migration. -

-
- - - -
- {/* TODO @YohanTz: Always show scroll bar to indicate that there is more content to view (with Radix ScrollArea ?) */} -
- {selectedNfts.map((selectedNft) => { - return ( -
- {selectedNft?.title -
- {selectedNft?.collectionName} - {selectedNft?.title} -
-
- ); - })} -
-
-

Total Nfts to migrate

- - {selectedNfts.length}/{selectedNfts.length} - -
-

- You must approve the selection of your assets before confirming the - migration. Each collection will require a signature via your wallet. -

- -
-
- ); -} diff --git a/apps/web/app/components/TargetChainButton.tsx b/apps/web/app/components/TargetChainButton.tsx deleted file mode 100644 index ce978e9e..00000000 --- a/apps/web/app/components/TargetChainButton.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useLocalStorage } from "usehooks-ts"; -import { type Chain } from "../helpers"; - -interface TargetChainButtonProps { - orientation: "horizontal" | "vertical"; -} - -export default function TargetChainButton({ - orientation, -}: TargetChainButtonProps) { - const [targetChain, setTargetChain] = useLocalStorage( - "chain", - "Ethereum" - ); - - function toggle() { - setTargetChain(targetChain === "Ethereum" ? "Starknet" : "Ethereum"); - } - - return ( -
- -
- ); -} diff --git a/apps/web/app/components/TargetChainSwitch.tsx b/apps/web/app/components/TargetChainSwitch.tsx deleted file mode 100644 index 57da89a5..00000000 --- a/apps/web/app/components/TargetChainSwitch.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import Image from "next/image"; -import { CHAIN_LOGOS_BY_NAME, type Chain } from "../helpers"; -import { useIsSSR } from "~/hooks/useIsSSR"; -import TargetChainButton from "./TargetChainButton"; - -type TargetChainSwitch = { - targetChain: Chain; - setTargetChain: (chain: Chain) => void; -}; - -export default function TargetChainSwitch({ - targetChain, - setTargetChain, -}: TargetChainSwitch) { - const isSSR = useIsSSR(); - - return ( -
- - - -
- ); -} diff --git a/apps/web/app/components/TokenList.tsx b/apps/web/app/components/TokenList.tsx deleted file mode 100644 index 64d748b7..00000000 --- a/apps/web/app/components/TokenList.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import * as Tabs from "@radix-ui/react-tabs"; -import { useAccount } from "wagmi"; -import { api } from "~/utils/api"; - -import { useState } from "react"; -import NftCard from "./NftCard"; -import { type Nft } from "~/server/api/routers/nfts"; - -interface TokenListProps { - selectedNftIds: Array; - setSelectedNftIds: (nfts: Array) => void; -} - -const tabs = [ - { label: "All nfts", id: "all" }, - { label: "Collections", id: "collections" }, -] as const; - -// TODO @YohanTz: Take time to optimize the lists with React.memo etc. -export default function TokenList({ - selectedNftIds, - setSelectedNftIds, -}: TokenListProps) { - const [activeTab, setActiveTab] = useState(tabs[0].id); - const { address: ethereumAddress } = useAccount(); - - const { data: nfts } = api.nfts.getL1NftsByCollection.useQuery( - { - address: ethereumAddress ?? "", - }, - { enabled: ethereumAddress !== undefined } - ); - - function handleNftClick(nftId: string) { - if (selectedNftIds.includes(nftId)) { - setSelectedNftIds( - selectedNftIds.filter((selectedNftId) => selectedNftId !== nftId) - ); - return; - } - setSelectedNftIds([...selectedNftIds, nftId]); - } - - function handleCollectionClick( - collectionNfts: Array, - isSelected: boolean - ) { - if (nfts === undefined) { - return; - } - - let selectedNftIdsCopy = [...selectedNftIds]; - - if (isSelected) { - selectedNftIdsCopy = selectedNftIds.filter((nftId) => { - return !collectionNfts?.some((nft) => nftId === nft.id); - }); - } else { - collectionNfts?.forEach((nft) => { - if (!selectedNftIds.includes(nft.id)) { - selectedNftIdsCopy.push(nft.id); - } - }); - } - - setSelectedNftIds(selectedNftIdsCopy); - } - - function selectAll() { - if (nfts === undefined) { - return; - } - setSelectedNftIds(nfts.raw.map((nft) => nft.id)); - } - - return ( - <> - {/* TODO @YohanTz: Export Tabs logic to design system package ? */} - - -
- {tabs.map((tab) => { - return ( - - {tab.label} - - ); - })} -
- -
- - {nfts && - Object.values(nfts.raw).map((nft) => { - const isSelected = selectedNftIds.includes(nft.id); - - return ( - handleNftClick(nft.id)} - title={nft.title} - /> - ); - })} - - - {nfts && - Object.entries(nfts.byCollection).map(([collectionName, nfts]) => { - const isSelected = nfts.every((nft) => - selectedNftIds.includes(nft.id) - ); - - return ( - handleCollectionClick(nfts, isSelected)} - title={collectionName} - /> - ); - })} - -
- - ); -} diff --git a/apps/web/app/head.tsx b/apps/web/app/head.tsx deleted file mode 100644 index 035ac636..00000000 --- a/apps/web/app/head.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export default function Head() { - return ( - <> - - - Starklane - - - ); -} diff --git a/apps/web/app/helpers.ts b/apps/web/app/helpers.ts deleted file mode 100644 index 1437e169..00000000 --- a/apps/web/app/helpers.ts +++ /dev/null @@ -1,29 +0,0 @@ -import argentXLogo from "~/public/argentX_logo.png"; -import braavosLogo from "~/public/braavos_logo.png"; -import ethereumLogo from "~/public/ethereum_logo.png"; -import metaMaskLogo from "~/public/metamask_logo.png"; -import starknetLogo from "~/public/starknet_logo.png"; -import { type StaticImageData } from "next/image"; - -export type Chain = "Ethereum" | "Starknet"; - -export const DEFAULT_ETHEREUM_CONNECTOR_LOGO = metaMaskLogo; -export const DEFAULT_STARKNET_CONNECTOR_LOGO = argentXLogo; - -export const WALLET_LOGOS_BY_ID: Record = { - braavos: braavosLogo, - argentX: argentXLogo, - injected: metaMaskLogo, -}; - -export const CHAIN_LOGOS_BY_NAME: Record = { - Ethereum: ethereumLogo, - Starknet: starknetLogo, -}; - -// TODO @YohanTz: An injected connector may not be Metamask -export const CONNECTOR_LABELS_BY_ID: Record = { - braavos: "Braavos", - argentX: "Argent X", - injected: "Metamask", -}; diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx deleted file mode 100644 index e55a80f0..00000000 --- a/apps/web/app/layout.tsx +++ /dev/null @@ -1,51 +0,0 @@ -"use client"; - -import "~/styles/globals.css"; - -import { WagmiConfig, configureChains, createConfig } from "wagmi"; -import { goerli } from "wagmi/chains"; -import { InjectedConnector, StarknetConfig } from "@starknet-react/core"; -import { publicProvider } from "wagmi/providers/public"; - -import Footer from "./components/Footer"; -import Header from "./components/Header"; -import { api } from "~/utils/api"; - -// TODO @YohanTz: Handle wallet connect and coinbase wallet connectors -// const alchemyId = process.env.ALCHEMY_ID; -// const walletConnectProjectId = process.env.WALLETCONNECT_PROJECT_ID ?? ""; - -const { publicClient, webSocketPublicClient } = configureChains( - [goerli], - [publicProvider()] -); - -const wagmiConfig = createConfig({ - autoConnect: true, - publicClient, - webSocketPublicClient, -}); - -const starknetConnectors = [ - new InjectedConnector({ options: { id: "braavos" } }), - new InjectedConnector({ options: { id: "argentX" } }), -]; - -function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - - - - -
- {children} -