Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli backmerge #44

Merged
merged 12 commits into from
Jun 10, 2024
13 changes: 13 additions & 0 deletions .changeset/giant-wolves-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"create-eth": patch
---

fix: add decimals to native currency price (#854)

fix: add use effect on Balance component for the price (#856)

feat: bump burner-connector version (#857)

feat: useDisplayUsdMode hook (#859)

feat: up wagmi viem rainbowkit (scaffold-eth#862)
4 changes: 2 additions & 2 deletions templates/base/packages/nextjs/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useGlobalState } from "~~/services/store/store";
* Site footer
*/
export const Footer = () => {
const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrencyPrice);
const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price);
const { targetNetwork } = useTargetNetwork();
const isLocalNetwork = targetNetwork.id === hardhat.id;

Expand All @@ -26,7 +26,7 @@ export const Footer = () => {
<div>
<div className="btn btn-primary btn-sm font-normal gap-1 cursor-auto">
<CurrencyDollarIcon className="h-4 w-4" />
<span>{nativeCurrencyPrice}</span>
<span>{nativeCurrencyPrice.toFixed(2)}</span>
</div>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,12 @@ import { Footer } from "~~/components/Footer";
import { Header } from "~~/components/Header";
import { BlockieAvatar } from "~~/components/scaffold-eth";
import { ProgressBar } from "~~/components/scaffold-eth/ProgressBar";
import { useNativeCurrencyPrice } from "~~/hooks/scaffold-eth";
import { useGlobalState } from "~~/services/store/store";
import { useInitializeNativeCurrencyPrice } from "~~/hooks/scaffold-eth";
import { wagmiConfig } from "~~/services/web3/wagmiConfig";
${providerImports.filter(Boolean).join("\n")}

const ScaffoldEthApp = ({ children }: { children: React.ReactNode }) => {
const price = useNativeCurrencyPrice();
const setNativeCurrencyPrice = useGlobalState(state => state.setNativeCurrencyPrice);

useEffect(() => {
if (price > 0) {
setNativeCurrencyPrice(price);
}
}, [setNativeCurrencyPrice, price]);
useInitializeNativeCurrencyPrice();

return (
<>
Expand Down
20 changes: 8 additions & 12 deletions templates/base/packages/nextjs/components/scaffold-eth/Balance.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useState } from "react";
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";
Expand All @@ -17,7 +17,9 @@ type BalanceProps = {
*/
export const Balance = ({ address, className = "", usdMode }: BalanceProps) => {
const { targetNetwork } = useTargetNetwork();
const price = useGlobalState(state => state.nativeCurrencyPrice);
const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price);
const isNativeCurrencyPriceFetching = useGlobalState(state => state.nativeCurrency.isFetching);

const {
data: balance,
isError,
Expand All @@ -26,15 +28,9 @@ export const Balance = ({ address, className = "", usdMode }: BalanceProps) => {
address,
});

const [displayUsdMode, setDisplayUsdMode] = useState(price > 0 ? Boolean(usdMode) : false);

const toggleBalanceMode = () => {
if (price > 0) {
setDisplayUsdMode(prevMode => !prevMode);
}
};
const { displayUsdMode, toggleDisplayUsdMode } = useDisplayUsdMode({ defaultUsdMode: usdMode });

if (!address || isLoading || balance === null) {
if (!address || isLoading || balance === null || (isNativeCurrencyPriceFetching && nativeCurrencyPrice === 0)) {
return (
<div className="animate-pulse flex space-x-4">
<div className="rounded-md bg-slate-300 h-6 w-6"></div>
Expand All @@ -58,13 +54,13 @@ export const Balance = ({ address, className = "", usdMode }: BalanceProps) => {
return (
<button
className={`btn btn-sm btn-ghost flex flex-col font-normal items-center hover:bg-transparent ${className}`}
onClick={toggleBalanceMode}
onClick={toggleDisplayUsdMode}
>
<div className="w-full flex items-center justify-center">
{displayUsdMode ? (
<>
<span className="text-[0.8em] font-bold mr-1">$</span>
<span>{(formattedBalance * price).toFixed(2)}</span>
<span>{(formattedBalance * nativeCurrencyPrice).toFixed(2)}</span>
</>
) : (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { ArrowsRightLeftIcon } from "@heroicons/react/24/outline";
import { CommonInputProps, InputBase, SIGNED_NUMBER_REGEX } from "~~/components/scaffold-eth";
import { useDisplayUsdMode } from "~~/hooks/scaffold-eth/useDisplayUsdMode";
import { useGlobalState } from "~~/services/store/store";

const MAX_DECIMALS_USD = 2;
Expand Down Expand Up @@ -52,24 +53,22 @@ export const EtherInput = ({
usdMode,
}: CommonInputProps & { usdMode?: boolean }) => {
const [transitoryDisplayValue, setTransitoryDisplayValue] = useState<string>();
const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrencyPrice);
const [internalUsdMode, setInternalUSDMode] = useState(nativeCurrencyPrice > 0 ? Boolean(usdMode) : false);
const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price);
const isNativeCurrencyPriceFetching = useGlobalState(state => state.nativeCurrency.isFetching);

useEffect(() => {
setInternalUSDMode(nativeCurrencyPrice > 0 ? Boolean(usdMode) : false);
}, [usdMode, nativeCurrencyPrice]);
const { displayUsdMode, toggleDisplayUsdMode } = useDisplayUsdMode({ defaultUsdMode: usdMode });

// The displayValue is derived from the ether value that is controlled outside of the component
// In usdMode, it is converted to its usd value, in regular mode it is unaltered
const displayValue = useMemo(() => {
const newDisplayValue = etherValueToDisplayValue(internalUsdMode, value, nativeCurrencyPrice);
const newDisplayValue = etherValueToDisplayValue(displayUsdMode, value, nativeCurrencyPrice || 0);
if (transitoryDisplayValue && parseFloat(newDisplayValue) === parseFloat(transitoryDisplayValue)) {
return transitoryDisplayValue;
}
// Clear any transitory display values that might be set
setTransitoryDisplayValue(undefined);
return newDisplayValue;
}, [nativeCurrencyPrice, transitoryDisplayValue, internalUsdMode, value]);
}, [nativeCurrencyPrice, transitoryDisplayValue, displayUsdMode, value]);

const handleChangeNumber = (newValue: string) => {
if (newValue && !SIGNED_NUMBER_REGEX.test(newValue)) {
Expand All @@ -78,7 +77,7 @@ export const EtherInput = ({

// Following condition is a fix to prevent usdMode from experiencing different display values
// than what the user entered. This can happen due to floating point rounding errors that are introduced in the back and forth conversion
if (internalUsdMode) {
if (displayUsdMode) {
const decimals = newValue.split(".")[1];
if (decimals && decimals.length > MAX_DECIMALS_USD) {
return;
Expand All @@ -93,37 +92,31 @@ export const EtherInput = ({
setTransitoryDisplayValue(undefined);
}

const newEthValue = displayValueToEtherValue(internalUsdMode, newValue, nativeCurrencyPrice);
const newEthValue = displayValueToEtherValue(displayUsdMode, newValue, nativeCurrencyPrice || 0);
onChange(newEthValue);
};

const toggleMode = () => {
if (nativeCurrencyPrice > 0) {
setInternalUSDMode(!internalUsdMode);
}
};

return (
<InputBase
name={name}
value={displayValue}
placeholder={placeholder}
onChange={handleChangeNumber}
disabled={disabled}
prefix={<span className="pl-4 -mr-2 text-accent self-center">{internalUsdMode ? "$" : "Ξ"}</span>}
prefix={<span className="pl-4 -mr-2 text-accent self-center">{displayUsdMode ? "$" : "Ξ"}</span>}
suffix={
<div
className={`${
nativeCurrencyPrice > 0
? ""
: "tooltip tooltip-secondary before:content-[attr(data-tip)] before:right-[-10px] before:left-auto before:transform-none"
}`}
data-tip="Unable to fetch price"
data-tip={isNativeCurrencyPriceFetching ? "Fetching price" : "Unable to fetch price"}
>
<button
className="btn btn-primary h-[2.2rem] min-h-[2.2rem]"
onClick={toggleMode}
disabled={!internalUsdMode && !nativeCurrencyPrice}
onClick={toggleDisplayUsdMode}
disabled={!displayUsdMode && !nativeCurrencyPrice}
>
<ArrowsRightLeftIcon className="h-3 w-3 cursor-pointer" aria-hidden="true" />
</button>
Expand Down
2 changes: 1 addition & 1 deletion templates/base/packages/nextjs/hooks/scaffold-eth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export * from "./useBurnerWallet";
export * from "./useContractLogs";
export * from "./useDeployedContractInfo";
export * from "./useFetchBlocks";
export * from "./useNativeCurrencyPrice";
export * from "./useInitializeNativeCurrencyPrice";
export * from "./useNetworkColor";
export * from "./useOutsideClick";
export * from "./useScaffoldContract";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useCallback, useEffect, useState } from "react";
import { useGlobalState } from "~~/services/store/store";

export const useDisplayUsdMode = ({ defaultUsdMode = false }: { defaultUsdMode?: boolean }) => {
const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price);
const isPriceFetched = nativeCurrencyPrice > 0;
const predefinedUsdMode = isPriceFetched ? Boolean(defaultUsdMode) : false;
const [displayUsdMode, setDisplayUsdMode] = useState(predefinedUsdMode);

useEffect(() => {
setDisplayUsdMode(predefinedUsdMode);
}, [predefinedUsdMode]);

const toggleDisplayUsdMode = useCallback(() => {
if (isPriceFetched) {
setDisplayUsdMode(!displayUsdMode);
}
}, [displayUsdMode, isPriceFetched]);

return { displayUsdMode, toggleDisplayUsdMode };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useCallback, useEffect } from "react";
import { useTargetNetwork } from "./useTargetNetwork";
import { useInterval } from "usehooks-ts";
import scaffoldConfig from "~~/scaffold.config";
import { useGlobalState } from "~~/services/store/store";
import { fetchPriceFromUniswap } from "~~/utils/scaffold-eth";

const enablePolling = false;

/**
* Get the price of Native Currency based on Native Token/DAI trading pair from Uniswap SDK
*/
export const useInitializeNativeCurrencyPrice = () => {
const setNativeCurrencyPrice = useGlobalState(state => state.setNativeCurrencyPrice);
const setIsNativeCurrencyFetching = useGlobalState(state => state.setIsNativeCurrencyFetching);
const { targetNetwork } = useTargetNetwork();

const fetchPrice = useCallback(async () => {
setIsNativeCurrencyFetching(true);
const price = await fetchPriceFromUniswap(targetNetwork);
setNativeCurrencyPrice(price);
setIsNativeCurrencyFetching(false);
}, [setIsNativeCurrencyFetching, setNativeCurrencyPrice, targetNetwork]);

// Get the price of ETH from Uniswap on mount
useEffect(() => {
fetchPrice();
}, [fetchPrice]);

// Get the price of ETH from Uniswap at a given interval
useInterval(fetchPrice, enablePolling ? scaffoldConfig.pollingInterval : null);
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from "react";
import { useEffect, useMemo } from "react";
import { useAccount } from "wagmi";
import scaffoldConfig from "~~/scaffold.config";
import { useGlobalState } from "~~/services/store/store";
Expand All @@ -20,10 +20,13 @@ export function useTargetNetwork(): { targetNetwork: ChainWithAttributes } {
}
}, [chain?.id, setTargetNetwork, targetNetwork.id]);

return {
targetNetwork: {
...targetNetwork,
...NETWORKS_EXTRA_DATA[targetNetwork.id],
},
};
return useMemo(
() => ({
targetNetwork: {
...targetNetwork,
...NETWORKS_EXTRA_DATA[targetNetwork.id],
},
}),
[targetNetwork],
);
}
8 changes: 4 additions & 4 deletions templates/base/packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
},
"dependencies": {
"@heroicons/react": "~2.0.11",
"@rainbow-me/rainbowkit": "2.1.0",
"@rainbow-me/rainbowkit": "2.1.2",
"@tanstack/react-query": "~5.28.6",
"@uniswap/sdk-core": "~4.0.1",
"@uniswap/v2-sdk": "~3.0.1",
"blo": "~1.0.1",
"burner-connector": "~0.0.7",
"burner-connector": "~0.0.8",
"daisyui": "4.5.0",
"next": "~14.0.4",
"next-themes": "~0.2.1",
Expand All @@ -32,8 +32,8 @@
"react-hot-toast": "~2.4.0",
"use-debounce": "~8.0.4",
"usehooks-ts": "2.13.0",
"viem": "2.10.9",
"wagmi": "2.9.2",
"viem": "2.13.6",
"wagmi": "2.9.8",
"zustand": "~4.1.2"
},
"devDependencies": {
Expand Down
16 changes: 13 additions & 3 deletions templates/base/packages/nextjs/services/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,25 @@ import { ChainWithAttributes } from "~~/utils/scaffold-eth";
*/

type GlobalState = {
nativeCurrencyPrice: number;
nativeCurrency: {
price: number;
isFetching: boolean;
};
setNativeCurrencyPrice: (newNativeCurrencyPriceState: number) => void;
setIsNativeCurrencyFetching: (newIsNativeCurrencyFetching: boolean) => void;
targetNetwork: ChainWithAttributes;
setTargetNetwork: (newTargetNetwork: ChainWithAttributes) => void;
};

export const useGlobalState = create<GlobalState>(set => ({
nativeCurrencyPrice: 0,
setNativeCurrencyPrice: (newValue: number): void => set(() => ({ nativeCurrencyPrice: newValue })),
nativeCurrency: {
price: 0,
isFetching: true,
},
setNativeCurrencyPrice: (newValue: number): void =>
set(state => ({ nativeCurrency: { ...state.nativeCurrency, price: newValue } })),
setIsNativeCurrencyFetching: (newValue: boolean): void =>
set(state => ({ nativeCurrency: { ...state.nativeCurrency, isFetching: newValue } })),
targetNetwork: scaffoldConfig.targetNetworks[0],
setTargetNetwork: (newTargetNetwork: ChainWithAttributes) => set(() => ({ targetNetwork: newTargetNetwork })),
}));
Loading