Skip to content

Commit

Permalink
Merge pull request #740 from scaffold-eth/cli-backmerge
Browse files Browse the repository at this point in the history
Cli backmerge
  • Loading branch information
carletex authored Feb 27, 2024
2 parents 15c21c0 + 8b55994 commit d7405f4
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 24 deletions.
10 changes: 10 additions & 0 deletions .changeset/lucky-tigers-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"create-eth": patch
---

1. basic example to show connected address (#721)
2. Standardize displaying of address and follow ERC-55 (#734)
3. fix contract balance hot reload balance issue (#739)
4. Fix cursor stealing & display loading for AddressInput (#738)
5. Fix blockexplorer code tab (#741)
6. Match link name with actual tab name for Debug Contracts (#743)
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const getContractData = async (address: string) => {
const deployedContractsOnChain = contracts ? contracts[chainId] : {};
for (const [contractName, contractInfo] of Object.entries(deployedContractsOnChain)) {
if (contractInfo.address.toLowerCase() === address) {
if (contractInfo.address.toLowerCase() === address.toLowerCase()) {
contractPath = \`contracts/\${contractName}.sol\`;
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

// @refresh reset
import { useReducer } from "react";
import { ContractReadMethods } from "./ContractReadMethods";
import { ContractVariables } from "./ContractVariables";
Expand Down
14 changes: 12 additions & 2 deletions templates/base/packages/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
"use client";

import Link from "next/link";
import type { NextPage } from "next";
import { useAccount } from "wagmi";
import { BugAntIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import { Address } from "~~/components/scaffold-eth";

const Home: NextPage = () => {
const { address: connectedAddress } = useAccount();

return (
<>
<div className="flex items-center flex-col flex-grow pt-10">
<div className="px-5">
<h1 className="text-center mb-8">
<h1 className="text-center">
<span className="block text-2xl mb-2">Welcome to</span>
<span className="block text-4xl font-bold">Scaffold-ETH 2</span>
</h1>
<div className="flex justify-center items-center space-x-2">
<p className="my-2 font-medium">Connected Address:</p>
<Address address={connectedAddress} />
</div>
<p className="text-center text-lg">
Get started by editing{" "}
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
Expand All @@ -36,7 +46,7 @@ const Home: NextPage = () => {
<p>
Tinker with your smart contract using the{" "}
<Link href="/debug" passHref className="link">
Debug Contract
Debug Contracts
</Link>{" "}
tab.
</p>
Expand Down
23 changes: 14 additions & 9 deletions templates/base/packages/nextjs/components/scaffold-eth/Address.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -35,10 +35,15 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
const [ens, setEns] = useState<string | null>();
const [ensAvatar, setEnsAvatar] = useState<string | null>();
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),
Expand All @@ -56,7 +61,7 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
}, [fetchedEnsAvatar]);

// Skeleton UI
if (!address) {
if (!checkSumAddress) {
return (
<div className="animate-pulse flex space-x-4">
<div className="rounded-md bg-slate-300 h-6 w-6"></div>
Expand All @@ -67,24 +72,24 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
);
}

if (!isAddress(address)) {
if (!isAddress(checkSumAddress)) {
return <span className="text-error">Wrong address</span>;
}

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 (
<div className="flex items-center">
<div className="flex-shrink-0">
<BlockieAvatar
address={address}
address={checkSumAddress}
ensImage={ensAvatar}
size={(blockieSizeMap[size] * 24) / blockieSizeMap["base"]}
/>
Expand Down Expand Up @@ -112,7 +117,7 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
/>
) : (
<CopyToClipboard
text={address}
text={checkSumAddress}
onCopy={() => {
setAddressCopied(true);
setTimeout(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -11,29 +11,39 @@ import { CommonInputProps, InputBase, isENS } from "~~/components/scaffold-eth";
export const AddressInput = ({ value, name, placeholder, onChange, disabled }: CommonInputProps<Address | string>) => {
// 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<string>();
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,
Expand All @@ -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 (
<InputBase<Address>
name={name}
Expand All @@ -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 ? (
<div className="flex bg-base-300 rounded-l-full items-center">
{isEnsAvtarLoading && <div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-full shrink-0"></div>}
{ensAvatar ? (
<span className="w-[35px]">
{
Expand All @@ -78,6 +98,13 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
) : null}
<span className="text-accent px-2">{enteredEnsName ?? ensName}</span>
</div>
) : (
(isEnsNameLoading || isEnsAddressLoading) && (
<div className="flex bg-base-300 rounded-l-full items-center gap-2 pr-2">
<div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-full shrink-0"></div>
<div className="skeleton bg-base-200 h-3 w-20"></div>
</div>
)
)
}
suffix={
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T> = CommonInputProps<T> & {
error?: boolean;
prefix?: ReactNode;
suffix?: ReactNode;
reFocus?: boolean;
};

export const InputBase = <T extends { toString: () => string } | undefined = string>({
Expand All @@ -16,7 +17,10 @@ export const InputBase = <T extends { toString: () => string } | undefined = str
disabled,
prefix,
suffix,
reFocus,
}: InputBaseProps<T>) => {
const inputReft = useRef<HTMLInputElement>(null);

let modifier = "";
if (error) {
modifier = "border-error";
Expand All @@ -31,6 +35,17 @@ export const InputBase = <T extends { toString: () => 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<HTMLInputElement, Element>) => {
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 (
<div className={`flex border-2 border-base-300 bg-base-200 rounded-full text-accent ${modifier}`}>
{prefix}
Expand All @@ -42,6 +57,8 @@ export const InputBase = <T extends { toString: () => string } | undefined = str
onChange={handleChange}
disabled={disabled}
autoComplete="off"
ref={inputReft}
onFocus={onFocus}
/>
{suffix}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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";

Expand All @@ -31,6 +32,7 @@ export const AddressInfoDropdown = ({
blockExplorerAddressLink,
}: AddressInfoDropdownProps) => {
const { disconnect } = useDisconnect();
const checkSumAddress = getAddress(address);

const [addressCopied, setAddressCopied] = useState(false);

Expand All @@ -46,8 +48,10 @@ export const AddressInfoDropdown = ({
<>
<details ref={dropdownRef} className="dropdown dropdown-end leading-3">
<summary tabIndex={0} className="btn btn-secondary btn-sm pl-0 pr-2 shadow-md dropdown-toggle gap-0 !h-auto">
<BlockieAvatar address={address} size={30} ensImage={ensAvatar} />
<span className="ml-2 mr-1">{displayName}</span>
<BlockieAvatar address={checkSumAddress} size={30} ensImage={ensAvatar} />
<span className="ml-2 mr-1">
{isENS(displayName) ? displayName : checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4)}
</span>
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
</summary>
<ul
Expand All @@ -66,7 +70,7 @@ export const AddressInfoDropdown = ({
</div>
) : (
<CopyToClipboard
text={address}
text={checkSumAddress}
onCopy={() => {
setAddressCopied(true);
setTimeout(() => {
Expand Down

0 comments on commit d7405f4

Please sign in to comment.