diff --git a/AVALANCHE-L1.md b/AVALANCHE-L1.md
index 9471067..4be92c1 100644
--- a/AVALANCHE-L1.md
+++ b/AVALANCHE-L1.md
@@ -21,7 +21,9 @@ It'll take some time to load, go take a walk or something
5. After it boots up the blockchain, for some reason port `9650` doesn't set itself automatically to public, so you gotta click `Ports` to the right of `Terminal`, find port 9650, right click it, Port visibility, Public
-> NOTE: After 30 minutes (or if you close the tab/browser) your codespaces goes to sleep. To restart your blockchain after your codespace was sleep, run `avalanche network start`. Remember to set **Port 9650** to public again.
+> NOTE: After 30 minutes (or if you close the tab/browser) your codespaces goes to sleep. To prevent this from happening, run this command: `while true; do echo "Ah ah ah ah, staying alive!"; sleep 900; done &` to ping the codespace every 15 minutes.
+
+> NOTE: Remember [GitHub will provide users in the free plan](https://docs.github.com/es/billing/managing-billing-for-your-products/managing-billing-for-github-codespaces/about-billing-for-github-codespaces) (boo!) 120 core hours or 60 hours of run time on a 2 core codespace, plus 15 GB of storage each month. So remember to shut it down when you're not using it.
> NOTE for Ava Labs: Maybe a **function to set Port 9650 to public** could be a temporary improvement to open it until it's opened automatically by default.
diff --git a/README.md b/README.md
index 3bcc033..d174dbd 100644
--- a/README.md
+++ b/README.md
@@ -112,7 +112,7 @@ yarn install
4. Start a local Avalanche L1:
-It'd be ideal to run it with one command like `yarn subnet`, but so far, **you gotta follow this instructions **.
+It'd be ideal to run it with one command like `yarn subnet` with a config file, but so far, **you gotta follow this instructions **.
You'll start a local Avalanche L1 using [Ava Labs avalanche-starter-kit](https://github.com/ava-labs/avalanche-starter-kit). The network runs on your local machine and can be used for testing and development.
diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx
index c36b9ca..f9dac23 100644
--- a/packages/nextjs/components/Header.tsx
+++ b/packages/nextjs/components/Header.tsx
@@ -4,18 +4,15 @@ import React from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { SwitchTheme } from "./SwitchTheme";
-import { PunkBalance } from "./punk-society/PunkBalance";
+import { ConfigMenu } from "./punk-society/ConfigMenu";
import { PunkConnectButton } from "./punk-society/PunkConnectButton";
import { FaucetButton } from "./scaffold-eth";
-import { useAccount } from "wagmi";
import { BellIcon, EnvelopeIcon, HomeIcon, MagnifyingGlassIcon } from "@heroicons/react/24/solid";
/**
* Site header
*/
export const Header = () => {
- const { address: connectedAddress } = useAccount();
-
const pathname = usePathname();
return (
@@ -37,7 +34,7 @@ export const Header = () => {
@@ -49,7 +46,7 @@ export const Header = () => {
@@ -61,7 +58,7 @@ export const Header = () => {
@@ -73,7 +70,7 @@ export const Header = () => {
@@ -103,17 +100,13 @@ export const Header = () => {
*/}
-
+
-
-
-
@@ -121,9 +114,10 @@ export const Header = () => {
{/*
*/}
+
+
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/AddressInfoDropdown.tsx b/packages/nextjs/components/punk-society/ConfigMenu/AddressInfoDropdown.tsx
new file mode 100644
index 0000000..66f12eb
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/AddressInfoDropdown.tsx
@@ -0,0 +1,128 @@
+import { useRef, useState } from "react";
+import { NetworkOptions } from "./NetworkOptions";
+import { Address } from "viem";
+import { useDisconnect } from "wagmi";
+import { Cog6ToothIcon } from "@heroicons/react/20/solid";
+import { KeyIcon as KeyIconOutline } from "@heroicons/react/24/outline";
+import {
+ ArrowLeftOnRectangleIcon,
+ ArrowTopRightOnSquareIcon,
+ ArrowsRightLeftIcon,
+ QrCodeIcon,
+} from "@heroicons/react/24/outline";
+import { KeyIcon as KeyIconSolid, LanguageIcon } from "@heroicons/react/24/solid";
+import { useOutsideClick } from "~~/hooks/scaffold-eth";
+import { getTargetNetworks } from "~~/utils/scaffold-eth";
+
+const allowedNetworks = getTargetNetworks();
+
+type AddressInfoDropdownProps = {
+ address: Address;
+ blockExplorerAddressLink: string | undefined;
+ displayName: string;
+ ensAvatar?: string;
+};
+
+export const AddressInfoDropdown = ({ blockExplorerAddressLink }: AddressInfoDropdownProps) => {
+ const [selectingNetwork, setSelectingNetwork] = useState(false);
+
+ const { disconnect } = useDisconnect();
+
+ const dropdownRef = useRef
(null);
+ const closeDropdown = () => {
+ setSelectingNetwork(false);
+ dropdownRef.current?.removeAttribute("open");
+ };
+ useOutsideClick(dropdownRef, closeDropdown);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ View your address
+
+
+
+
+
+ View Private Key
+
+
+
+
+
+
+ Load Private Key
+
+
+ {allowedNetworks.length > 1 ? (
+
+ {
+ setSelectingNetwork(true);
+ }}
+ >
+ Switch Network
+
+
+ ) : null}
+
+
+
+ Switch languages
+
+
+
+
+
+
+
+ View on Block Explorer
+
+
+
+
+
+ disconnect()}
+ >
+ Disconnect
+
+
+
+
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/AddressQRCodeModal.tsx b/packages/nextjs/components/punk-society/ConfigMenu/AddressQRCodeModal.tsx
new file mode 100644
index 0000000..d256142
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/AddressQRCodeModal.tsx
@@ -0,0 +1,50 @@
+import React, { useState } from "react";
+import { QRCodeSVG } from "qrcode.react";
+import { CopyToClipboard } from "react-copy-to-clipboard";
+import { Address as AddressType } from "viem";
+import { notification } from "~~/utils/scaffold-eth";
+
+type AddressQRCodeModalProps = {
+ address: AddressType;
+ modalId: string;
+};
+
+export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps) => {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = () => {
+ setCopied(true);
+ notification.success("Address copied to clipboard");
+ setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds
+ };
+
+ return (
+ <>
+
+
+
+
+
+
Your Address
+
+
+
+
{address}
+
+
+ {copied ? "Copied!" : "Copy Address"}
+
+
+
+ ✕
+
+
+
+
+
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/Balance.tsx b/packages/nextjs/components/punk-society/ConfigMenu/Balance.tsx
new file mode 100644
index 0000000..948fa0a
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/Balance.tsx
@@ -0,0 +1,74 @@
+"use client";
+
+import { Address, formatEther } from "viem";
+import { useDisplayUsdMode } from "~~/hooks/scaffold-eth/useDisplayUsdMode";
+import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
+import { useWatchBalance } from "~~/hooks/scaffold-eth/useWatchBalance";
+import { useGlobalState } from "~~/services/store/store";
+
+type BalanceProps = {
+ address?: Address;
+ className?: string;
+ usdMode?: boolean;
+};
+
+/**
+ * Display (ETH & USD) balance of an ETH address.
+ */
+export const Balance = ({ address, className = "", usdMode }: BalanceProps) => {
+ const { targetNetwork } = useTargetNetwork();
+ const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price);
+ const isNativeCurrencyPriceFetching = useGlobalState(state => state.nativeCurrency.isFetching);
+
+ const {
+ data: balance,
+ isError,
+ isLoading,
+ } = useWatchBalance({
+ address,
+ });
+
+ const { displayUsdMode, toggleDisplayUsdMode } = useDisplayUsdMode({ defaultUsdMode: usdMode });
+
+ if (!address || isLoading || balance === null || (isNativeCurrencyPriceFetching && nativeCurrencyPrice === 0)) {
+ return (
+
+ );
+ }
+
+ if (isError) {
+ return (
+
+ );
+ }
+
+ const formattedBalance = balance ? Number(formatEther(balance.value)) : 0;
+
+ return (
+
+
+ {displayUsdMode ? (
+ <>
+ $
+ {(formattedBalance * nativeCurrencyPrice).toFixed(2)}
+ >
+ ) : (
+ <>
+ {formattedBalance.toFixed(4)}
+ {targetNetwork.nativeCurrency.symbol}
+ >
+ )}
+
+
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/BridgeUSDCModal.tsx b/packages/nextjs/components/punk-society/ConfigMenu/BridgeUSDCModal.tsx
new file mode 100644
index 0000000..83b054a
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/BridgeUSDCModal.tsx
@@ -0,0 +1,47 @@
+import Image from "next/image";
+
+type BridgeUSDCModalProps = {
+ modalId: string;
+};
+
+export const BridgeUSDCModal = ({ modalId }: BridgeUSDCModalProps) => {
+ const handleBridgeUSDCClick = () => {
+ window.open("https://x.com/LuloxDev", "_blank");
+ };
+ return (
+ <>
+
+
+
+
+
+
+ Sorry, we can't bridge USDC from Avalanche yet.
+
+
+
+
+ Send me a DM and I'll get you started!
+
+
+ ✕
+
+
+
+
+
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/LoadPrivateKeyModal.tsx b/packages/nextjs/components/punk-society/ConfigMenu/LoadPrivateKeyModal.tsx
new file mode 100644
index 0000000..f4ca654
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/LoadPrivateKeyModal.tsx
@@ -0,0 +1,38 @@
+import Image from "next/image";
+
+type LoadPrivateKeyModalProps = {
+ modalId: string;
+};
+
+export const LoadPrivateKeyModal = ({ modalId }: LoadPrivateKeyModalProps) => {
+ const handleLoadPrivateKeyClick = () => {
+ window.open("https://core.app/es/", "_blank");
+ };
+ return (
+ <>
+
+
+
+
+
+
Sorry, we can't load private keys yet.
+
+
+ Import your key in Core!
+
+
+ ✕
+
+
+
+
+
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/NetworkOptions.tsx b/packages/nextjs/components/punk-society/ConfigMenu/NetworkOptions.tsx
new file mode 100644
index 0000000..bf16a56
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/NetworkOptions.tsx
@@ -0,0 +1,48 @@
+import { useTheme } from "next-themes";
+import { useAccount, useSwitchChain } from "wagmi";
+import { ArrowsRightLeftIcon } from "@heroicons/react/24/solid";
+import { getNetworkColor } from "~~/hooks/scaffold-eth";
+import { getTargetNetworks } from "~~/utils/scaffold-eth";
+
+const allowedNetworks = getTargetNetworks();
+
+type NetworkOptionsProps = {
+ hidden?: boolean;
+};
+
+export const NetworkOptions = ({ hidden = false }: NetworkOptionsProps) => {
+ const { switchChain } = useSwitchChain();
+ const { chain } = useAccount();
+ const { resolvedTheme } = useTheme();
+ const isDarkMode = resolvedTheme === "dark";
+
+ return (
+ <>
+ {allowedNetworks
+ .filter(allowedNetwork => allowedNetwork.id !== chain?.id)
+ .map(allowedNetwork => (
+
+ {
+ switchChain?.({ chainId: allowedNetwork.id });
+ }}
+ >
+
+
+ Switch to{" "}
+
+ {allowedNetwork.name}
+
+
+
+
+ ))}
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/PrivateKeyModal.tsx b/packages/nextjs/components/punk-society/ConfigMenu/PrivateKeyModal.tsx
new file mode 100644
index 0000000..2dfb57e
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/PrivateKeyModal.tsx
@@ -0,0 +1,64 @@
+import { useEffect, useState } from "react";
+import { notification } from "~~/utils/scaffold-eth";
+
+type AddressQRCodeModalProps = {
+ modalId: string;
+};
+
+export const PrivateKeyModal = ({ modalId }: AddressQRCodeModalProps) => {
+ const [privateKey, setPrivateKey] = useState(null);
+
+ useEffect(() => {
+ const storedPrivateKey = localStorage.getItem("burnerWallet.pk");
+ setPrivateKey(storedPrivateKey);
+ }, []);
+
+ const handleCopy = () => {
+ if (privateKey) {
+ navigator.clipboard
+ .writeText(privateKey)
+ .then(() => {
+ notification.success("Private key copied to clipboard");
+ // alert("Private key copied to clipboard");
+ })
+ .catch(err => {
+ notification.error("Failed to copy private key: ", err);
+ });
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+
+
Your Private Key
+
+ Save this into a safe place and don't share it with anyone.
+
+
+ This is the only way to recover your account and funds, and it can't be changed or retrieved by
+ PunkSociety team.
+
+
{privateKey}
+
+ Copy Private Key
+
+
+
+ ✕
+
+
+
+
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/SendUSDCModal.tsx b/packages/nextjs/components/punk-society/ConfigMenu/SendUSDCModal.tsx
new file mode 100644
index 0000000..5a6e3eb
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/SendUSDCModal.tsx
@@ -0,0 +1,41 @@
+import Image from "next/image";
+
+type SendUSDCModalProps = {
+ modalId: string;
+};
+
+export const SendUSDCModal = ({ modalId }: SendUSDCModalProps) => {
+ const handleSendUSDCClick = () => {
+ window.open("https://core.app/es/", "_blank");
+ };
+ return (
+ <>
+
+
+
+
+
+
Sorry, we can't send USDC yet.
+
+ Import your private key to Core Wallet and send them there
+
+
+
+ Send USDC with Core!
+
+
+ ✕
+
+
+
+
+
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/SwitchLanguageModal.tsx b/packages/nextjs/components/punk-society/ConfigMenu/SwitchLanguageModal.tsx
new file mode 100644
index 0000000..6cf1460
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/SwitchLanguageModal.tsx
@@ -0,0 +1,38 @@
+import Image from "next/image";
+
+type AddressQRCodeModalProps = {
+ modalId: string;
+};
+
+export const SwitchLanguageModal = ({ modalId }: AddressQRCodeModalProps) => {
+ const handleLearnEnglishClick = () => {
+ window.open("https://www.duolingo.com/", "_blank");
+ };
+ return (
+ <>
+
+
+
+
+
+
Sorry, we don't have other languages yet.
+
+
+ Learn English in Duolingo!
+
+
+ ✕
+
+
+
+
+
+ >
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/WrongNetworkDropdown.tsx b/packages/nextjs/components/punk-society/ConfigMenu/WrongNetworkDropdown.tsx
new file mode 100644
index 0000000..f9f0fd8
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/WrongNetworkDropdown.tsx
@@ -0,0 +1,32 @@
+import { NetworkOptions } from "./NetworkOptions";
+import { useDisconnect } from "wagmi";
+import { ArrowLeftOnRectangleIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
+
+export const WrongNetworkDropdown = () => {
+ const { disconnect } = useDisconnect();
+
+ return (
+
+
+ Wrong network
+
+
+
+
+
+ disconnect()}
+ >
+
+ Disconnect
+
+
+
+
+ );
+};
diff --git a/packages/nextjs/components/punk-society/ConfigMenu/index.tsx b/packages/nextjs/components/punk-society/ConfigMenu/index.tsx
new file mode 100644
index 0000000..3e8e43c
--- /dev/null
+++ b/packages/nextjs/components/punk-society/ConfigMenu/index.tsx
@@ -0,0 +1,75 @@
+"use client";
+
+// @refresh reset
+import { AddressInfoDropdown } from "./AddressInfoDropdown";
+import { AddressQRCodeModal } from "./AddressQRCodeModal";
+import { BridgeUSDCModal } from "./BridgeUSDCModal";
+import { LoadPrivateKeyModal } from "./LoadPrivateKeyModal";
+import { PrivateKeyModal } from "./PrivateKeyModal";
+import { SendUSDCModal } from "./SendUSDCModal";
+import { SwitchLanguageModal } from "./SwitchLanguageModal";
+import { WrongNetworkDropdown } from "./WrongNetworkDropdown";
+import { ConnectButton } from "@rainbow-me/rainbowkit";
+import { Address } from "viem";
+import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
+import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth";
+
+/**
+ * Custom Wagmi Connect Button (watch balance + custom design)
+ */
+export const ConfigMenu = () => {
+ const { targetNetwork } = useTargetNetwork();
+
+ return (
+
+ {({ account, chain, openConnectModal, mounted }) => {
+ const connected = mounted && account && chain;
+ const blockExplorerAddressLink = account
+ ? getBlockExplorerAddressLink(targetNetwork, account.address)
+ : undefined;
+
+ return (
+ <>
+ {(() => {
+ if (!connected) {
+ return (
+
+ Connect Wallet
+
+ );
+ }
+
+ if (chain.unsupported || chain.id !== targetNetwork.id) {
+ return ;
+ }
+
+ return (
+ <>
+
+ {/*
+
+
+ {chain.name}
+
+
*/}
+
+
+
+
+
+
+
+ >
+ );
+ })()}
+ >
+ );
+ }}
+
+ );
+};
diff --git a/packages/nextjs/components/punk-society/PunkBalance.tsx b/packages/nextjs/components/punk-society/PunkBalance.tsx
index 9716d8d..1a4f535 100644
--- a/packages/nextjs/components/punk-society/PunkBalance.tsx
+++ b/packages/nextjs/components/punk-society/PunkBalance.tsx
@@ -67,7 +67,7 @@ export const PunkBalance = ({ address, className = "", usdMode }: BalanceProps)
>
) : (
<>
- {formattedBalance.toFixed(6)}
+ {formattedBalance.toFixed(2)}
{targetNetwork.nativeCurrency.symbol}
>
)}
diff --git a/packages/nextjs/components/punk-society/PunkConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/punk-society/PunkConnectButton/AddressInfoDropdown.tsx
index 907c82b..10676c4 100644
--- a/packages/nextjs/components/punk-society/PunkConnectButton/AddressInfoDropdown.tsx
+++ b/packages/nextjs/components/punk-society/PunkConnectButton/AddressInfoDropdown.tsx
@@ -1,24 +1,16 @@
import { useRef, useState } from "react";
import Link from "next/link";
+import { PunkBalance } from "../PunkBalance";
import { NetworkOptions } from "./NetworkOptions";
import { FundButton, getOnrampBuyUrl } from "@coinbase/onchainkit/fund";
import { getAddress } from "viem";
import { Address } from "viem";
import { useAccount, useDisconnect } from "wagmi";
-import { KeyIcon as KeyIconOutline, UserIcon } from "@heroicons/react/24/outline";
-import {
- ArrowLeftOnRectangleIcon,
- ArrowTopRightOnSquareIcon,
- ArrowsRightLeftIcon,
- ChevronDownIcon,
- QrCodeIcon,
-} from "@heroicons/react/24/outline";
-import { ArrowUpLeftIcon, KeyIcon as KeyIconSolid, LanguageIcon, LinkIcon } from "@heroicons/react/24/solid";
+import { UserIcon } from "@heroicons/react/24/outline";
+import { ArrowLeftOnRectangleIcon, ChevronDownIcon, QrCodeIcon } from "@heroicons/react/24/outline";
+import { ArrowUpLeftIcon, LinkIcon } from "@heroicons/react/24/solid";
import { isENS } from "~~/components/scaffold-eth";
import { useOutsideClick, useScaffoldReadContract } from "~~/hooks/scaffold-eth";
-import { getTargetNetworks } from "~~/utils/scaffold-eth";
-
-const allowedNetworks = getTargetNetworks();
type AddressInfoDropdownProps = {
address: Address;
@@ -27,7 +19,7 @@ type AddressInfoDropdownProps = {
ensAvatar?: string;
};
-export const AddressInfoDropdown = ({ address, displayName, blockExplorerAddressLink }: AddressInfoDropdownProps) => {
+export const AddressInfoDropdown = ({ address, displayName }: AddressInfoDropdownProps) => {
const [selectingNetwork, setSelectingNetwork] = useState(false);
const { disconnect } = useDisconnect();
@@ -88,35 +80,8 @@ export const AddressInfoDropdown = ({ address, displayName, blockExplorerAddress
className="dropdown-content menu z-[2] p-2 mt-2 shadow-center shadow-accent bg-base-200 rounded-box gap-1"
>
- {/*
- {addressCopied ? (
-
-
- Copy address
-
- ) : (
- {
- setAddressCopied(true);
- setTimeout(() => {
- setAddressCopied(false);
- }, 800);
- }}
- >
-
-
- Copy address
-
-
- )}
- */}
+
+
@@ -163,52 +128,6 @@ export const AddressInfoDropdown = ({ address, displayName, blockExplorerAddress
/>
-
-
-
- View Private Key
-
-
-
-
-
- Load Private Key
-
-
- {allowedNetworks.length > 1 ? (
-
- {
- setSelectingNetwork(true);
- }}
- >
- Switch Network
-
-
- ) : null}
-
-
-
- Switch languages
-
-
-
-
-
-
-
- View on Block Explorer
-
-
-
-
{
return (
<>
- {/*
-
-
- {chain.name}
-
-
*/}
{
-
-
>
);
})()}