diff --git a/packages/react-app/src/AddressContext.ts b/packages/react-app/src/AddressContext.ts index c483f38d..3fe6ac3a 100644 --- a/packages/react-app/src/AddressContext.ts +++ b/packages/react-app/src/AddressContext.ts @@ -1,26 +1,16 @@ -import { BigNumber } from "@ethersproject/bignumber"; import { TokenBalance } from "./TokenBalance"; import { TokenKey } from "./tokens"; export type AddressContext = Readonly<{ - address: string; + address: `0x${string}`; tokenBalances: { [tk: TokenKey]: TokenBalance } }> // emptyAddressContext is a convenience and safety function to // construct an empty AddressContext. -export function emptyAddressContext(address: string): AddressContext { +export function emptyAddressContext(address: `0x${string}`): AddressContext { return { address, tokenBalances: {}, }; } - -// canAfford is a convenience predicate that returns true iff the -// passed address context can afford to transfer the passed amount of -// the token indicated by the passed token key. -export function canAfford(ac: AddressContext, tk: TokenKey, amountAsBigNumberHexString: string): boolean { - const tb = ac.tokenBalances[tk]; - if (tb === undefined) return false; - else return BigNumber.from(tb.balanceAsBigNumberHexString).gte(BigNumber.from(amountAsBigNumberHexString)); // NB here we are able to compare nominal amounts directly without worrying about units because the passed amount is denominated in the same token as the tokenbalance because both are for the passed token key -} diff --git a/packages/react-app/src/RenderLiveTokenBalance.tsx b/packages/react-app/src/RenderLiveTokenBalance.tsx index 43ab4a78..7140bd57 100644 --- a/packages/react-app/src/RenderLiveTokenBalance.tsx +++ b/packages/react-app/src/RenderLiveTokenBalance.tsx @@ -6,7 +6,7 @@ import { useLiveTokenBalance } from "./hooks/useLiveTokenBalance"; import { RenderRawTokenBalance } from "./RenderRawTokenBalance"; type RenderLiveTokenBalanceProps = { - address: string; // address whose token balance will be live-reloaded and rendered + address: `0x${string}`; // address whose token balance will be live-reloaded and rendered nativeCurrencyOrToken: NativeCurrency | Token; // native currency or token whose address balance will be live-reloaded and rendered }; export const RenderLiveTokenBalance: React.FC = ({ address, nativeCurrencyOrToken }) => { @@ -16,7 +16,7 @@ export const RenderLiveTokenBalance: React.FC = ({ } type RenderLiveNativeCurrencyBalanceProps = { - address: string; + address: `0x${string}`; nativeCurrency: NativeCurrency; }; const RenderLiveNativeCurrencyBalance: React.FC = ({ address, nativeCurrency }) => { @@ -30,7 +30,7 @@ const RenderLiveNativeCurrencyBalance: React.FC = ({ address, token }) => { diff --git a/packages/react-app/src/TokenBalance.ts b/packages/react-app/src/TokenBalance.ts index 519eed4f..cc79405d 100644 --- a/packages/react-app/src/TokenBalance.ts +++ b/packages/react-app/src/TokenBalance.ts @@ -6,7 +6,7 @@ import { getDecimalsToRenderForTokenTicker, getTokenByTokenKey, TokenKey } from // specific chain. For example, a snapshot of Bob's DAI balance on // Arbitrum. export type TokenBalance = Readonly<{ - address: string; // user address for this token balance + address: `0x${string}`; // user address for this token balance tokenKey: TokenKey; // TokenKey of the NativeCurrency or Token for this token balance balanceAsBigNumberHexString: string; // the actual token balance as a BigNumber.toHexString() balanceAsOf: number; // time at which this token balance was snapshotted in milliseconds since epoch diff --git a/packages/react-app/src/blockExplorerUrls.ts b/packages/react-app/src/blockExplorerUrls.ts index e9d85dc2..c1312141 100644 --- a/packages/react-app/src/blockExplorerUrls.ts +++ b/packages/react-app/src/blockExplorerUrls.ts @@ -9,7 +9,7 @@ export function getBlockExplorerUrlForTransaction(chainId: number | undefined, t } else return undefined; } -export function getBlockExplorerUrlForAddress(chainId: number | undefined, address: string): string | undefined { +export function getBlockExplorerUrlForAddress(chainId: number | undefined, address: `0x${string}`): string | undefined { const chain: Chain | undefined = getChain(chainId); const blockExplorerUrl: string | undefined = getBlockExplorerUrl(chain); if (blockExplorerUrl) { diff --git a/packages/react-app/src/hooks/useLiveNativeCurrencyBalance.ts b/packages/react-app/src/hooks/useLiveNativeCurrencyBalance.ts index b8eecc49..beb0acb6 100644 --- a/packages/react-app/src/hooks/useLiveNativeCurrencyBalance.ts +++ b/packages/react-app/src/hooks/useLiveNativeCurrencyBalance.ts @@ -1,21 +1,20 @@ import { BigNumber } from "@ethersproject/bignumber"; import { useMemo } from "react"; import { useBalance } from 'wagmi'; -import { makeAddress } from "../makeAddress"; import { useIsEnabledSmartRefresh } from "../useIsEnabledSmartRefresh"; // useLiveNativeCurrencyBalance is a React hook that returns a // live-reloaded ETH (or other native chain currency) balance for the // passed address and chainId. export function useLiveNativeCurrencyBalance( - address: string, // address whose native currency balance will be live-reloaded + address: `0x${string}`, // address whose native currency balance will be live-reloaded chainId: number, // chainId whose native currency balance will be live-reloaded ): BigNumber | undefined { const enabled = useIsEnabledSmartRefresh(); const args = useMemo(() => { return { enabled, // here, we are periodically toggling enabled to force a useBalance refresh. We explored solutions like `watch: true` and `staleTime/cacheTime`, and none of them worked for us. See 'redesign of live balance fetching' in 3cities design. Crucially, this toggle works because when useBalance is disabled, it continues returning the most recent result and doesn't return undefined, so the client is unaware that useBalance has been disabled. - address: makeAddress(address), + address, chainId, onError(error: Error) { console.error('useLiveNativeCurrencyBalance: error fetching balance with wagmi.useBalance, chainId', chainId, 'address', address, 'error', error); diff --git a/packages/react-app/src/hooks/useLiveTokenBalance.ts b/packages/react-app/src/hooks/useLiveTokenBalance.ts index f2c9fb8e..6f73fcc4 100644 --- a/packages/react-app/src/hooks/useLiveTokenBalance.ts +++ b/packages/react-app/src/hooks/useLiveTokenBalance.ts @@ -1,22 +1,21 @@ import { BigNumber } from "@ethersproject/bignumber"; import { useMemo } from "react"; import { useBalance } from 'wagmi'; -import { makeAddress } from "../makeAddress"; import { useIsEnabledSmartRefresh } from "../useIsEnabledSmartRefresh"; // useLiveTokenBalance is a React hook that returns a live-reloaded token // balance for the passed token contract address, address, and chainId. export function useLiveTokenBalance( - tokenContractAddress: string, // token contract address whose token balance will be live-reloaded - address: string, // address whose token balance will be live-reloaded + tokenContractAddress: `0x${string}`, // token contract address whose token balance will be live-reloaded + address: `0x${string}`, // address whose token balance will be live-reloaded chainId: number, // chainId on which tokenAddress contract resides ): BigNumber | undefined { const enabled = useIsEnabledSmartRefresh(); const args = useMemo(() => { return { enabled, // here, we are periodically toggling enabled to force a useBalance refresh. We explored solutions like `watch: true` and `staleTime/cacheTime`, and none of them worked for us. See 'redesign of live balance fetching' in 3cities design. Crucially, this toggle works because when useBalance is disabled, it continues returning the most recent result and doesn't return undefined, so the client is unaware that useBalance has been disabled. - token: makeAddress(tokenContractAddress), - address: makeAddress(address), + token: tokenContractAddress, + address, chainId, onError(error: Error) { console.error('useLiveTokenBalance: error fetching balance with wagmi.useBalance, chainId', chainId, 'token', tokenContractAddress, 'address', address, 'error', error); diff --git a/packages/react-app/src/tokenTransfer.ts b/packages/react-app/src/tokenTransfer.ts index 1ce6b7f4..0e959444 100644 --- a/packages/react-app/src/tokenTransfer.ts +++ b/packages/react-app/src/tokenTransfer.ts @@ -11,12 +11,12 @@ import { NativeCurrency, Token } from "./Token"; // isTokenAndNotNativeCurrencyTransfer and have the client output be a // local variable of type TokenTransferForToken else // TokenTransferForNativeCurrency. -type TokenTransferBase = { - toAddress: string; // address receiving this token transfer - fromAddress: string; // address sending this token transfer +type TokenTransferBase = Readonly<{ + receiverAddress: `0x${string}`; // address receiving this token transfer + senderAddress: `0x${string}`; // address sending this token transfer token: NativeCurrency | Token; // token or native currency being transferred amountAsBigNumberHexString: string; // amount of the transfer in full-precision units of the token (ie. the amount expressed in minimal units of the token based on its number of decimals, eg. wei for ETH, 10^-18 dollars for DAI, 10^-6 dollars for USDC, etc.) as a BigNumber.toHexString() -}; +}>; export type TokenTransferForToken = Narrow; // TokenTransferForToken is a convenience type for the subset of TokenTransfers where a token (and not a native currency) is being transferred. But where possible, clients should prefer using TokenTransfer to abstract over the type of transfer diff --git a/packages/react-app/src/truncateAddress.ts b/packages/react-app/src/truncateAddress.ts index e5fd0a4b..357838e8 100644 --- a/packages/react-app/src/truncateAddress.ts +++ b/packages/react-app/src/truncateAddress.ts @@ -9,7 +9,7 @@ export const truncateEthAddressVeryShort = truncateEthAddressInternal.bind(null, // truncateEthAddress truncates the passed Ethereum address. export const truncateEthAddress = truncateEthAddressInternal.bind(null, truncateRegex); -function truncateEthAddressInternal(regex: RegExp, address?: string, separator: string = '••••'): string { +function truncateEthAddressInternal(regex: RegExp, address?: `0x${string}`, separator: string = '••'): string { if (!address) return ''; else { const match = address.match(regex); diff --git a/packages/react-app/src/useEnsName.ts b/packages/react-app/src/useEnsName.ts index 7c8e658e..9a2e25fe 100644 --- a/packages/react-app/src/useEnsName.ts +++ b/packages/react-app/src/useEnsName.ts @@ -2,7 +2,7 @@ import { isAddress } from "@ethersproject/address"; import { useMemo } from "react"; import { goerli, mainnet, useEnsName as wagmiUseEnsName } from 'wagmi'; import { isProduction } from "./isProduction"; -import { truncateENSAddress } from "./truncateAddress"; +import { truncateEnsAddress } from "./truncateAddress"; import { useEnsAddress } from "./useEnsAddress"; import { useIsPageVisibleOrRecentlyVisible } from "./useIsPageVisibleOrRecentlyVisible"; @@ -97,7 +97,7 @@ export function useEnsName(address: `0x${string}` | undefined, opts?: Opts): { error: new Error(`useEnsName: forward resolution verification error`, { cause: useEnsAddressError }), isLoading: false, }; else if (typeof ensName === 'string' && ensName.length > 0 && (opts?.unsafeDisableForwardResolutionVerification || verificationSuccessful)) return { - ensName: opts?.truncate ? truncateENSAddress(ensName, opts.maxENSNameLength ?? 14) : ensName, + ensName: opts?.truncate ? truncateEnsAddress(ensName, opts.maxENSNameLength ?? 14) : ensName, isLoading: false, }; else if (wagmiUseEnsNameIsLoading || (!opts?.unsafeDisableForwardResolutionVerification && useEnsAddressIsLoading)) return { isLoading: true,