Skip to content

Commit d7405f4

Browse files
authored
Merge pull request #740 from scaffold-eth/cli-backmerge
Cli backmerge
2 parents 15c21c0 + 8b55994 commit d7405f4

File tree

8 files changed

+98
-24
lines changed

8 files changed

+98
-24
lines changed

.changeset/lucky-tigers-unite.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"create-eth": patch
3+
---
4+
5+
1. basic example to show connected address (#721)
6+
2. Standardize displaying of address and follow ERC-55 (#734)
7+
3. fix contract balance hot reload balance issue (#739)
8+
4. Fix cursor stealing & display loading for AddressInput (#738)
9+
5. Fix blockexplorer code tab (#741)
10+
6. Match link name with actual tab name for Debug Contracts (#743)

templates/base/packages/nextjs/app/blockexplorer/address/[address]/page.tsx.template.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const getContractData = async (address: string) => {
6363
6464
const deployedContractsOnChain = contracts ? contracts[chainId] : {};
6565
for (const [contractName, contractInfo] of Object.entries(deployedContractsOnChain)) {
66-
if (contractInfo.address.toLowerCase() === address) {
66+
if (contractInfo.address.toLowerCase() === address.toLowerCase()) {
6767
contractPath = \`contracts/\${contractName}.sol\`;
6868
break;
6969
}

templates/base/packages/nextjs/app/debug/_components/contract/ContractUI.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
// @refresh reset
34
import { useReducer } from "react";
45
import { ContractReadMethods } from "./ContractReadMethods";
56
import { ContractVariables } from "./ContractVariables";

templates/base/packages/nextjs/app/page.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1+
"use client";
2+
13
import Link from "next/link";
24
import type { NextPage } from "next";
5+
import { useAccount } from "wagmi";
36
import { BugAntIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
7+
import { Address } from "~~/components/scaffold-eth";
48

59
const Home: NextPage = () => {
10+
const { address: connectedAddress } = useAccount();
11+
612
return (
713
<>
814
<div className="flex items-center flex-col flex-grow pt-10">
915
<div className="px-5">
10-
<h1 className="text-center mb-8">
16+
<h1 className="text-center">
1117
<span className="block text-2xl mb-2">Welcome to</span>
1218
<span className="block text-4xl font-bold">Scaffold-ETH 2</span>
1319
</h1>
20+
<div className="flex justify-center items-center space-x-2">
21+
<p className="my-2 font-medium">Connected Address:</p>
22+
<Address address={connectedAddress} />
23+
</div>
1424
<p className="text-center text-lg">
1525
Get started by editing{" "}
1626
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
@@ -36,7 +46,7 @@ const Home: NextPage = () => {
3646
<p>
3747
Tinker with your smart contract using the{" "}
3848
<Link href="/debug" passHref className="link">
39-
Debug Contract
49+
Debug Contracts
4050
</Link>{" "}
4151
tab.
4252
</p>

templates/base/packages/nextjs/components/scaffold-eth/Address.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useEffect, useState } from "react";
44
import Link from "next/link";
55
import { CopyToClipboard } from "react-copy-to-clipboard";
6-
import { Address as AddressType, isAddress } from "viem";
6+
import { Address as AddressType, getAddress, isAddress } from "viem";
77
import { hardhat } from "viem/chains";
88
import { useEnsAvatar, useEnsName } from "wagmi";
99
import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline";
@@ -35,10 +35,15 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
3535
const [ens, setEns] = useState<string | null>();
3636
const [ensAvatar, setEnsAvatar] = useState<string | null>();
3737
const [addressCopied, setAddressCopied] = useState(false);
38+
const checkSumAddress = address ? getAddress(address) : undefined;
3839

3940
const { targetNetwork } = useTargetNetwork();
4041

41-
const { data: fetchedEns } = useEnsName({ address, enabled: isAddress(address ?? ""), chainId: 1 });
42+
const { data: fetchedEns } = useEnsName({
43+
address: checkSumAddress,
44+
enabled: isAddress(checkSumAddress ?? ""),
45+
chainId: 1,
46+
});
4247
const { data: fetchedEnsAvatar } = useEnsAvatar({
4348
name: fetchedEns,
4449
enabled: Boolean(fetchedEns),
@@ -56,7 +61,7 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
5661
}, [fetchedEnsAvatar]);
5762

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

70-
if (!isAddress(address)) {
75+
if (!isAddress(checkSumAddress)) {
7176
return <span className="text-error">Wrong address</span>;
7277
}
7378

74-
const blockExplorerAddressLink = getBlockExplorerAddressLink(targetNetwork, address);
75-
let displayAddress = address?.slice(0, 5) + "..." + address?.slice(-4);
79+
const blockExplorerAddressLink = getBlockExplorerAddressLink(targetNetwork, checkSumAddress);
80+
let displayAddress = checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4);
7681

7782
if (ens) {
7883
displayAddress = ens;
7984
} else if (format === "long") {
80-
displayAddress = address;
85+
displayAddress = checkSumAddress;
8186
}
8287

8388
return (
8489
<div className="flex items-center">
8590
<div className="flex-shrink-0">
8691
<BlockieAvatar
87-
address={address}
92+
address={checkSumAddress}
8893
ensImage={ensAvatar}
8994
size={(blockieSizeMap[size] * 24) / blockieSizeMap["base"]}
9095
/>
@@ -112,7 +117,7 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:
112117
/>
113118
) : (
114119
<CopyToClipboard
115-
text={address}
120+
text={checkSumAddress}
116121
onCopy={() => {
117122
setAddressCopied(true);
118123
setTimeout(() => {

templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, useEffect, useState } from "react";
22
import { blo } from "blo";
3-
import { useDebounce } from "usehooks-ts";
3+
import { useDebounceValue } from "usehooks-ts";
44
import { Address, isAddress } from "viem";
55
import { useEnsAddress, useEnsAvatar, useEnsName } from "wagmi";
66
import { CommonInputProps, InputBase, isENS } from "~~/components/scaffold-eth";
@@ -11,29 +11,39 @@ import { CommonInputProps, InputBase, isENS } from "~~/components/scaffold-eth";
1111
export const AddressInput = ({ value, name, placeholder, onChange, disabled }: CommonInputProps<Address | string>) => {
1212
// Debounce the input to keep clean RPC calls when resolving ENS names
1313
// If the input is an address, we don't need to debounce it
14-
const _debouncedValue = useDebounce(value, 500);
14+
const [_debouncedValue] = useDebounceValue(value, 500);
1515
const debouncedValue = isAddress(value) ? value : _debouncedValue;
1616
const isDebouncedValueLive = debouncedValue === value;
1717

1818
// If the user changes the input after an ENS name is already resolved, we want to remove the stale result
1919
const settledValue = isDebouncedValueLive ? debouncedValue : undefined;
2020

21-
const { data: ensAddress, isLoading: isEnsAddressLoading } = useEnsAddress({
21+
const {
22+
data: ensAddress,
23+
isLoading: isEnsAddressLoading,
24+
isError: isEnsAddressError,
25+
isSuccess: isEnsAddressSuccess,
26+
} = useEnsAddress({
2227
name: settledValue,
23-
enabled: isENS(debouncedValue),
28+
enabled: isDebouncedValueLive && isENS(debouncedValue),
2429
chainId: 1,
2530
cacheTime: 30_000,
2631
});
2732

2833
const [enteredEnsName, setEnteredEnsName] = useState<string>();
29-
const { data: ensName, isLoading: isEnsNameLoading } = useEnsName({
34+
const {
35+
data: ensName,
36+
isLoading: isEnsNameLoading,
37+
isError: isEnsNameError,
38+
isSuccess: isEnsNameSuccess,
39+
} = useEnsName({
3040
address: settledValue as Address,
3141
enabled: isAddress(debouncedValue),
3242
chainId: 1,
3343
cacheTime: 30_000,
3444
});
3545

36-
const { data: ensAvatar } = useEnsAvatar({
46+
const { data: ensAvatar, isLoading: isEnsAvtarLoading } = useEnsAvatar({
3747
name: ensName,
3848
enabled: Boolean(ensName),
3949
chainId: 1,
@@ -57,6 +67,14 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
5767
[onChange],
5868
);
5969

70+
const reFocus =
71+
isEnsAddressError ||
72+
isEnsNameError ||
73+
isEnsNameSuccess ||
74+
isEnsAddressSuccess ||
75+
ensName === null ||
76+
ensAddress === null;
77+
6078
return (
6179
<InputBase<Address>
6280
name={name}
@@ -65,9 +83,11 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
6583
value={value as Address}
6684
onChange={handleChange}
6785
disabled={isEnsAddressLoading || isEnsNameLoading || disabled}
86+
reFocus={reFocus}
6887
prefix={
69-
ensName && (
88+
ensName ? (
7089
<div className="flex bg-base-300 rounded-l-full items-center">
90+
{isEnsAvtarLoading && <div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-full shrink-0"></div>}
7191
{ensAvatar ? (
7292
<span className="w-[35px]">
7393
{
@@ -78,6 +98,13 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
7898
) : null}
7999
<span className="text-accent px-2">{enteredEnsName ?? ensName}</span>
80100
</div>
101+
) : (
102+
(isEnsNameLoading || isEnsAddressLoading) && (
103+
<div className="flex bg-base-300 rounded-l-full items-center gap-2 pr-2">
104+
<div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-full shrink-0"></div>
105+
<div className="skeleton bg-base-200 h-3 w-20"></div>
106+
</div>
107+
)
81108
)
82109
}
83110
suffix={

templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { ChangeEvent, ReactNode, useCallback } from "react";
1+
import { ChangeEvent, FocusEvent, ReactNode, useCallback, useEffect, useRef } from "react";
22
import { CommonInputProps } from "~~/components/scaffold-eth";
33

44
type InputBaseProps<T> = CommonInputProps<T> & {
55
error?: boolean;
66
prefix?: ReactNode;
77
suffix?: ReactNode;
8+
reFocus?: boolean;
89
};
910

1011
export const InputBase = <T extends { toString: () => string } | undefined = string>({
@@ -16,7 +17,10 @@ export const InputBase = <T extends { toString: () => string } | undefined = str
1617
disabled,
1718
prefix,
1819
suffix,
20+
reFocus,
1921
}: InputBaseProps<T>) => {
22+
const inputReft = useRef<HTMLInputElement>(null);
23+
2024
let modifier = "";
2125
if (error) {
2226
modifier = "border-error";
@@ -31,6 +35,17 @@ export const InputBase = <T extends { toString: () => string } | undefined = str
3135
[onChange],
3236
);
3337

38+
// Runs only when reFocus prop is passed, usefull for setting the cursor
39+
// at the end of the input. Example AddressInput
40+
const onFocus = (e: FocusEvent<HTMLInputElement, Element>) => {
41+
if (reFocus !== undefined) {
42+
e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length);
43+
}
44+
};
45+
useEffect(() => {
46+
if (reFocus !== undefined && reFocus === true) inputReft.current?.focus();
47+
}, [reFocus]);
48+
3449
return (
3550
<div className={`flex border-2 border-base-300 bg-base-200 rounded-full text-accent ${modifier}`}>
3651
{prefix}
@@ -42,6 +57,8 @@ export const InputBase = <T extends { toString: () => string } | undefined = str
4257
onChange={handleChange}
4358
disabled={disabled}
4459
autoComplete="off"
60+
ref={inputReft}
61+
onFocus={onFocus}
4562
/>
4663
{suffix}
4764
</div>

templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useRef, useState } from "react";
22
import { NetworkOptions } from "./NetworkOptions";
33
import CopyToClipboard from "react-copy-to-clipboard";
4+
import { getAddress } from "viem";
45
import { Address, useDisconnect } from "wagmi";
56
import {
67
ArrowLeftOnRectangleIcon,
@@ -11,7 +12,7 @@ import {
1112
DocumentDuplicateIcon,
1213
QrCodeIcon,
1314
} from "@heroicons/react/24/outline";
14-
import { BlockieAvatar } from "~~/components/scaffold-eth";
15+
import { BlockieAvatar, isENS } from "~~/components/scaffold-eth";
1516
import { useOutsideClick } from "~~/hooks/scaffold-eth";
1617
import { getTargetNetworks } from "~~/utils/scaffold-eth";
1718

@@ -31,6 +32,7 @@ export const AddressInfoDropdown = ({
3132
blockExplorerAddressLink,
3233
}: AddressInfoDropdownProps) => {
3334
const { disconnect } = useDisconnect();
35+
const checkSumAddress = getAddress(address);
3436

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

@@ -46,8 +48,10 @@ export const AddressInfoDropdown = ({
4648
<>
4749
<details ref={dropdownRef} className="dropdown dropdown-end leading-3">
4850
<summary tabIndex={0} className="btn btn-secondary btn-sm pl-0 pr-2 shadow-md dropdown-toggle gap-0 !h-auto">
49-
<BlockieAvatar address={address} size={30} ensImage={ensAvatar} />
50-
<span className="ml-2 mr-1">{displayName}</span>
51+
<BlockieAvatar address={checkSumAddress} size={30} ensImage={ensAvatar} />
52+
<span className="ml-2 mr-1">
53+
{isENS(displayName) ? displayName : checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4)}
54+
</span>
5155
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
5256
</summary>
5357
<ul
@@ -66,7 +70,7 @@ export const AddressInfoDropdown = ({
6670
</div>
6771
) : (
6872
<CopyToClipboard
69-
text={address}
73+
text={checkSumAddress}
7074
onCopy={() => {
7175
setAddressCopied(true);
7276
setTimeout(() => {

0 commit comments

Comments
 (0)