Skip to content

Commit

Permalink
send amount input
Browse files Browse the repository at this point in the history
  • Loading branch information
brendan-defi committed Jan 24, 2025
1 parent 9b8fe0f commit 09b5880
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 75 deletions.
56 changes: 56 additions & 0 deletions src/core-react/internal/hooks/useExchangeRate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { getSwapQuote } from '@/api';
import { isApiError } from '@/core/utils/isApiResponseError';
import type { Token } from '@/token';
import { usdcToken } from '@/token/constants';
import type { Dispatch, SetStateAction } from 'react';

type UseExchangeRateParams = {
token: Token;
selectedInputType: 'crypto' | 'fiat';
setExchangeRate: Dispatch<SetStateAction<number>>;
setExchangeRateLoading: Dispatch<SetStateAction<boolean>>;
};

export async function useExchangeRate({
token,
selectedInputType,
setExchangeRate,
setExchangeRateLoading,
}: UseExchangeRateParams) {
console.log('useing exchange rate hook');
if (!token) {
return;
}

if (token.address === usdcToken.address) {
setExchangeRate(1);
return;
}

setExchangeRateLoading(true);

const fromToken = selectedInputType === 'crypto' ? token : usdcToken;
const toToken = selectedInputType === 'crypto' ? usdcToken : token;

try {
const response = await getSwapQuote({
amount: '1', // hardcoded amount because we only need the exchange rate
from: fromToken,
to: toToken,
useAggregator: false,
});
if (isApiError(response)) {
console.error('Error fetching exchange rate:', response.error);
return;
}
const rate =
selectedInputType === 'crypto'
? 1 / Number(response.fromAmountUSD)
: Number(response.toAmount) / 10 ** response.to.decimals;
setExchangeRate(rate);
} catch (error) {
console.error('Uncaught error fetching exchange rate:', error);
} finally {
setExchangeRateLoading(false);
}
}
16 changes: 8 additions & 8 deletions src/internal/components/AmountInput/AmountInputTypeSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type AmountInputTypeSwitchPropsReact = {
selectedInputType: 'fiat' | 'crypto';
setSelectedInputType: (type: 'fiat' | 'crypto') => void;
asset: string;
fundAmountFiat: string;
fundAmountCrypto: string;
fiatAmount: string;
cryptoAmount: string;
exchangeRate: number;
exchangeRateLoading: boolean;
currency: string;
Expand All @@ -21,8 +21,8 @@ export function AmountInputTypeSwitch({
selectedInputType,
setSelectedInputType,
asset,
fundAmountFiat,
fundAmountCrypto,
fiatAmount,
cryptoAmount,
exchangeRate,
exchangeRateLoading,
currency,
Expand All @@ -45,17 +45,17 @@ export function AmountInputTypeSwitch({
return (
<span data-testid="ockAmountLine" className={cn(text.label1)}>
{selectedInputType === 'fiat'
? formatCrypto(fundAmountCrypto)
? formatCrypto(cryptoAmount)
: formatFiatAmount({
amount: fundAmountFiat,
amount: fiatAmount,
currency: currency,
minimumFractionDigits: 0,
})}
</span>
);
}, [
fundAmountCrypto,
fundAmountFiat,
cryptoAmount,
fiatAmount,
selectedInputType,
formatCrypto,
currency,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@ import { TextInput } from '@/internal/components/TextInput';
import { background, border, cn, color } from '@/styles/theme';
import type { Dispatch, SetStateAction } from 'react';

/***
delayMs?: number;
disabled?: boolean;
onBlur?: () => void;
inputValidator?: (s: string) => boolean;
};
*/

type AddressInputProps = {
addressInput: string | null;
setAddressInput: Dispatch<SetStateAction<string | null>>;
Expand All @@ -36,9 +28,9 @@ export function AddressInput({
<TextInput
inputMode="text"
placeholder="Basename, ENS, or Address"
aria-label="Input Receiver Address"
value={addressInput ?? ''}
onChange={setAddressInput}
aria-label="Input Receiver Address"
className={cn(background.default, 'w-full outline-none')}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { Address, Avatar, Identity, Name } from '@/identity';
import { useSendContext } from '@/wallet/components/WalletAdvancedSend/components/SendProvider';
import { background, border, cn, pressable } from '@/styles/theme';
import { useCallback } from 'react';
import type { Address as AddressType } from 'viem';

export function AddressSelector() {
const { senderChain, validatedRecipientAddress, handleAddressSelection } =
useSendContext();
type AddressSelectorProps = {
address: AddressType;
};

if (!validatedRecipientAddress) {
return null;
}
export function AddressSelector({ address }: AddressSelectorProps) {
const { senderChain, handleAddressSelection } = useSendContext();

const handleClick = useCallback(() => {
handleAddressSelection(address);
}, [handleAddressSelection, address]);

return (
<button
type="button"
onClick={() => handleAddressSelection(validatedRecipientAddress)}
className="text-left"
>
<button type="button" onClick={handleClick} className="text-left">
<Identity
address={validatedRecipientAddress}
address={address}
chain={senderChain || undefined}
hasCopyAddressOnClick={false}
className={cn(
Expand Down
5 changes: 4 additions & 1 deletion src/wallet/components/WalletAdvancedSend/components/Send.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ function SendDefaultChildren() {
addressInput={context.recipientInput}
setAddressInput={context.setRecipientInput}
/>
<AddressSelector />
{context.validatedRecipientAddress && (
<AddressSelector address={context.validatedRecipientAddress} />
)}
</>
);
}
Expand All @@ -99,6 +101,7 @@ function SendDefaultChildren() {
context.selectedToken,
context.recipientInput,
context.setRecipientInput,
context.validatedRecipientAddress,
]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ import { AmountInput } from '@/internal/components/AmountInput/AmountInput';
import { AmountInputTypeSwitch } from '@/internal/components/AmountInput/AmountInputTypeSwitch';
// import { cn } from '@/styles/theme';
import { useSendContext } from '@/wallet/components/WalletAdvancedSend/components/SendProvider';
import { useState } from 'react';

export function SendAmountInput({ className }: { className?: string }) {
const [selectedInputType, setSelectedInputType] = useState<'fiat' | 'crypto'>(
'crypto',
);
const {
fiatAmount,
cryptoAmount,
selectedToken,
setFiatAmount,
setCryptoAmount,
exchangeRate,
selectedInputType,
setSelectedInputType,
} = useSendContext();

return (
Expand All @@ -32,8 +30,8 @@ export function SendAmountInput({ className }: { className?: string }) {
/>
<AmountInputTypeSwitch
asset={selectedToken?.symbol ?? ''}
fundAmountFiat={fiatAmount ?? ''}
fundAmountCrypto={cryptoAmount ?? ''}
fiatAmount={fiatAmount ?? ''}
cryptoAmount={cryptoAmount ?? ''}
exchangeRate={exchangeRate}
exchangeRateLoading={false}
currency={'USD'}
Expand Down
113 changes: 74 additions & 39 deletions src/wallet/components/WalletAdvancedSend/components/SendProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import { useLifecycleStatus } from '@/core-react/internal/hooks/useLifecycleStat
import type { Token } from '@/token';
import { useWalletContext } from '@/wallet/components/WalletProvider';
import { useWalletAdvancedContext } from '@/wallet/components/WalletAdvancedProvider';
import { getSwapQuote } from '@/api';
import { usdcToken } from '@/token/constants';
import { isApiError } from '@/core/utils/isApiResponseError';
import { useExchangeRate } from '@/core-react/internal/hooks/useExchangeRate';

type SendContextType = {
lifecycleStatus: LifecycleStatus;
Expand All @@ -41,6 +45,8 @@ type SendContextType = {
setExchangeRate: Dispatch<SetStateAction<number>>;
exchangeRateLoading: boolean;
setExchangeRateLoading: Dispatch<SetStateAction<boolean>>;
selectedInputType: 'fiat' | 'crypto';
setSelectedInputType: Dispatch<SetStateAction<'fiat' | 'crypto'>>;
};

type SendProviderReact = {
Expand Down Expand Up @@ -123,12 +129,14 @@ export function SendProvider({ children }: SendProviderReact) {
const [selectedRecipientAddress, setSelectedRecipientAddress] =
useState<Address | null>(null);
const [selectedToken, setSelectedToken] = useState<Token | null>(null);
const [selectedInputType, setSelectedInputType] = useState<'fiat' | 'crypto'>(
'crypto',
);
const [fiatAmount, setFiatAmount] = useState<string | null>(null);
const [cryptoAmount, setCryptoAmount] = useState<string | null>(null);
const [exchangeRate, setExchangeRate] = useState<number>(100);
const [exchangeRateLoading, setExchangeRateLoading] = useState<boolean>(true);

// TODO FETCH EXCHANGE RATE
const [exchangeRate, setExchangeRate] = useState<number>(0);
const [exchangeRateLoading, setExchangeRateLoading] =
useState<boolean>(false);

const [lifecycleStatus, updateLifecycleStatus] =
useLifecycleStatus<LifecycleStatus>({
Expand All @@ -141,44 +149,16 @@ export function SendProvider({ children }: SendProviderReact) {
const { address: senderAddress, chain: senderChain } = useWalletContext();
const { tokenBalances } = useWalletAdvancedContext();


useEffect(() => {
if (lifecycleStatus.statusName === 'init') {
if (senderAddress) {
updateLifecycleStatus({
statusName: 'selectingAddress',
statusData: {
isMissingRequiredField: true,
},
});
}
const ethBalance = tokenBalances?.find((token) => token.address === '');
if (ethBalance && ethBalance.cryptoBalance > 0) {
setEthBalance(
Number(ethBalance.cryptoBalance / 10 ** ethBalance.decimals),
);
}
}, [senderAddress, lifecycleStatus, updateLifecycleStatus]);
}, [tokenBalances]);

// Set Lifecycle Status after fetching token balances
useEffect(() => {
const ethBalance = tokenBalances?.filter(
(token) => token.symbol === 'ETH',
)[0];
if (!ethBalance || ethBalance.cryptoBalance === 0) {
updateLifecycleStatus({
statusName: 'fundingWallet',
statusData: {
isMissingRequiredField: true,
},
});
} else if (ethBalance.cryptoBalance > 0) {
setEthBalance(ethBalance.cryptoBalance);
updateLifecycleStatus({
statusName: 'selectingAddress',
statusData: {
isMissingRequiredField: true,
},
});
}
}, [tokenBalances, updateLifecycleStatus]);

// Validate Recipient Input
// Validate recipient input and set validated recipient address
useEffect(() => {
async function validateRecipientInput() {
if (recipientInput) {
Expand Down Expand Up @@ -219,6 +199,59 @@ export function SendProvider({ children }: SendProviderReact) {
[updateLifecycleStatus],
);

// const fetchExchangeRate = useCallback(async () => {
// if (!selectedToken) {
// return;
// }

// if (selectedToken.address === usdcToken.address) {
// setExchangeRate(1);
// return;
// }

// setExchangeRateLoading(true);

// const fromToken =
// selectedInputType === 'crypto' ? selectedToken : usdcToken;
// const toToken =
// selectedInputType === 'crypto' ? usdcToken : selectedToken;

// try {
// const response = await getSwapQuote({
// amount: '1', // hardcoded amount because we only need the exchange rate
// from: fromToken,
// to: toToken,
// useAggregator: false,
// });
// if (isApiError(response)) {
// console.error('Error fetching exchange rate:', response.error);
// return;
// }
// const rate =
// selectedInputType === 'crypto'
// ? 1 / Number(response.fromAmountUSD)
// : Number(response.toAmount) / 10 ** response.to.decimals;
// setExchangeRate(rate);
// } catch (error) {
// console.error('Uncaught error fetching exchange rate:', error);
// } finally {
// setExchangeRateLoading(false);
// }
// }, [selectedToken, selectedInputType]);

useEffect(() => {
if (!selectedToken) {
return;
}

useExchangeRate({
token: selectedToken,
selectedInputType,
setExchangeRate,
setExchangeRateLoading,
});
}, [selectedToken, selectedInputType]);

const value = useValue({
lifecycleStatus,
senderAddress,
Expand All @@ -243,6 +276,8 @@ export function SendProvider({ children }: SendProviderReact) {
setExchangeRate,
exchangeRateLoading,
setExchangeRateLoading,
selectedInputType,
setSelectedInputType,
});

return <SendContext.Provider value={value}>{children}</SendContext.Provider>;
Expand Down

0 comments on commit 09b5880

Please sign in to comment.