Balance:
diff --git a/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx b/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx
index 3148affdf..4c5ab2007 100644
--- a/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx
+++ b/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx
@@ -30,7 +30,7 @@ export const displayTxResult = (
if (typeof displayContent === "string") {
if (isAddress(displayContent)) {
- return
;
+ return
;
}
if (isHex(displayContent)) {
diff --git a/packages/nextjs/components/scaffold-eth/Address.tsx b/packages/nextjs/components/scaffold-eth/Address.tsx
deleted file mode 100644
index 6f299324c..000000000
--- a/packages/nextjs/components/scaffold-eth/Address.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-"use client";
-
-import { useEffect, useState } from "react";
-import Link from "next/link";
-import CopyToClipboard from "react-copy-to-clipboard";
-import { Address as AddressType, getAddress, isAddress } from "viem";
-import { hardhat } from "viem/chains";
-import { normalize } from "viem/ens";
-import { useEnsAvatar, useEnsName } from "wagmi";
-import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline";
-import { BlockieAvatar } from "~~/components/scaffold-eth";
-import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
-import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth";
-
-type AddressProps = {
- address?: AddressType;
- disableAddressLink?: boolean;
- format?: "short" | "long";
- size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl";
-};
-
-const blockieSizeMap = {
- xs: 6,
- sm: 7,
- base: 8,
- lg: 9,
- xl: 10,
- "2xl": 12,
- "3xl": 15,
-};
-
-/**
- * Displays an address (or ENS) with a Blockie image and option to copy address.
- */
-export const Address = ({ address, disableAddressLink, format, size = "base" }: AddressProps) => {
- const [ens, setEns] = useState
();
- const [ensAvatar, setEnsAvatar] = useState();
- const [addressCopied, setAddressCopied] = useState(false);
- const checkSumAddress = address ? getAddress(address) : undefined;
-
- const { targetNetwork } = useTargetNetwork();
-
- const { data: fetchedEns } = useEnsName({
- address: checkSumAddress,
- chainId: 1,
- query: {
- enabled: isAddress(checkSumAddress ?? ""),
- },
- });
- const { data: fetchedEnsAvatar } = useEnsAvatar({
- name: fetchedEns ? normalize(fetchedEns) : undefined,
- chainId: 1,
- query: {
- enabled: Boolean(fetchedEns),
- gcTime: 30_000,
- },
- });
-
- // We need to apply this pattern to avoid Hydration errors.
- useEffect(() => {
- setEns(fetchedEns);
- }, [fetchedEns]);
-
- useEffect(() => {
- setEnsAvatar(fetchedEnsAvatar);
- }, [fetchedEnsAvatar]);
-
- // Skeleton UI
- if (!checkSumAddress) {
- return (
-
- );
- }
-
- if (!isAddress(checkSumAddress)) {
- return Wrong address;
- }
-
- const blockExplorerAddressLink = getBlockExplorerAddressLink(targetNetwork, checkSumAddress);
- let displayAddress = checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4);
-
- if (ens) {
- displayAddress = ens;
- } else if (format === "long") {
- displayAddress = checkSumAddress;
- }
-
- return (
-
-
-
-
- {disableAddressLink ? (
-
{displayAddress}
- ) : targetNetwork.id === hardhat.id ? (
-
- {displayAddress}
-
- ) : (
-
- {displayAddress}
-
- )}
- {addressCopied ? (
-
- ) : (
-
{
- setAddressCopied(true);
- setTimeout(() => {
- setAddressCopied(false);
- }, 800);
- }}
- >
-
-
- )}
-
- );
-};
diff --git a/packages/nextjs/components/scaffold-eth/Address/Address.tsx b/packages/nextjs/components/scaffold-eth/Address/Address.tsx
new file mode 100644
index 000000000..28011d51b
--- /dev/null
+++ b/packages/nextjs/components/scaffold-eth/Address/Address.tsx
@@ -0,0 +1,187 @@
+"use client";
+
+import { AddressCopyIcon } from "./AddressCopyIcon";
+import { AddressLinkWrapper } from "./AddressLinkWrapper";
+import { Address as AddressType, getAddress, isAddress } from "viem";
+import { normalize } from "viem/ens";
+import { useEnsAvatar, useEnsName } from "wagmi";
+import { BlockieAvatar } from "~~/components/scaffold-eth";
+import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
+import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth";
+
+const textSizeMap = {
+ "3xs": "text-[10px]",
+ "2xs": "text-[11px]",
+ xs: "text-xs",
+ sm: "text-sm",
+ base: "text-base",
+ lg: "text-lg",
+ xl: "text-xl",
+ "2xl": "text-2xl",
+ "3xl": "text-3xl",
+ "4xl": "text-4xl",
+} as const;
+
+const blockieSizeMap = {
+ "3xs": 4,
+ "2xs": 5,
+ xs: 6,
+ sm: 7,
+ base: 8,
+ lg: 9,
+ xl: 10,
+ "2xl": 12,
+ "3xl": 15,
+ "4xl": 17,
+ "5xl": 19,
+ "6xl": 21,
+ "7xl": 23,
+} as const;
+
+const copyIconSizeMap = {
+ "3xs": "h-2.5 w-2.5",
+ "2xs": "h-3 w-3",
+ xs: "h-3.5 w-3.5",
+ sm: "h-4 w-4",
+ base: "h-[18px] w-[18px]",
+ lg: "h-5 w-5",
+ xl: "h-[22px] w-[22px]",
+ "2xl": "h-6 w-6",
+ "3xl": "h-[26px] w-[26px]",
+ "4xl": "h-7 w-7",
+} as const;
+
+type SizeMap = typeof textSizeMap | typeof blockieSizeMap;
+
+const getNextSize = (sizeMap: T, currentSize: keyof T, step = 1): keyof T => {
+ const sizes = Object.keys(sizeMap) as Array;
+ const currentIndex = sizes.indexOf(currentSize);
+ const nextIndex = Math.min(currentIndex + step, sizes.length - 1);
+ return sizes[nextIndex];
+};
+
+const getPrevSize = (sizeMap: T, currentSize: keyof T, step = 1): keyof T => {
+ const sizes = Object.keys(sizeMap) as Array;
+ const currentIndex = sizes.indexOf(currentSize);
+ const prevIndex = Math.max(currentIndex - step, 0);
+ return sizes[prevIndex];
+};
+
+type AddressProps = {
+ address?: AddressType;
+ disableAddressLink?: boolean;
+ format?: "short" | "long";
+ size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl";
+ onlyEnsOrAddress?: boolean;
+};
+
+export const Address = ({
+ address,
+ disableAddressLink,
+ format,
+ size = "base",
+ onlyEnsOrAddress = false,
+}: AddressProps) => {
+ const checkSumAddress = address ? getAddress(address) : undefined;
+
+ const { targetNetwork } = useTargetNetwork();
+
+ const { data: ens, isLoading: isEnsNameLoading } = useEnsName({
+ address: checkSumAddress,
+ chainId: 1,
+ query: {
+ enabled: isAddress(checkSumAddress ?? ""),
+ },
+ });
+ const { data: ensAvatar } = useEnsAvatar({
+ name: ens ? normalize(ens) : undefined,
+ chainId: 1,
+ query: {
+ enabled: Boolean(ens),
+ gcTime: 30_000,
+ },
+ });
+
+ const shortAddress = checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4);
+ const displayAddress = format === "long" ? checkSumAddress : shortAddress;
+ const displayEnsOrAddress = ens || displayAddress;
+
+ const showSkeleton = !checkSumAddress || (!onlyEnsOrAddress && (ens || isEnsNameLoading));
+
+ const addressSize = showSkeleton && !onlyEnsOrAddress ? getPrevSize(textSizeMap, size, 2) : size;
+ const ensSize = getNextSize(textSizeMap, addressSize);
+ const blockieSize = showSkeleton && !onlyEnsOrAddress ? getNextSize(blockieSizeMap, addressSize, 4) : addressSize;
+
+ if (!checkSumAddress) {
+ return (
+
+
+
+ {!onlyEnsOrAddress && (
+
+ 0x1234...56789
+
+ )}
+
+ 0x1234...56789
+
+
+
+ );
+ }
+
+ if (!isAddress(checkSumAddress)) {
+ return Wrong address;
+ }
+
+ const blockExplorerAddressLink = getBlockExplorerAddressLink(targetNetwork, checkSumAddress);
+
+ return (
+
+
+
+
+
+ {showSkeleton &&
+ (isEnsNameLoading ? (
+
+ {shortAddress}
+
+ ) : (
+
+
+ {ens}
+
+
+ ))}
+
+
+
+ {onlyEnsOrAddress ? displayEnsOrAddress : displayAddress}
+
+
+
+
+
+
+ );
+};
diff --git a/packages/nextjs/components/scaffold-eth/Address/AddressCopyIcon.tsx b/packages/nextjs/components/scaffold-eth/Address/AddressCopyIcon.tsx
new file mode 100644
index 000000000..48fbe1f0b
--- /dev/null
+++ b/packages/nextjs/components/scaffold-eth/Address/AddressCopyIcon.tsx
@@ -0,0 +1,26 @@
+import { useState } from "react";
+import CopyToClipboard from "react-copy-to-clipboard";
+import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline";
+
+export const AddressCopyIcon = ({ className, address }: { className?: string; address: string }) => {
+ const [addressCopied, setAddressCopied] = useState(false);
+ return (
+ {
+ setAddressCopied(true);
+ setTimeout(() => {
+ setAddressCopied(false);
+ }, 800);
+ }}
+ >
+
+
+ );
+};
diff --git a/packages/nextjs/components/scaffold-eth/Address/AddressLinkWrapper.tsx b/packages/nextjs/components/scaffold-eth/Address/AddressLinkWrapper.tsx
new file mode 100644
index 000000000..50e2ec0bb
--- /dev/null
+++ b/packages/nextjs/components/scaffold-eth/Address/AddressLinkWrapper.tsx
@@ -0,0 +1,29 @@
+import Link from "next/link";
+import { hardhat } from "viem/chains";
+import { useTargetNetwork } from "~~/hooks/scaffold-eth";
+
+type AddressLinkWrapperProps = {
+ children: React.ReactNode;
+ disableAddressLink?: boolean;
+ blockExplorerAddressLink: string;
+};
+
+export const AddressLinkWrapper = ({
+ children,
+ disableAddressLink,
+ blockExplorerAddressLink,
+}: AddressLinkWrapperProps) => {
+ const { targetNetwork } = useTargetNetwork();
+
+ return disableAddressLink ? (
+ <>{children}>
+ ) : (
+
+ {children}
+
+ );
+};
diff --git a/packages/nextjs/components/scaffold-eth/Faucet.tsx b/packages/nextjs/components/scaffold-eth/Faucet.tsx
index a55e7fb1f..569a923f1 100644
--- a/packages/nextjs/components/scaffold-eth/Faucet.tsx
+++ b/packages/nextjs/components/scaffold-eth/Faucet.tsx
@@ -98,7 +98,7 @@ export const Faucet = () => {
Available:
diff --git a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx
index b5bb2efb8..a46356b10 100644
--- a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx
+++ b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx
@@ -22,7 +22,7 @@ export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps
diff --git a/packages/nextjs/components/scaffold-eth/index.tsx b/packages/nextjs/components/scaffold-eth/index.tsx
index bf1e8a749..333cdf74b 100644
--- a/packages/nextjs/components/scaffold-eth/index.tsx
+++ b/packages/nextjs/components/scaffold-eth/index.tsx
@@ -1,4 +1,4 @@
-export * from "./Address";
+export * from "./Address/Address";
export * from "./Balance";
export * from "./BlockieAvatar";
export * from "./Faucet";