-
+
Welcome to
Scaffold-ETH 2
+
Get started by editing{" "}
@@ -36,7 +46,7 @@ const Home: NextPage = () => {
Tinker with your smart contract using the{" "}
- Debug Contract
+ Debug Contracts
{" "}
tab.
diff --git a/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx b/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx
index bc8bfffa2..1c1ad600a 100644
--- a/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx
+++ b/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx
@@ -3,7 +3,7 @@
import { useEffect, useState } from "react";
import Link from "next/link";
import { CopyToClipboard } from "react-copy-to-clipboard";
-import { Address as AddressType, isAddress } from "viem";
+import { Address as AddressType, getAddress, isAddress } from "viem";
import { hardhat } from "viem/chains";
import { useEnsAvatar, useEnsName } from "wagmi";
import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline";
@@ -35,10 +35,15 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
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, enabled: isAddress(address ?? ""), chainId: 1 });
+ const { data: fetchedEns } = useEnsName({
+ address: checkSumAddress,
+ enabled: isAddress(checkSumAddress ?? ""),
+ chainId: 1,
+ });
const { data: fetchedEnsAvatar } = useEnsAvatar({
name: fetchedEns,
enabled: Boolean(fetchedEns),
@@ -56,7 +61,7 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
}, [fetchedEnsAvatar]);
// Skeleton UI
- if (!address) {
+ if (!checkSumAddress) {
return (
@@ -67,24 +72,24 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
);
}
- if (!isAddress(address)) {
+ if (!isAddress(checkSumAddress)) {
return
Wrong address;
}
- const blockExplorerAddressLink = getBlockExplorerAddressLink(targetNetwork, address);
- let displayAddress = address?.slice(0, 5) + "..." + address?.slice(-4);
+ const blockExplorerAddressLink = getBlockExplorerAddressLink(targetNetwork, checkSumAddress);
+ let displayAddress = checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4);
if (ens) {
displayAddress = ens;
} else if (format === "long") {
- displayAddress = address;
+ displayAddress = checkSumAddress;
}
return (
@@ -112,7 +117,7 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
/>
) : (
{
setAddressCopied(true);
setTimeout(() => {
diff --git a/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx b/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx
index 4f057015a..164664466 100644
--- a/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx
+++ b/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import { blo } from "blo";
-import { useDebounce } from "usehooks-ts";
+import { useDebounceValue } from "usehooks-ts";
import { Address, isAddress } from "viem";
import { useEnsAddress, useEnsAvatar, useEnsName } from "wagmi";
import { CommonInputProps, InputBase, isENS } from "~~/components/scaffold-eth";
@@ -11,29 +11,39 @@ import { CommonInputProps, InputBase, isENS } from "~~/components/scaffold-eth";
export const AddressInput = ({ value, name, placeholder, onChange, disabled }: CommonInputProps) => {
// Debounce the input to keep clean RPC calls when resolving ENS names
// If the input is an address, we don't need to debounce it
- const _debouncedValue = useDebounce(value, 500);
+ const [_debouncedValue] = useDebounceValue(value, 500);
const debouncedValue = isAddress(value) ? value : _debouncedValue;
const isDebouncedValueLive = debouncedValue === value;
// If the user changes the input after an ENS name is already resolved, we want to remove the stale result
const settledValue = isDebouncedValueLive ? debouncedValue : undefined;
- const { data: ensAddress, isLoading: isEnsAddressLoading } = useEnsAddress({
+ const {
+ data: ensAddress,
+ isLoading: isEnsAddressLoading,
+ isError: isEnsAddressError,
+ isSuccess: isEnsAddressSuccess,
+ } = useEnsAddress({
name: settledValue,
- enabled: isENS(debouncedValue),
+ enabled: isDebouncedValueLive && isENS(debouncedValue),
chainId: 1,
cacheTime: 30_000,
});
const [enteredEnsName, setEnteredEnsName] = useState();
- const { data: ensName, isLoading: isEnsNameLoading } = useEnsName({
+ const {
+ data: ensName,
+ isLoading: isEnsNameLoading,
+ isError: isEnsNameError,
+ isSuccess: isEnsNameSuccess,
+ } = useEnsName({
address: settledValue as Address,
enabled: isAddress(debouncedValue),
chainId: 1,
cacheTime: 30_000,
});
- const { data: ensAvatar } = useEnsAvatar({
+ const { data: ensAvatar, isLoading: isEnsAvtarLoading } = useEnsAvatar({
name: ensName,
enabled: Boolean(ensName),
chainId: 1,
@@ -57,6 +67,14 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
[onChange],
);
+ const reFocus =
+ isEnsAddressError ||
+ isEnsNameError ||
+ isEnsNameSuccess ||
+ isEnsAddressSuccess ||
+ ensName === null ||
+ ensAddress === null;
+
return (
name={name}
@@ -65,9 +83,11 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
value={value as Address}
onChange={handleChange}
disabled={isEnsAddressLoading || isEnsNameLoading || disabled}
+ reFocus={reFocus}
prefix={
- ensName && (
+ ensName ? (
+ {isEnsAvtarLoading &&
}
{ensAvatar ? (
{
@@ -78,6 +98,13 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
) : null}
{enteredEnsName ?? ensName}
+ ) : (
+ (isEnsNameLoading || isEnsAddressLoading) && (
+
+ )
)
}
suffix={
diff --git a/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx b/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx
index 73d5a4f8a..f38bca217 100644
--- a/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx
+++ b/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx
@@ -1,10 +1,11 @@
-import { ChangeEvent, ReactNode, useCallback } from "react";
+import { ChangeEvent, FocusEvent, ReactNode, useCallback, useEffect, useRef } from "react";
import { CommonInputProps } from "~~/components/scaffold-eth";
type InputBaseProps = CommonInputProps & {
error?: boolean;
prefix?: ReactNode;
suffix?: ReactNode;
+ reFocus?: boolean;
};
export const InputBase = string } | undefined = string>({
@@ -16,7 +17,10 @@ export const InputBase = string } | undefined = str
disabled,
prefix,
suffix,
+ reFocus,
}: InputBaseProps) => {
+ const inputReft = useRef(null);
+
let modifier = "";
if (error) {
modifier = "border-error";
@@ -31,6 +35,17 @@ export const InputBase = string } | undefined = str
[onChange],
);
+ // Runs only when reFocus prop is passed, usefull for setting the cursor
+ // at the end of the input. Example AddressInput
+ const onFocus = (e: FocusEvent) => {
+ if (reFocus !== undefined) {
+ e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length);
+ }
+ };
+ useEffect(() => {
+ if (reFocus !== undefined && reFocus === true) inputReft.current?.focus();
+ }, [reFocus]);
+
return (
{prefix}
@@ -42,6 +57,8 @@ export const InputBase = string } | undefined = str
onChange={handleChange}
disabled={disabled}
autoComplete="off"
+ ref={inputReft}
+ onFocus={onFocus}
/>
{suffix}
diff --git a/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx b/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
index 9112d93e3..b86128c9b 100644
--- a/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
+++ b/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
@@ -1,6 +1,7 @@
import { useRef, useState } from "react";
import { NetworkOptions } from "./NetworkOptions";
import CopyToClipboard from "react-copy-to-clipboard";
+import { getAddress } from "viem";
import { Address, useDisconnect } from "wagmi";
import {
ArrowLeftOnRectangleIcon,
@@ -11,7 +12,7 @@ import {
DocumentDuplicateIcon,
QrCodeIcon,
} from "@heroicons/react/24/outline";
-import { BlockieAvatar } from "~~/components/scaffold-eth";
+import { BlockieAvatar, isENS } from "~~/components/scaffold-eth";
import { useOutsideClick } from "~~/hooks/scaffold-eth";
import { getTargetNetworks } from "~~/utils/scaffold-eth";
@@ -31,6 +32,7 @@ export const AddressInfoDropdown = ({
blockExplorerAddressLink,
}: AddressInfoDropdownProps) => {
const { disconnect } = useDisconnect();
+ const checkSumAddress = getAddress(address);
const [addressCopied, setAddressCopied] = useState(false);
@@ -46,8 +48,10 @@ export const AddressInfoDropdown = ({
<>
-
- {displayName}
+
+
+ {isENS(displayName) ? displayName : checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4)}
+
) : (
{
setAddressCopied(true);
setTimeout(() => {