From 72cc8f72e07967cca209ecf6de7c158a98569eb5 Mon Sep 17 00:00:00 2001 From: Eduardo <43045722+edulanasca@users.noreply.github.com> Date: Sun, 7 Apr 2024 03:37:51 -0500 Subject: [PATCH 1/7] Wallet improvement (#29) * update: button improvement * update: burner wallet * fix: types * add mainnet sample contract --------- Co-authored-by: Eduardo Co-authored-by: 0xquantum3labs --- packages/nextjs/app/page.tsx | 6 - packages/nextjs/components/Header.tsx | 6 +- .../ScaffoldStarkAppWithProviders.tsx | 41 +- .../scaffold-stark/BlockieAvatar.tsx | 22 + .../AddressInfoDropdown.tsx | 135 +++++ .../AddressQRCodeModal.tsx | 33 ++ .../CustomConnectButton}/ConnectModal.tsx | 8 +- .../CustomConnectButton}/GenericModal.tsx | 0 .../CustomConnectButton/NetworkOptions.tsx | 47 ++ .../WrongNetworkDropdown.tsx | 32 ++ .../CustomConnectButton/index.tsx | 64 +++ .../components/scaffold-stark/index.tsx | 1 + .../components/wallet/WalletConnect.tsx | 85 --- .../nextjs/contracts/deployedContracts.ts | 526 +++++++++++++++++- packages/nextjs/hooks/scaffold-stark/index.ts | 2 + .../hooks/scaffold-stark/useAutoConnect.ts | 21 + .../hooks/scaffold-stark/useSwitchNetwork.ts | 14 + .../hooks/scaffold-stark/useTargetNetwork.ts | 8 +- packages/nextjs/package.json | 2 + packages/nextjs/scaffold.config.ts | 10 + .../web3/stark-burner/BurnerConnector.ts | 73 +++ .../stark-burner/BurnerConnectorErrors.ts | 23 + yarn.lock | 18 + 23 files changed, 1052 insertions(+), 125 deletions(-) create mode 100644 packages/nextjs/components/scaffold-stark/BlockieAvatar.tsx create mode 100644 packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx create mode 100644 packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressQRCodeModal.tsx rename packages/nextjs/components/{wallet => scaffold-stark/CustomConnectButton}/ConnectModal.tsx (96%) rename packages/nextjs/components/{ => scaffold-stark/CustomConnectButton}/GenericModal.tsx (100%) create mode 100644 packages/nextjs/components/scaffold-stark/CustomConnectButton/NetworkOptions.tsx create mode 100644 packages/nextjs/components/scaffold-stark/CustomConnectButton/WrongNetworkDropdown.tsx create mode 100644 packages/nextjs/components/scaffold-stark/CustomConnectButton/index.tsx delete mode 100644 packages/nextjs/components/wallet/WalletConnect.tsx create mode 100644 packages/nextjs/hooks/scaffold-stark/useAutoConnect.ts create mode 100644 packages/nextjs/hooks/scaffold-stark/useSwitchNetwork.ts create mode 100644 packages/nextjs/services/web3/stark-burner/BurnerConnector.ts create mode 100644 packages/nextjs/services/web3/stark-burner/BurnerConnectorErrors.ts diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index 602daf9f..107066a3 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -3,12 +3,6 @@ import Link from "next/link"; import type { NextPage } from "next"; import { BugAntIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; -import { useScaffoldContract } from "~~/hooks/scaffold-stark/useScaffoldContract"; -import { useScaffoldContractRead } from "~~/hooks/scaffold-stark/useScaffoldContractRead"; -import { useScaffoldContractWrite } from "~~/hooks/scaffold-stark/useScaffoldContractWrite"; -import { Button } from "@radix-ui/themes"; -import ConnectModal from "~~/components/wallet/ConnectModal"; -import { useAccount } from "@starknet-react/core"; // import { Address } from "~~/components/scaffold-eth"; const Home: NextPage = () => { diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx index 9abecc72..66007487 100644 --- a/packages/nextjs/components/Header.tsx +++ b/packages/nextjs/components/Header.tsx @@ -4,13 +4,13 @@ import React, { useCallback, useRef, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; -import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline"; +import {Bars3Icon, BugAntIcon} from "@heroicons/react/24/outline"; // import { // FaucetButton, // RainbowKitCustomConnectButton, // } from "~~/components/scaffold-eth"; import { useOutsideClick } from "~~/hooks/scaffold-stark"; -import WalletConnect from "~~/components/wallet/WalletConnect"; +import {CustomConnectButton} from "~~/components/scaffold-stark/CustomConnectButton"; type HeaderMenuLink = { label: string; @@ -117,7 +117,7 @@ export const Header = () => {
- +
); diff --git a/packages/nextjs/components/ScaffoldStarkAppWithProviders.tsx b/packages/nextjs/components/ScaffoldStarkAppWithProviders.tsx index 5fd4a770..de30fde0 100644 --- a/packages/nextjs/components/ScaffoldStarkAppWithProviders.tsx +++ b/packages/nextjs/components/ScaffoldStarkAppWithProviders.tsx @@ -1,42 +1,39 @@ "use client"; import React from "react"; -import { useEffect, useState } from "react"; -import { useTheme } from "next-themes"; -import { sepolia, mainnet, goerli, devnet } from "@starknet-react/chains"; -import { Toaster } from "react-hot-toast"; +import {useEffect, useState} from "react"; +import {useTheme} from "next-themes"; +import {Toaster} from "react-hot-toast"; import { StarknetConfig, publicProvider, argent, braavos, useInjectedConnectors, - voyager, starkscan, } from "@starknet-react/core"; -import { Header } from "~~/components/Header"; -import { Footer } from "~~/components/Footer"; -import { ProgressBar } from "~~/components/scaffold-stark/ProgressBar"; -import { appChains } from "~~/services/web3/connectors"; +import {Header} from "~~/components/Header"; +import {Footer} from "~~/components/Footer"; +import {ProgressBar} from "~~/components/scaffold-stark/ProgressBar"; +import {appChains} from "~~/services/web3/connectors"; +import {BurnerConnector} from "~~/services/web3/stark-burner/BurnerConnector"; -const ScaffoldStarkApp = ({ children }: { children: React.ReactNode }) => { +const ScaffoldStarkApp = ({children}: { children: React.ReactNode }) => { return ( <>
-
+
{children}
-
+
- + ); }; -export const ScaffoldStarkAppWithProviders = ({ - children, -}: { +export const ScaffoldStarkAppWithProviders = ({children,}: { children: React.ReactNode; }) => { - const { resolvedTheme } = useTheme(); + const {resolvedTheme} = useTheme(); const isDarkMode = resolvedTheme === "dark"; const [mounted, setMounted] = useState(false); @@ -44,13 +41,9 @@ export const ScaffoldStarkAppWithProviders = ({ setMounted(true); }, []); - const { connectors } = useInjectedConnectors({ + const {connectors} = useInjectedConnectors({ // Show these connectors if the user has no connector installed. - recommended: [argent(), braavos()], - // Hide recommended connectors if the user has any connector installed. - includeRecommended: "onlyIfNoConnectors", - // Randomize the order of the connectors. - order: "random", + recommended: [argent(), braavos(), new BurnerConnector()], }); return ( @@ -60,7 +53,7 @@ export const ScaffoldStarkAppWithProviders = ({ connectors={connectors} explorer={starkscan} > - + {children} ); diff --git a/packages/nextjs/components/scaffold-stark/BlockieAvatar.tsx b/packages/nextjs/components/scaffold-stark/BlockieAvatar.tsx new file mode 100644 index 00000000..cc701877 --- /dev/null +++ b/packages/nextjs/components/scaffold-stark/BlockieAvatar.tsx @@ -0,0 +1,22 @@ +"use client"; + +import { blo } from "blo"; + +interface BlockieAvatarProps { + address: string; + ensImage?: string; + size: number; +} + +// Custom Avatar for RainbowKit +export const BlockieAvatar = ({ address, ensImage, size }: BlockieAvatarProps) => ( + // Don't want to use nextJS Image here (and adding remote patterns for the URL) + // eslint-disable-next-line @next/next/no-img-element + {`${address} +); diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx new file mode 100644 index 00000000..b8490dbb --- /dev/null +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressInfoDropdown.tsx @@ -0,0 +1,135 @@ +import { useRef, useState } from "react"; +import { NetworkOptions } from "./NetworkOptions"; +import CopyToClipboard from "react-copy-to-clipboard"; +import { + ArrowLeftEndOnRectangleIcon, + ArrowTopRightOnSquareIcon, + ArrowsRightLeftIcon, + CheckCircleIcon, + ChevronDownIcon, + DocumentDuplicateIcon, + QrCodeIcon, +} from "@heroicons/react/24/outline"; +import { BlockieAvatar, isENS } from "~~/components/scaffold-stark"; +import { useOutsideClick } from "~~/hooks/scaffold-stark"; +import { getTargetNetworks } from "~~/utils/scaffold-stark"; +import {Address} from "@starknet-react/chains"; +import {useDisconnect} from "@starknet-react/core"; + +const allowedNetworks = getTargetNetworks(); + +type AddressInfoDropdownProps = { + address: Address; + blockExplorerAddressLink: string | undefined; + displayName: string; + ensAvatar?: string; +}; + +export const AddressInfoDropdown = ({ + address, + ensAvatar, + displayName, + blockExplorerAddressLink, +}: AddressInfoDropdownProps) => { + const { disconnect } = useDisconnect(); + + const [addressCopied, setAddressCopied] = useState(false); + + const [selectingNetwork, setSelectingNetwork] = useState(false); + const dropdownRef = useRef(null); + const closeDropdown = () => { + setSelectingNetwork(false); + dropdownRef.current?.removeAttribute("open"); + }; + useOutsideClick(dropdownRef, closeDropdown); + + return ( + <> +
+ + + + {isENS(displayName) ? displayName : address?.slice(0, 6) + "..." + address?.slice(-4)} + + + +
    +
+
+ + ); +}; diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressQRCodeModal.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressQRCodeModal.tsx new file mode 100644 index 00000000..a7a082a0 --- /dev/null +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/AddressQRCodeModal.tsx @@ -0,0 +1,33 @@ +import { QRCodeSVG } from "qrcode.react"; +import { Address as AddressType } from "@starknet-react/chains"; +import { Address } from "~~/components/scaffold-stark"; + +type AddressQRCodeModalProps = { + address: AddressType; + modalId: string; +}; + +export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps) => { + return ( + <> +
+ + +
+ + ); +}; diff --git a/packages/nextjs/components/wallet/ConnectModal.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/ConnectModal.tsx similarity index 96% rename from packages/nextjs/components/wallet/ConnectModal.tsx rename to packages/nextjs/components/scaffold-stark/CustomConnectButton/ConnectModal.tsx index f2ce2d6f..7fb67701 100644 --- a/packages/nextjs/components/wallet/ConnectModal.tsx +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/ConnectModal.tsx @@ -1,7 +1,8 @@ import Image from "next/image"; -import GenericModal from "../GenericModal"; +import GenericModal from "./GenericModal"; import { Connector, useConnect } from "@starknet-react/core"; import { useEffect, useState } from "react"; +import {useLocalStorage} from "usehooks-ts"; type Props = { isOpen: boolean; @@ -27,11 +28,14 @@ const Wallet = ({ }) => { const { connect } = useConnect(); const isSvg = src?.startsWith("): void { connect({ connector }); closeModal(e); - localStorage.setItem("lastUsedConnector", connector.name); + setLastConnector(connector.name); } return ( diff --git a/packages/nextjs/components/GenericModal.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/GenericModal.tsx similarity index 100% rename from packages/nextjs/components/GenericModal.tsx rename to packages/nextjs/components/scaffold-stark/CustomConnectButton/GenericModal.tsx diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/NetworkOptions.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/NetworkOptions.tsx new file mode 100644 index 00000000..92ff65e8 --- /dev/null +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/NetworkOptions.tsx @@ -0,0 +1,47 @@ +import {useTheme} from "next-themes"; +import {ArrowsRightLeftIcon} from "@heroicons/react/24/solid"; +import { + getNetworkColor, useSwitchNetwork, +} from "~~/hooks/scaffold-stark"; +import {getTargetNetworks} from "~~/utils/scaffold-stark"; +import {useAccount} from "@starknet-react/core"; + +type NetworkOptionsProps = { + hidden?: boolean; +}; + +export const NetworkOptions = ({hidden = false}: NetworkOptionsProps) => { + const {switchNetwork} = useSwitchNetwork(); + const {chainId} = useAccount(); + const {resolvedTheme} = useTheme(); + const isDarkMode = resolvedTheme === "dark"; + const allowedNetworks = getTargetNetworks(); + + return ( + <> + {allowedNetworks + .filter(allowedNetwork => allowedNetwork.id !== chainId) + .map(allowedNetwork => ( +
  • + +
  • + ))} + + ); +}; diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/WrongNetworkDropdown.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/WrongNetworkDropdown.tsx new file mode 100644 index 00000000..705cfb6d --- /dev/null +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/WrongNetworkDropdown.tsx @@ -0,0 +1,32 @@ +import { NetworkOptions } from "./NetworkOptions"; +import { ArrowLeftEndOnRectangleIcon, ChevronDownIcon } from "@heroicons/react/24/outline"; +import {useDisconnect} from "@starknet-react/core"; + +export const WrongNetworkDropdown = () => { + const { disconnect } = useDisconnect(); + + return ( +
    + +
      + +
    • + +
    • +
    +
    + ); +}; diff --git a/packages/nextjs/components/scaffold-stark/CustomConnectButton/index.tsx b/packages/nextjs/components/scaffold-stark/CustomConnectButton/index.tsx new file mode 100644 index 00000000..6c1b107e --- /dev/null +++ b/packages/nextjs/components/scaffold-stark/CustomConnectButton/index.tsx @@ -0,0 +1,64 @@ +"use client"; + +// @refresh reset +import {Balance} from "../Balance"; +import {AddressInfoDropdown} from "./AddressInfoDropdown"; +import {AddressQRCodeModal} from "./AddressQRCodeModal"; +import {WrongNetworkDropdown} from "./WrongNetworkDropdown"; +import {useAutoConnect, useNetworkColor} from "~~/hooks/scaffold-stark"; +import {useTargetNetwork} from "~~/hooks/scaffold-stark/useTargetNetwork"; +import {getBlockExplorerAddressLink} from "~~/utils/scaffold-stark"; +import {useAccount, useNetwork} from "@starknet-react/core"; +import {Address} from "@starknet-react/chains"; +import {useState} from "react"; +import ConnectModal from "./ConnectModal"; + +/** + * Custom Connect Button (watch balance + custom design) + */ +export const CustomConnectButton = () => { + useAutoConnect(); + const networkColor = useNetworkColor(); + const {targetNetwork} = useTargetNetwork(); + const {address, status, chainId, ...props} = useAccount(); + const {chain} = useNetwork(); + const [modalOpen, setModalOpen] = useState(false); + + const blockExplorerAddressLink = address + ? getBlockExplorerAddressLink(targetNetwork, address) + : undefined; + + const handleWalletConnect = () => { + setModalOpen(true); + } + + const handleModalClose = () => { + setModalOpen(false); + } + + return ( + status == "disconnected" ? + <> + + + + : chainId !== targetNetwork.id ? + + : + <> +
    + + {chain.name} +
    + + + + ); +}; diff --git a/packages/nextjs/components/scaffold-stark/index.tsx b/packages/nextjs/components/scaffold-stark/index.tsx index 4d416d81..130d2ba6 100644 --- a/packages/nextjs/components/scaffold-stark/index.tsx +++ b/packages/nextjs/components/scaffold-stark/index.tsx @@ -1,3 +1,4 @@ export * from "./Address"; export * from "./Balance"; export * from "./Input"; +export * from "./BlockieAvatar"; \ No newline at end of file diff --git a/packages/nextjs/components/wallet/WalletConnect.tsx b/packages/nextjs/components/wallet/WalletConnect.tsx deleted file mode 100644 index 56555b4d..00000000 --- a/packages/nextjs/components/wallet/WalletConnect.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import {useAccount, useDisconnect} from "@starknet-react/core"; -import {useEffect, useRef, useState} from "react"; -import ConnectModal from "~~/components/wallet/ConnectModal"; - -const WalletConnect = () => { - const {address: connectedAddress} = useAccount(); - const {disconnect} = useDisconnect(); - const [isMenuVisible, setIsMenuVisible] = useState(false); - const [modalOpen, setModalOpen] = useState(false); - - const menuRef = useRef(null); - - const toggleMenu = () => { - setIsMenuVisible(!isMenuVisible); - }; - - const handleCopyAddress = async () => { - try { - await navigator.clipboard.writeText(connectedAddress!); - setIsMenuVisible(false); - } catch (err) { - console.error('Failed to copy: ', err); - } - }; - - const handleClickOutside = (event: MouseEvent | Event) => { - if (menuRef.current && !menuRef.current.contains(event.target as Node)) { - setIsMenuVisible(false); - } - }; - - const handleDisconnect = () => { - disconnect(); - setIsMenuVisible(false); - } - - const handleWalletConnect = () => { - if (connectedAddress) { - toggleMenu(); - } else { - setModalOpen(true); - } - } - - const handleModalClose = () => { - setModalOpen(false); - } - - useEffect(() => { - document.addEventListener("click", handleClickOutside, true); - return () => { - document.removeEventListener("click", handleClickOutside, true); - }; - }, []); - - return ( -
    - - - {isMenuVisible && ( -
    -
      -
    • Copy Address
    • -
    • Disconnect
    • -
    -
    - )} -
    - ) -} - -export default WalletConnect; \ No newline at end of file diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 25751cee..22e75953 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -3,6 +3,530 @@ * You should not edit it manually or your changes might be overwritten. */ -const deployedContracts = {} as const; +const deployedContracts = { + devnet: { + HelloStarknet: { + address: + "0x0739258a1891b30fbee34ba0b984acc3f55bc19f63e99c96b1a88606383dd322", + abi: [ + { + type: "impl", + name: "HelloStarknetImpl", + interface_name: "contracts::helloStarknet::IHelloStarknet", + }, + { + type: "interface", + name: "contracts::helloStarknet::IHelloStarknet", + items: [ + { + type: "function", + name: "increase_balance", + inputs: [ + { + name: "amount", + type: "core::integer::u32", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_balance", + inputs: [], + outputs: [ + { + type: "core::integer::u32", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "get_balance_increment", + inputs: [ + { + name: "amount", + type: "core::integer::u32", + }, + ], + outputs: [ + { + type: "core::integer::u32", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "event", + name: "contracts::helloStarknet::HelloStarknet::Event", + kind: "enum", + variants: [], + }, + ], + }, + }, + mainnet: { + HelloStarknet: { + address: + "0x0750aa622d77436d69d58c91dbc73a6d0d25db0b7076c1377cdda15e13fd632b", + abi: [ + { + type: "impl", + name: "HelloStarknetImpl", + interface_name: "contracts::helloStarknet::IHelloStarknet", + }, + { + type: "interface", + name: "contracts::helloStarknet::IHelloStarknet", + items: [ + { + type: "function", + name: "increase_balance", + inputs: [ + { + name: "amount", + type: "core::integer::u32", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_balance", + inputs: [], + outputs: [ + { + type: "core::integer::u32", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "get_balance_increment", + inputs: [ + { + name: "amount", + type: "core::integer::u32", + }, + ], + outputs: [ + { + type: "core::integer::u32", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "event", + name: "contracts::helloStarknet::HelloStarknet::Event", + kind: "enum", + variants: [], + }, + ], + }, + }, + sepolia: { + HelloStarknet: { + address: + "0x009329408104badc53c6338ba88966a565bf97268a9ac43114f395114c4b4b91", + abi: [ + { + type: "impl", + name: "HelloStarknetImpl", + interface_name: "contracts::helloStarknet::IHelloStarknet", + }, + { + type: "interface", + name: "contracts::helloStarknet::IHelloStarknet", + items: [ + { + type: "function", + name: "increase_balance", + inputs: [ + { + name: "amount", + type: "core::integer::u32", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_balance", + inputs: [], + outputs: [ + { + type: "core::integer::u32", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "get_balance_increment", + inputs: [ + { + name: "amount", + type: "core::integer::u32", + }, + ], + outputs: [ + { + type: "core::integer::u32", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "event", + name: "contracts::helloStarknet::HelloStarknet::Event", + kind: "enum", + variants: [], + }, + ], + }, + SimpleStorage: { + address: + "0x002c20a4ee6eee7c976e3e71c4217871c7c629c5b3d8b2d03ca151512894f5af", + abi: [ + { + type: "impl", + name: "SimpleStorageImpl", + interface_name: "contracts::simpleStorage::ISimpleStorage", + }, + { + type: "interface", + name: "contracts::simpleStorage::ISimpleStorage", + items: [ + { + type: "function", + name: "get_name", + inputs: [], + outputs: [ + { + type: "core::felt252", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "set_name", + inputs: [ + { + name: "name", + type: "core::felt252", + }, + ], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "name", + type: "core::felt252", + }, + ], + }, + { + type: "event", + name: "contracts::simpleStorage::SimpleStorage::Event", + kind: "enum", + variants: [], + }, + ], + }, + Vote: { + address: + "0x04c394c4fc87a9a62f04205ac456f6811b6bcb0465e339f95308332cbff7364f", + abi: [ + { + type: "impl", + name: "VoteImpl", + interface_name: "contracts::vote::VoteTrait", + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "contracts::vote::VoteTrait", + items: [ + { + type: "function", + name: "get_vote_status", + inputs: [], + outputs: [ + { + type: "(core::integer::u8, core::integer::u8, core::integer::u8, core::integer::u8)", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "voter_can_vote", + inputs: [ + { + name: "user_address", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "is_voter_registered", + inputs: [ + { + name: "address", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "vote", + inputs: [ + { + name: "vote", + type: "core::integer::u8", + }, + ], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "voter_1", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "voter_2", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "voter_3", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "contracts::vote::Vote::VoteCast", + kind: "struct", + members: [ + { + name: "voter", + type: "core::starknet::contract_address::ContractAddress", + kind: "data", + }, + { + name: "vote", + type: "core::integer::u8", + kind: "data", + }, + ], + }, + { + type: "event", + name: "contracts::vote::Vote::UnauthorizedAttempt", + kind: "struct", + members: [ + { + name: "unauthorized_address", + type: "core::starknet::contract_address::ContractAddress", + kind: "data", + }, + ], + }, + { + type: "event", + name: "contracts::vote::Vote::Event", + kind: "enum", + variants: [ + { + name: "VoteCast", + type: "contracts::vote::Vote::VoteCast", + kind: "nested", + }, + { + name: "UnauthorizedAttempt", + type: "contracts::vote::Vote::UnauthorizedAttempt", + kind: "nested", + }, + ], + }, + ], + }, + Ownable: { + address: + "0x07b0a115eb4111e34607418ff37015e8f60149541d3af39b094d19eea2ec5ad4", + abi: [ + { + type: "impl", + name: "OwnableDataImpl", + interface_name: "contracts::IData", + }, + { + type: "interface", + name: "contracts::IData", + items: [ + { + type: "function", + name: "get_data", + inputs: [], + outputs: [ + { + type: "core::integer::u64", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "set_data", + inputs: [ + { + name: "new_value", + type: "core::integer::u64", + }, + ], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "contracts::IOwnable", + }, + { + type: "interface", + name: "contracts::IOwnable", + items: [ + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "initial_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "contracts::ownable_component::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "data", + }, + ], + }, + { + type: "event", + name: "contracts::ownable_component::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "contracts::ownable_component::OwnershipTransferred", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::Ownable::Event", + kind: "enum", + variants: [ + { + name: "OwnableEvent", + type: "contracts::ownable_component::Event", + kind: "nested", + }, + ], + }, + ], + }, + }, +} as const; export default deployedContracts; diff --git a/packages/nextjs/hooks/scaffold-stark/index.ts b/packages/nextjs/hooks/scaffold-stark/index.ts index a2ae3395..d30859c5 100644 --- a/packages/nextjs/hooks/scaffold-stark/index.ts +++ b/packages/nextjs/hooks/scaffold-stark/index.ts @@ -3,3 +3,5 @@ export * from "./useDeployedContractInfo"; export * from "./useNetworkColor"; export * from "./useAnimationConfig"; export * from "./useTransactor"; +export * from "./useAutoConnect"; +export * from "./useSwitchNetwork"; \ No newline at end of file diff --git a/packages/nextjs/hooks/scaffold-stark/useAutoConnect.ts b/packages/nextjs/hooks/scaffold-stark/useAutoConnect.ts new file mode 100644 index 00000000..a154d483 --- /dev/null +++ b/packages/nextjs/hooks/scaffold-stark/useAutoConnect.ts @@ -0,0 +1,21 @@ +import {useReadLocalStorage} from "usehooks-ts"; +import {useEffect} from "react"; +import {useConnect} from "@starknet-react/core"; +import scaffoldConfig from "~~/scaffold.config"; + +/** + * Automatically connect to a wallet/connector based on config and prior wallet + */ +export const useAutoConnect = (): void => { + const wagmiWalletValue = useReadLocalStorage("lastUsedConnector"); + const {connect, connectors} = useConnect(); + + useEffect(() => { + if (scaffoldConfig.walletAutoConnect) { + const connector = connectors.find(conn => conn.name == wagmiWalletValue); + if (connector) { + connect({connector}); + } + } + }, [wagmiWalletValue, connectors, connect]); +}; diff --git a/packages/nextjs/hooks/scaffold-stark/useSwitchNetwork.ts b/packages/nextjs/hooks/scaffold-stark/useSwitchNetwork.ts new file mode 100644 index 00000000..9fdeaf46 --- /dev/null +++ b/packages/nextjs/hooks/scaffold-stark/useSwitchNetwork.ts @@ -0,0 +1,14 @@ +export const useSwitchNetwork = () => { + return { + switchNetwork: async (network: string) => { + if (window.starknet && window.starknet.isConnected) { + await window.starknet.request({ + type: "wallet_switchStarknetChain", + params: { + chainId: `SN_${network == "mainnet" ? "MAIN" : network.toUpperCase()}` + } + }); + } + } + } +} \ No newline at end of file diff --git a/packages/nextjs/hooks/scaffold-stark/useTargetNetwork.ts b/packages/nextjs/hooks/scaffold-stark/useTargetNetwork.ts index 5fe2163b..3d2a9d6b 100644 --- a/packages/nextjs/hooks/scaffold-stark/useTargetNetwork.ts +++ b/packages/nextjs/hooks/scaffold-stark/useTargetNetwork.ts @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { useNetwork } from "@starknet-react/core"; +import {useAccount} from "@starknet-react/core"; import scaffoldConfig from "~~/scaffold.config"; import { useGlobalState } from "~~/services/store/store"; import { ChainWithAttributes } from "~~/utils/scaffold-stark"; @@ -9,7 +9,7 @@ import { ChainWithAttributes } from "~~/utils/scaffold-stark"; * Retrieves the connected wallet's network from scaffold.config or defaults to the 0th network in the list if the wallet is not connected. */ export function useTargetNetwork(): { targetNetwork: ChainWithAttributes } { - const { chain } = useNetwork(); + const { chainId } = useAccount(); const targetNetwork = useGlobalState(({ targetNetwork }) => targetNetwork); const setTargetNetwork = useGlobalState( ({ setTargetNetwork }) => setTargetNetwork @@ -17,12 +17,12 @@ export function useTargetNetwork(): { targetNetwork: ChainWithAttributes } { useEffect(() => { const newSelectedNetwork = scaffoldConfig.targetNetworks.find( - (targetNetwork) => targetNetwork.id === chain?.id + (targetNetwork) => targetNetwork.id === chainId ); if (newSelectedNetwork && newSelectedNetwork.id !== targetNetwork.id) { setTargetNetwork(newSelectedNetwork); } - }, [chain?.id, setTargetNetwork, targetNetwork.id]); + }, [chainId, setTargetNetwork, targetNetwork.id]); return { targetNetwork: { diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 85d5a75f..b78822b2 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -18,11 +18,13 @@ "@starknet-react/chains": "^0.1.7", "@starknet-react/core": "^2.3.0", "abi-wan-kanabi": "^2.2.2", + "blo": "^1.1.1", "daisyui": "^4.7.3", "get-starknet-core": "^3.2.0", "next": "14.1.3", "next-themes": "^0.2.1", "nprogress": "^0.2.0", + "qrcode.react": "^3.1.0", "react": "^18", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18", diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index c8792353..71377066 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -2,10 +2,20 @@ import * as chains from "@starknet-react/chains"; export type ScaffoldConfig = { targetNetworks: readonly chains.Chain[]; + onlyLocalBurnerWallet: boolean; + walletAutoConnect: boolean; }; const scaffoldConfig = { targetNetworks: [chains.sepolia], + // Only show the Burner Wallet when running on devnet + onlyLocalBurnerWallet: false, + /** + * Auto connect: + * 1. If the user was connected into a wallet before, on page reload reconnect automatically + * 2. If user is not connected to any wallet: On reload, connect to burner wallet if burnerWallet.enabled is true && burnerWallet.onlyLocal is false + */ + walletAutoConnect: true, } as const satisfies ScaffoldConfig; export default scaffoldConfig; diff --git a/packages/nextjs/services/web3/stark-burner/BurnerConnector.ts b/packages/nextjs/services/web3/stark-burner/BurnerConnector.ts new file mode 100644 index 00000000..b80bf0e9 --- /dev/null +++ b/packages/nextjs/services/web3/stark-burner/BurnerConnector.ts @@ -0,0 +1,73 @@ +import {InjectedConnector, starknetChainId} from "@starknet-react/core"; +import { + Account, + AccountInterface, + RpcProvider, +} from "starknet"; +import {ConnectorData, ConnectorIcons} from "@starknet-react/core/src/connectors/base"; +import {Chain, devnet} from "@starknet-react/chains"; +import scaffoldConfig from "~~/scaffold.config"; + +export const burnerWalletId = "burner-wallet"; +export const burnerWalletName = "Burner Wallet"; +const burnerWalletIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzUzIiBoZWlnaHQ9IjM1MiIgdmlld0JveD0iMCAwIDM1MyAzNTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzE2MzA5IiB5PSIwLjMxNzEzOSIgd2lkdGg9IjM1MS4zOTQiIGhlaWdodD0iMzUxLjM5NCIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzNfMTUxKSIvPgo8Y2lyY2xlIGN4PSIzNC40OTUzIiBjeT0iMzQuNDk1MyIgcj0iMzQuNDk1MyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjA3LjAxOCAyNTQuMTIpIiBmaWxsPSIjRkY2NjBBIi8+CjxwYXRoIGQ9Ik0xNTQuMzE4IDMxNy45NTVDMTcxLjI3MyAzMTAuODkgMTc2LjU4MiAyOTAuNzE1IDE3Ni4xNTcgMjgzLjQ4N0wyMDcuMDE4IDI4OC44NjRDMjA3LjAxOCAzMDMuMzE0IDIwMC4yMTIgMzA5LjQwMiAxOTcuODI0IDMxMi40MzNDMTkzLjQ3NCAzMTcuOTU1IDE3My4zNTEgMzMwLjAzIDE1NC4zMTggMzE3Ljk1NVoiIGZpbGw9InVybCgjcGFpbnQxX3JhZGlhbF8zXzE1MSkiLz4KPGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfZF8zXzE1MSkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIyNy4zNzcgMzAyLjI3NkMyMjYuNDI2IDMwNS44OTcgMjMwLjMxNSAzMDkuNDA1IDIzMy4zOTYgMzA3LjI3OUMyNTQuNTM4IDI5Mi42ODQgMjcwLjQ3OSAyNjkuOTQ1IDI3NC44OSAyNDcuNDg5QzI4Mi4yNCAyMTAuMDcxIDI3Mi4yMzUgMTc1LjcyNyAyMzguMDI4IDE0NS45MjVDMjAwLjg3NCAxMTMuNTU2IDE5MS44NDQgODguNDU2MSAxOTAuMTYyIDUwLjg3MThDMTg5Ljc5NyA0Mi43MjE4IDE4MS42MDQgMzcuMjk0NyAxNzQuODI0IDQxLjgzMTdDMTUyLjY2OCA1Ni42NTc0IDEzMi41MTIgODQuNDk5IDEzOC45MTEgMTIwLjc1OEMxNDEuMDA0IDEzMi42MjEgMTQ2Ljc5NCAxNDEuMDE2IDE1MS45NyAxNDguNTIzQzE1OC40OTEgMTU3Ljk3OCAxNjQuMDM5IDE2Ni4wMjMgMTU5Ljk5NyAxNzcuODFDMTU1LjIwMyAxOTEuNzk0IDEzOS4xMzQgMTk5LjE2MiAxMjguNzQ3IDE5Mi40MjlDMTE0LjE3IDE4Mi45ODEgMTEzLjI1MyAxNjYuNjUxIDExNy45NjkgMTQ5LjQ1NkMxMTguOTAyIDE0Ni4wNTUgMTE1LjQ3MSAxNDMuMjA0IDExMi42OCAxNDUuMzU5QzkxLjM2MDQgMTYxLjgyMSA2OS4xNTMyIDE5OS4yNjcgNzcuNjY0NyAyNDcuNDg5Qzg1Ljk3OTIgMjc2LjIxMiA5Ny45Mjc3IDI5Mi41MzcgMTEwLjk3MSAzMDEuNTQxQzExMy43NjMgMzAzLjQ2OCAxMTcuMTU5IDMwMC42MzEgMTE2LjU5NyAyOTcuMjg2QzExNi4wODEgMjk0LjIxMiAxMTUuODEzIDI5MS4wNTQgMTE1LjgxMyAyODcuODMzQzExNS44MTMgMjU2LjUxMyAxNDEuMjAzIDIzMS4xMjMgMTcyLjUyMyAyMzEuMTIzQzIwMy44NDIgMjMxLjEyMyAyMjkuMjMyIDI1Ni41MTMgMjI5LjIzMiAyODcuODMzQzIyOS4yMzIgMjkyLjgyNCAyMjguNTg3IDI5Ny42NjUgMjI3LjM3NyAzMDIuMjc2WiIgZmlsbD0idXJsKCNwYWludDJfbGluZWFyXzNfMTUxKSIvPgo8L2c+CjxkZWZzPgo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2RfM18xNTEiIHg9IjcyLjExMTIiIHk9IjM2LjQ5NCIgd2lkdGg9IjIwOC43NDIiIGhlaWdodD0iMjc1LjEyIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPgo8ZmVPZmZzZXQvPgo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxLjg0NTA2Ii8+CjxmZUNvbXBvc2l0ZSBpbjI9ImhhcmRBbHBoYSIgb3BlcmF0b3I9Im91dCIvPgo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAxIDAgMCAwIDAgMC40MiAwIDAgMCAwIDAgMCAwIDAgMC43IDAiLz4KPGZlQmxlbmQgbW9kZT0ibXVsdGlwbHkiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd18zXzE1MSIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvd18zXzE1MSIgcmVzdWx0PSJzaGFwZSIvPgo8L2ZpbHRlcj4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzNfMTUxIiB4MT0iMTc2LjQxMyIgeTE9IjAuMzE3MTM5IiB4Mj0iMTc2LjQxMyIgeTI9IjM1MS43MTEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRjI3OSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkQzMzYiLz4KPC9saW5lYXJHcmFkaWVudD4KPHJhZGlhbEdyYWRpZW50IGlkPSJwYWludDFfcmFkaWFsXzNfMTUxIiBjeD0iMCIgY3k9IjAiIHI9IjEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKDIxOC4wNDggMjQ5LjM0Nykgcm90YXRlKDEyNC4wMTgpIHNjYWxlKDg5LjI5NTUgMjY0LjgwOSkiPgo8c3RvcCBvZmZzZXQ9IjAuNjQwODUiIHN0b3AtY29sb3I9IiNGRjY2MEEiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCRTE1Ii8+CjwvcmFkaWFsR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQyX2xpbmVhcl8zXzE1MSIgeDE9IjE3Ni40ODIiIHkxPSI0MC4xODQxIiB4Mj0iMTc2LjQ4MiIgeTI9IjMxNy4yNzgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwLjMzODU0MiIgc3RvcC1jb2xvcj0iI0ZGOEYzRiIvPgo8c3RvcCBvZmZzZXQ9IjAuNjU2MjUiIHN0b3AtY29sb3I9IiNGRjcwMjAiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkYzRDAwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg=="; + +// https://github.com/apibara/starknet-react/blob/main/packages/core/src/connectors/injected.ts +export class BurnerConnector extends InjectedConnector { + chain: Chain = devnet; + + constructor() { + super({ + options: { + id: burnerWalletId, + name: burnerWalletName, + icon: {dark: burnerWalletIcon, light: burnerWalletIcon} + } + }); + this.chain = scaffoldConfig.targetNetworks[0] + } + + get id(): string { + return super.id; + } + + get name(): string { + return super.name; + } + + async account(): Promise { + const address = "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691"; + const privateKey = "0x71d7bb07b9a64f6f78ac4c816aff4da9" + return Promise.resolve(new Account(new RpcProvider({nodeUrl: this.chain.rpcUrls.public.http[0], chainId: starknetChainId(this.chain.id)}), address, privateKey)); + } + + available(): boolean { + return true; + } + + chainId(): Promise { + return Promise.resolve(this.chain.id); + } + + get icon(): ConnectorIcons { + return { + dark: burnerWalletIcon, + light: burnerWalletIcon + }; + } + + async ready(): Promise { + return Promise.resolve((await this.account()).address !== ""); + } + + + async connect(): Promise> { + return Promise.resolve({account: (await this.account()).address, chainId: this.chain.id}); + } + + disconnect(): Promise { + console.log("disconnect from burnerwallet"); + return Promise.resolve(); + } + +} diff --git a/packages/nextjs/services/web3/stark-burner/BurnerConnectorErrors.ts b/packages/nextjs/services/web3/stark-burner/BurnerConnectorErrors.ts new file mode 100644 index 00000000..48478706 --- /dev/null +++ b/packages/nextjs/services/web3/stark-burner/BurnerConnectorErrors.ts @@ -0,0 +1,23 @@ +/** + * Error list used by {@link BurnerConnectorError} + */ +export const BurnerConnectorErrorList = { + accountNotFound: "Account not found", + couldNotConnect: "Could not connect to network", + unsupportedBurnerChain: "This network is not supported for burner connector", + chainIdNotResolved: "Could not resolve chainId", + chainNotSupported: "Chain is not supported, check burner wallet config", +} as const; + +/** + * A union of all the BurnerConnectorErrorList + */ +export type BurnerConnectorErrorTypes = (typeof BurnerConnectorErrorList)[keyof typeof BurnerConnectorErrorList]; + +export class BurnerConnectorError extends Error { + constructor(errorType: BurnerConnectorErrorTypes, message?: string) { + const msg = `BurnerConnectorError ${errorType}: ${message ?? ""} `; + super(msg); + console.warn(msg); + } +} diff --git a/yarn.lock b/yarn.lock index cf85ce3e..4f92eed0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1578,6 +1578,7 @@ __metadata: "@types/react-dom": ^18 abi-wan-kanabi: ^2.2.2 autoprefixer: ^10.0.1 + blo: ^1.1.1 daisyui: ^4.7.3 eslint: ^8 eslint-config-next: 14.1.3 @@ -1586,6 +1587,7 @@ __metadata: next-themes: ^0.2.1 nprogress: ^0.2.0 postcss: ^8 + qrcode.react: ^3.1.0 react: ^18 react-copy-to-clipboard: ^5.1.0 react-dom: ^18 @@ -2532,6 +2534,13 @@ __metadata: languageName: node linkType: hard +"blo@npm:^1.1.1": + version: 1.1.1 + resolution: "blo@npm:1.1.1" + checksum: 896183e4ff43e8ebfc89153f6772e510d8f4566ef25e6bebb7e31531e1059c0517bc23d2d605758c9e5ea0c62c21203172575625a53748d013ed78ace08f30f4 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -6168,6 +6177,15 @@ __metadata: languageName: node linkType: hard +"qrcode.react@npm:^3.1.0": + version: 3.1.0 + resolution: "qrcode.react@npm:3.1.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 94a2942ecf83f461d869adb20305ae663c6d1abe93ef2c72442b07d756ce70cf6deb6fd588dc5b382b48c6991cfde1dfd5ac9b814c1461e71d5edb2d945e67fc + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" From 7020bc1f4085e2be46f425fedd1bde181db0572d Mon Sep 17 00:00:00 2001 From: Carlos Ramos <88168831+jrcarlos2000@users.noreply.github.com> Date: Sun, 7 Apr 2024 17:30:56 +0800 Subject: [PATCH 2/7] Update scaffold.config.ts --- packages/nextjs/scaffold.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index 71377066..c933e724 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -7,7 +7,7 @@ export type ScaffoldConfig = { }; const scaffoldConfig = { - targetNetworks: [chains.sepolia], + targetNetworks: [chains.devnet], // Only show the Burner Wallet when running on devnet onlyLocalBurnerWallet: false, /** From 7da6d07969e9eaf9daeb67f45523118434831508 Mon Sep 17 00:00:00 2001 From: Quantum3Labs <157618676+0xquantum3labs@users.noreply.github.com> Date: Sun, 7 Apr 2024 18:25:44 +0800 Subject: [PATCH 3/7] Remove bash (#34) * updadte json script * remove bash scripts --- packages/snfoundry/bash/run-devnet.sh | 2 -- packages/snfoundry/package.json | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100755 packages/snfoundry/bash/run-devnet.sh diff --git a/packages/snfoundry/bash/run-devnet.sh b/packages/snfoundry/bash/run-devnet.sh deleted file mode 100755 index 04cd4f35..00000000 --- a/packages/snfoundry/bash/run-devnet.sh +++ /dev/null @@ -1,2 +0,0 @@ -cd .. && cd local-devnet -cargo run -- --seed 0 --account-class cairo1 \ No newline at end of file diff --git a/packages/snfoundry/package.json b/packages/snfoundry/package.json index fe02baa9..181fdd0b 100644 --- a/packages/snfoundry/package.json +++ b/packages/snfoundry/package.json @@ -2,7 +2,7 @@ "name": "@ss-2/snfoundry", "version": "0.0.1", "scripts": { - "chain": "cd bash && ./run-devnet.sh", + "chain": "cd local-devnet && cargo run -- --seed 0 --account-class cairo1", "deploy": "node scripts_js/helpers/deploy-wrapper.js", "prev-deploy": "scarb build && node scripts_js/deploy.js ${1} > ./scripts_js/deployOutput.txt && node ./scripts_js/parseDeployOutput.js", "bare-deploy": "scripts_js/deploy.js" From 59d6c3896b8d8567c6d23944926a921e8cdef75d Mon Sep 17 00:00:00 2001 From: 0xquantum3labs Date: Sun, 7 Apr 2024 18:51:25 +0800 Subject: [PATCH 4/7] remove max fee --- packages/snfoundry/scripts_js/deploy.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/snfoundry/scripts_js/deploy.js b/packages/snfoundry/scripts_js/deploy.js index 6a7f5bd6..45e29bf2 100644 --- a/packages/snfoundry/scripts_js/deploy.js +++ b/packages/snfoundry/scripts_js/deploy.js @@ -77,16 +77,11 @@ const deployContract = async ( } try { - const tryDeclareAndDeploy = await deployer.declareAndDeploy( - { - contract: compiledContractSierra, - casm: compiledContractCasm, - constructorCalldata, - }, - { - maxFee: totalFee, - } - ); + const tryDeclareAndDeploy = await deployer.declareAndDeploy({ + contract: compiledContractSierra, + casm: compiledContractCasm, + constructorCalldata, + }); await provider.waitForTransaction( tryDeclareAndDeploy.deploy.transaction_hash ); @@ -132,7 +127,7 @@ const deployScript = async () => { } = await deployContract(null, "HelloStarknet"); // can pass another argument for the exported contract name await deployContract( { - name: 1 + name: 1, }, "SimpleStorage" ); // simple storage receives an argument in the constructor @@ -152,7 +147,9 @@ const deployScript = async () => { ); await deployContract( { - initial_owner: addAddressPadding("0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691"), + initial_owner: addAddressPadding( + "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691" + ), }, "Ownable" ); // simple storage receives an argument in the constructor From b0eaa50623d518ba134adf2f98f273761b2f0d55 Mon Sep 17 00:00:00 2001 From: 0xquantum3labs Date: Sun, 7 Apr 2024 18:55:21 +0800 Subject: [PATCH 5/7] add success states --- packages/snfoundry/scripts_js/deploy.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/snfoundry/scripts_js/deploy.js b/packages/snfoundry/scripts_js/deploy.js index 45e29bf2..6405e1dd 100644 --- a/packages/snfoundry/scripts_js/deploy.js +++ b/packages/snfoundry/scripts_js/deploy.js @@ -7,7 +7,7 @@ const { ContractAddress, getChecksumAddress, CallData, - validateChecksumAddress, + TransactionStatus, addAddressPadding, } = require("starknet"); const { hash } = require("starknet"); @@ -83,7 +83,10 @@ const deployContract = async ( constructorCalldata, }); await provider.waitForTransaction( - tryDeclareAndDeploy.deploy.transaction_hash + tryDeclareAndDeploy.deploy.transaction_hash, + { + successStates: [TransactionStatus.ACCEPTED_ON_L2], + } ); classHash = tryDeclareAndDeploy.declare.class_hash; existingClass = await provider.getClassByHash(classHash); From cd031818c7a622f0bfb26ba45339dc64c1ae7f9f Mon Sep 17 00:00:00 2001 From: Nadai <112663528+Nadai2010@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:06:58 +0100 Subject: [PATCH 6/7] branch Challenge 1 (#36) * Feat: Add basic smart contract for challenge-0 * deploy erc721 * Fix deploy challenge-0 * Update 3 challnge, preset de OZ, all compiled --------- Co-authored-by: gianalarcon --- .../nextjs/contracts/deployedContracts.ts | 601 ++++++++++-------- packages/snfoundry/contracts/Scarb.lock | 8 + packages/snfoundry/contracts/Scarb.toml | 1 + .../snfoundry/contracts/src/challenge0.cairo | 63 ++ .../snfoundry/contracts/src/challenge1.cairo | 39 ++ .../snfoundry/contracts/src/challenge2.cairo | 54 ++ .../snfoundry/contracts/src/challenge3.cairo | 35 + packages/snfoundry/contracts/src/lib.cairo | 4 + packages/snfoundry/scripts_js/deploy.js | 47 +- 9 files changed, 579 insertions(+), 273 deletions(-) create mode 100644 packages/snfoundry/contracts/src/challenge0.cairo create mode 100644 packages/snfoundry/contracts/src/challenge1.cairo create mode 100644 packages/snfoundry/contracts/src/challenge2.cairo create mode 100644 packages/snfoundry/contracts/src/challenge3.cairo diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 22e75953..91b86e89 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ const deployedContracts = { devnet: { HelloStarknet: { address: - "0x0739258a1891b30fbee34ba0b984acc3f55bc19f63e99c96b1a88606383dd322", + "0x049596cd6bba05027f7928d893b5a204720e59c5591206ea4d957f29f3481a6a", abi: [ { type: "impl", @@ -67,92 +67,162 @@ const deployedContracts = { }, ], }, - }, - mainnet: { - HelloStarknet: { + Challenge0: { address: - "0x0750aa622d77436d69d58c91dbc73a6d0d25db0b7076c1377cdda15e13fd632b", + "0x0047466ee4e2950b249a27cc77d6eaecb30e28096d901a2bb1ee94654242d34f", abi: [ { type: "impl", - name: "HelloStarknetImpl", - interface_name: "contracts::helloStarknet::IHelloStarknet", + name: "ERC721MixinImpl", + interface_name: "openzeppelin::token::erc721::interface::ERC721ABI", + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "struct", + name: "core::array::Span::", + members: [ + { + name: "snapshot", + type: "@core::array::Array::", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], }, { type: "interface", - name: "contracts::helloStarknet::IHelloStarknet", + name: "openzeppelin::token::erc721::interface::ERC721ABI", items: [ { type: "function", - name: "increase_balance", + name: "balance_of", inputs: [ { - name: "amount", - type: "core::integer::u32", + name: "account", + type: "core::starknet::contract_address::ContractAddress", }, ], - outputs: [], - state_mutability: "external", + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", }, { type: "function", - name: "get_balance", - inputs: [], + name: "owner_of", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], outputs: [ { - type: "core::integer::u32", + type: "core::starknet::contract_address::ContractAddress", }, ], state_mutability: "view", }, { type: "function", - name: "get_balance_increment", + name: "safe_transfer_from", inputs: [ { - name: "amount", - type: "core::integer::u32", + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", }, ], - outputs: [ + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transfer_from", + inputs: [ { - type: "core::integer::u32", + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", }, ], - state_mutability: "view", + outputs: [], + state_mutability: "external", }, - ], - }, - { - type: "event", - name: "contracts::helloStarknet::HelloStarknet::Event", - kind: "enum", - variants: [], - }, - ], - }, - }, - sepolia: { - HelloStarknet: { - address: - "0x009329408104badc53c6338ba88966a565bf97268a9ac43114f395114c4b4b91", - abi: [ - { - type: "impl", - name: "HelloStarknetImpl", - interface_name: "contracts::helloStarknet::IHelloStarknet", - }, - { - type: "interface", - name: "contracts::helloStarknet::IHelloStarknet", - items: [ { type: "function", - name: "increase_balance", + name: "approve", inputs: [ { - name: "amount", - type: "core::integer::u32", + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", }, ], outputs: [], @@ -160,310 +230,211 @@ const deployedContracts = { }, { type: "function", - name: "get_balance", - inputs: [], - outputs: [ + name: "set_approval_for_all", + inputs: [ { - type: "core::integer::u32", + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", }, ], - state_mutability: "view", + outputs: [], + state_mutability: "external", }, { type: "function", - name: "get_balance_increment", + name: "get_approved", inputs: [ { - name: "amount", - type: "core::integer::u32", + name: "token_id", + type: "core::integer::u256", }, ], outputs: [ { - type: "core::integer::u32", + type: "core::starknet::contract_address::ContractAddress", }, ], state_mutability: "view", }, - ], - }, - { - type: "event", - name: "contracts::helloStarknet::HelloStarknet::Event", - kind: "enum", - variants: [], - }, - ], - }, - SimpleStorage: { - address: - "0x002c20a4ee6eee7c976e3e71c4217871c7c629c5b3d8b2d03ca151512894f5af", - abi: [ - { - type: "impl", - name: "SimpleStorageImpl", - interface_name: "contracts::simpleStorage::ISimpleStorage", - }, - { - type: "interface", - name: "contracts::simpleStorage::ISimpleStorage", - items: [ { type: "function", - name: "get_name", - inputs: [], + name: "is_approved_for_all", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], outputs: [ { - type: "core::felt252", + type: "core::bool", }, ], state_mutability: "view", }, { type: "function", - name: "set_name", + name: "supports_interface", inputs: [ { - name: "name", + name: "interface_id", type: "core::felt252", }, ], - outputs: [], - state_mutability: "external", + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ { + type: "function", name: "name", - type: "core::felt252", - }, - ], - }, - { - type: "event", - name: "contracts::simpleStorage::SimpleStorage::Event", - kind: "enum", - variants: [], - }, - ], - }, - Vote: { - address: - "0x04c394c4fc87a9a62f04205ac456f6811b6bcb0465e339f95308332cbff7364f", - abi: [ - { - type: "impl", - name: "VoteImpl", - interface_name: "contracts::vote::VoteTrait", - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", }, - ], - }, - { - type: "interface", - name: "contracts::vote::VoteTrait", - items: [ { type: "function", - name: "get_vote_status", + name: "symbol", inputs: [], outputs: [ { - type: "(core::integer::u8, core::integer::u8, core::integer::u8, core::integer::u8)", + type: "core::byte_array::ByteArray", }, ], state_mutability: "view", }, { type: "function", - name: "voter_can_vote", + name: "token_uri", inputs: [ { - name: "user_address", - type: "core::starknet::contract_address::ContractAddress", + name: "token_id", + type: "core::integer::u256", }, ], outputs: [ { - type: "core::bool", + type: "core::byte_array::ByteArray", }, ], state_mutability: "view", }, { type: "function", - name: "is_voter_registered", + name: "balanceOf", inputs: [ { - name: "address", + name: "account", type: "core::starknet::contract_address::ContractAddress", }, ], outputs: [ { - type: "core::bool", + type: "core::integer::u256", }, ], state_mutability: "view", }, { type: "function", - name: "vote", + name: "ownerOf", inputs: [ { - name: "vote", - type: "core::integer::u8", + name: "tokenId", + type: "core::integer::u256", }, ], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "voter_1", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "voter_2", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "voter_3", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "contracts::vote::Vote::VoteCast", - kind: "struct", - members: [ - { - name: "voter", - type: "core::starknet::contract_address::ContractAddress", - kind: "data", - }, - { - name: "vote", - type: "core::integer::u8", - kind: "data", - }, - ], - }, - { - type: "event", - name: "contracts::vote::Vote::UnauthorizedAttempt", - kind: "struct", - members: [ - { - name: "unauthorized_address", - type: "core::starknet::contract_address::ContractAddress", - kind: "data", - }, - ], - }, - { - type: "event", - name: "contracts::vote::Vote::Event", - kind: "enum", - variants: [ - { - name: "VoteCast", - type: "contracts::vote::Vote::VoteCast", - kind: "nested", + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", }, - { - name: "UnauthorizedAttempt", - type: "contracts::vote::Vote::UnauthorizedAttempt", - kind: "nested", - }, - ], - }, - ], - }, - Ownable: { - address: - "0x07b0a115eb4111e34607418ff37015e8f60149541d3af39b094d19eea2ec5ad4", - abi: [ - { - type: "impl", - name: "OwnableDataImpl", - interface_name: "contracts::IData", - }, - { - type: "interface", - name: "contracts::IData", - items: [ { type: "function", - name: "get_data", - inputs: [], - outputs: [ + name: "safeTransferFrom", + inputs: [ { - type: "core::integer::u64", + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "tokenId", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", }, ], - state_mutability: "view", + outputs: [], + state_mutability: "external", }, { type: "function", - name: "set_data", + name: "transferFrom", inputs: [ { - name: "new_value", - type: "core::integer::u64", + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "tokenId", + type: "core::integer::u256", }, ], outputs: [], state_mutability: "external", }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "contracts::IOwnable", - }, - { - type: "interface", - name: "contracts::IOwnable", - items: [ { type: "function", - name: "transfer_ownership", + name: "setApprovalForAll", inputs: [ { - name: "new_owner", + name: "operator", type: "core::starknet::contract_address::ContractAddress", }, + { + name: "approved", + type: "core::bool", + }, ], outputs: [], state_mutability: "external", }, { type: "function", - name: "owner", - inputs: [], + name: "getApproved", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], outputs: [ { type: "core::starknet::contract_address::ContractAddress", @@ -471,6 +442,42 @@ const deployedContracts = { ], state_mutability: "view", }, + { + type: "function", + name: "isApprovedForAll", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "tokenURI", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, ], }, { @@ -478,49 +485,119 @@ const deployedContracts = { name: "constructor", inputs: [ { - name: "initial_owner", + name: "recipient", + type: "core::felt252", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "struct", + members: [ + { + name: "from", type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", }, ], }, { type: "event", - name: "contracts::ownable_component::OwnershipTransferred", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", kind: "struct", members: [ { - name: "previous_owner", + name: "owner", type: "core::starknet::contract_address::ContractAddress", kind: "key", }, { - name: "new_owner", + name: "approved", type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::bool", kind: "data", }, ], }, { type: "event", - name: "contracts::ownable_component::Event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", kind: "enum", variants: [ { - name: "OwnershipTransferred", - type: "contracts::ownable_component::OwnershipTransferred", + name: "Transfer", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "nested", + }, + { + name: "Approval", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "nested", + }, + { + name: "ApprovalForAll", + type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", kind: "nested", }, ], }, { type: "event", - name: "contracts::Ownable::Event", + name: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::challenge0::Challenge0::Event", kind: "enum", variants: [ { - name: "OwnableEvent", - type: "contracts::ownable_component::Event", - kind: "nested", + name: "ERC721Event", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "flat", + }, + { + name: "SRC5Event", + type: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "flat", }, ], }, diff --git a/packages/snfoundry/contracts/Scarb.lock b/packages/snfoundry/contracts/Scarb.lock index 83cca99e..5edbcf72 100644 --- a/packages/snfoundry/contracts/Scarb.lock +++ b/packages/snfoundry/contracts/Scarb.lock @@ -4,3 +4,11 @@ version = 1 [[package]] name = "contracts" version = "0.1.0" +dependencies = [ + "openzeppelin", +] + +[[package]] +name = "openzeppelin" +version = "0.10.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.10.0#d77082732daab2690ba50742ea41080eb23299d3" diff --git a/packages/snfoundry/contracts/Scarb.toml b/packages/snfoundry/contracts/Scarb.toml index c6c3f691..2c315c76 100644 --- a/packages/snfoundry/contracts/Scarb.toml +++ b/packages/snfoundry/contracts/Scarb.toml @@ -8,6 +8,7 @@ edition = "2023_11" [dependencies] starknet = ">=2.5.4" +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.10.0" } [[target.starknet-contract]] casm = true diff --git a/packages/snfoundry/contracts/src/challenge0.cairo b/packages/snfoundry/contracts/src/challenge0.cairo new file mode 100644 index 00000000..217fc74f --- /dev/null +++ b/packages/snfoundry/contracts/src/challenge0.cairo @@ -0,0 +1,63 @@ +#[starknet::contract] +mod Challenge0 { + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::ERC721Component; + use starknet::ContractAddress; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC721Mixin + #[abi(embed_v0)] + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + recipient: felt252 + ) { + //let arr = array![1, 2, 3]; + let name:ByteArray = "YourCollectible"; + let symbol:ByteArray = "YCB"; + let base_uri:ByteArray = "https://ipfs.io/ipfs/QmfVMAmNM1kDEBYrC2TPzQDoCRFH6F5tE1e9Mr4FkkR5Xr"; // bison nft + //let token_id = arr.span(); + + self.erc721.initializer(name, symbol, base_uri); + //self._mint_assets(recipient, token_id); + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + /// Mints `token_ids` to `recipient`. + fn _mint_assets( + ref self: ContractState, recipient: ContractAddress, mut token_ids: Span + ) { + loop { + if token_ids.len() == 0 { + break; + } + let id = *token_ids.pop_front().unwrap(); + + self.erc721._mint(recipient, id); + } + } + } +} \ No newline at end of file diff --git a/packages/snfoundry/contracts/src/challenge1.cairo b/packages/snfoundry/contracts/src/challenge1.cairo new file mode 100644 index 00000000..3b367d74 --- /dev/null +++ b/packages/snfoundry/contracts/src/challenge1.cairo @@ -0,0 +1,39 @@ +#[starknet::contract] +mod Challenge1 { + use openzeppelin::token::erc20::ERC20Component; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // ERC20Mixin + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc20: ERC20Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event + } + + /// Sets the token `name` and `symbol`. + /// Mints `fixed_supply` tokens to `recipient`. + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + fixed_supply: u256, + recipient: ContractAddress + ) { + self.erc20.initializer(name, symbol); + self.erc20._mint(recipient, fixed_supply); + } +} \ No newline at end of file diff --git a/packages/snfoundry/contracts/src/challenge2.cairo b/packages/snfoundry/contracts/src/challenge2.cairo new file mode 100644 index 00000000..050ae7c8 --- /dev/null +++ b/packages/snfoundry/contracts/src/challenge2.cairo @@ -0,0 +1,54 @@ +#[starknet::contract] +mod Challenge2 { + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc1155::ERC1155Component; + use starknet::ContractAddress; + + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC1155 + #[abi(embed_v0)] + impl ERC1155Impl = ERC1155Component::ERC1155Impl; + #[abi(embed_v0)] + impl ERC1155MetadataURIImpl = + ERC1155Component::ERC1155MetadataURIImpl; + #[abi(embed_v0)] + impl ERC1155Camel = ERC1155Component::ERC1155CamelImpl; + impl ERC1155InternalImpl = ERC1155Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc1155: ERC1155Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC1155Event: ERC1155Component::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + base_uri: ByteArray, + recipient: ContractAddress, + token_ids: Span, + values: Span + ) { + self.erc1155.initializer(base_uri); + self + .erc1155 + .batch_mint_with_acceptance_check(recipient, token_ids, values, array![].span()); + } +} \ No newline at end of file diff --git a/packages/snfoundry/contracts/src/challenge3.cairo b/packages/snfoundry/contracts/src/challenge3.cairo new file mode 100644 index 00000000..0ce54256 --- /dev/null +++ b/packages/snfoundry/contracts/src/challenge3.cairo @@ -0,0 +1,35 @@ +#[starknet::contract(account)] +mod Challenge3 { + use openzeppelin::account::AccountComponent; + use openzeppelin::introspection::src5::SRC5Component; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // AccountMixin + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + impl AccountInternalImpl = AccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + } +} \ No newline at end of file diff --git a/packages/snfoundry/contracts/src/lib.cairo b/packages/snfoundry/contracts/src/lib.cairo index ebeb0718..4290cbfd 100644 --- a/packages/snfoundry/contracts/src/lib.cairo +++ b/packages/snfoundry/contracts/src/lib.cairo @@ -1,6 +1,10 @@ mod helloStarknet; mod simpleStorage; mod vote; +mod challenge0; +mod challenge1; +mod challenge2; +mod challenge3; use starknet::ContractAddress; diff --git a/packages/snfoundry/scripts_js/deploy.js b/packages/snfoundry/scripts_js/deploy.js index 6405e1dd..e3d272ed 100644 --- a/packages/snfoundry/scripts_js/deploy.js +++ b/packages/snfoundry/scripts_js/deploy.js @@ -130,21 +130,46 @@ const deployScript = async () => { } = await deployContract(null, "HelloStarknet"); // can pass another argument for the exported contract name await deployContract( { - name: 1, + recipient: 1, }, - "SimpleStorage" + "Challenge0" ); // simple storage receives an argument in the constructor + await deployContract( { - voter_1: addAddressPadding( - "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E" - ), - voter_2: addAddressPadding( - "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E" - ), - voter_3: addAddressPadding( - "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E" - ), + name: 1, + symbol: 2, + fixed_supply: 10, + recipient: "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + }, + "Challenge1" + ); + + await deployContract( + { + base_uri: 1, + recipient: "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + token_ids: 2, + values: 100, + }, + "Challenge2" + ); + + await deployContract( + { + public_key: "0x6e4fd4f9d6442e10cf8e20a799be3533be3756c5ea4d13e16a297d7d2717039", + }, + "Challenge3" + ); + + await deployContract( + { + voter_1: + "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + voter_2: + "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + voter_3: + "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", }, "Vote" ); From 785110e91f0494a629150a7113c3e1254566ba03 Mon Sep 17 00:00:00 2001 From: 0xquantum3labs Date: Tue, 9 Apr 2024 01:22:29 +0800 Subject: [PATCH 7/7] revert deploy scripts --- packages/snfoundry/scripts_js/deploy.js | 104 +++++++++++++----------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/packages/snfoundry/scripts_js/deploy.js b/packages/snfoundry/scripts_js/deploy.js index e3d272ed..eefaa449 100644 --- a/packages/snfoundry/scripts_js/deploy.js +++ b/packages/snfoundry/scripts_js/deploy.js @@ -128,59 +128,67 @@ const deployScript = async () => { abi: helloStarknetAbi, address: ContractAddress, } = await deployContract(null, "HelloStarknet"); // can pass another argument for the exported contract name - await deployContract( - { - recipient: 1, - }, - "Challenge0" - ); // simple storage receives an argument in the constructor - await deployContract( { name: 1, - symbol: 2, - fixed_supply: 10, - recipient: "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", - }, - "Challenge1" - ); - - await deployContract( - { - base_uri: 1, - recipient: "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", - token_ids: 2, - values: 100, - }, - "Challenge2" - ); - - await deployContract( - { - public_key: "0x6e4fd4f9d6442e10cf8e20a799be3533be3756c5ea4d13e16a297d7d2717039", }, - "Challenge3" + "SimpleStorage" ); - - await deployContract( - { - voter_1: - "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", - voter_2: - "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", - voter_3: - "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", - }, - "Vote" - ); - await deployContract( - { - initial_owner: addAddressPadding( - "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691" - ), - }, - "Ownable" - ); // simple storage receives an argument in the constructor + // await deployContract( + // { + // recipient: 1, + // }, + // "Challenge0" + // ); + + // await deployContract( + // { + // name: 1, + // symbol: 2, + // fixed_supply: 10, + // recipient: + // "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + // }, + // "Challenge1" + // ); + + // await deployContract( + // { + // base_uri: CallData.byteArrayFromString("https://example.com/"), + // // recipient: + // // "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + // // token_ids: 2, + // // values: 100, + // }, + // "Challenge2" + // ); + + // await deployContract( + // { + // public_key: "0x6e4fd4f9d6442e10cf8e20a799be3533be3756c5ea4d13e16a297d7d2717039", + // }, + // "Challenge3" + // ); + + // await deployContract( + // { + // voter_1: + // "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + // voter_2: + // "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + // voter_3: + // "0x06072Bb27d275a0bC1deBf1753649b8721CF845B681A48443Ac46baF45769f8E", + // }, + // "Vote" + // ); + // await deployContract( + // { + // initial_owner: addAddressPadding( + // "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691" + // ), + // }, + // "Ownable" + // ); // simple storage receives an argument in the constructor }; deployScript()