Skip to content

Commit

Permalink
Merge pull request #33 from astriaorg/develop
Browse files Browse the repository at this point in the history
Release 10/23/24-6
  • Loading branch information
steezeburger authored Oct 24, 2024
2 parents 0b3d32c + eaab0f4 commit a500688
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 203 deletions.
92 changes: 27 additions & 65 deletions web/src/components/DepositCard/DepositCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type React from "react";
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useContext, useEffect, useMemo, useState } from "react";

import { Dec, DecUtils } from "@keplr-wallet/unit";
import AnimatedArrowSpacer from "components/AnimatedDownArrowSpacer/AnimatedDownArrowSpacer";
Expand All @@ -8,27 +8,25 @@ import type { EvmChainInfo, IbcChainInfo } from "config/chainConfigs";
import { useConfig } from "config/hooks/useConfig";
import { NotificationType } from "features/Notifications/components/Notification/types";
import { NotificationsContext } from "features/Notifications/contexts/NotificationsContext";
import EthWalletConnector from "features/EthWallet/components/EthWalletConnector/EthWalletConnector";
import { useEthWallet } from "features/EthWallet/hooks/useEthWallet";
import { useEvmChainSelection } from "features/EthWallet/hooks/useEvmChainSelection";
import { useIbcChainSelection } from "features/IbcChainSelector/hooks/useIbcChainSelection";
import { sendIbcTransfer } from "services/ibc";

export default function DepositCard(): React.ReactElement {
const { addNotification } = useContext(NotificationsContext);
const { userAccount: evmUserAccount, selectedWallet } = useEthWallet();
const { evmChains, ibcChains } = useConfig();

const {
evmAccountAddress: recipientAddress,
selectEvmChain,
evmChainsOptions,
selectedEvmChain,
selectEvmCurrency,
evmCurrencyOptions,
evmBalance,
isLoadingEvmBalance,
connectEVMWallet,
} = useEvmChainSelection(evmChains);
const defaultEvmChainOption = useMemo(() => {
return evmChainsOptions[0] || null;
}, [evmChainsOptions]);
const defaultEvmCurrencyOption = useMemo(() => {
return evmCurrencyOptions[0] || null;
}, [evmCurrencyOptions]);
Expand Down Expand Up @@ -94,7 +92,6 @@ export default function DepositCard(): React.ReactElement {

const [amount, setAmount] = useState<string>("");
const [isAmountValid, setIsAmountValid] = useState<boolean>(false);
const [recipientAddress, setRecipientAddress] = useState<string>("");
const [isRecipientAddressValid, setIsRecipientAddressValid] =
useState<boolean>(false);
const [hasTouchedForm, setHasTouchedForm] = useState<boolean>(false);
Expand All @@ -114,7 +111,15 @@ export default function DepositCard(): React.ReactElement {
setAmount(event.target.value);
};

const checkIsFormValid = (addressInput: string, amountInput: string) => {
const checkIsFormValid = (
addressInput: string | null,
amountInput: string,
) => {
if (addressInput === null) {
setIsRecipientAddressValid(false);
return;
}

const amount = Number.parseFloat(amountInput);
const amountValid = amount > 0;
setIsAmountValid(amountValid);
Expand All @@ -123,68 +128,15 @@ export default function DepositCard(): React.ReactElement {
setIsRecipientAddressValid(addressValid);
};

// NOTE - this was required to ensure the latest state was used in a callback
// used in the modal that connects to the evm wallet.
// create refs to hold the latest state values
const latestState = useRef({
evmUserAccount,
selectedWallet,
recipientAddress,
selectedEvmChain,
});
// update the ref whenever the state changes
useEffect(() => {
latestState.current = {
evmUserAccount,
selectedWallet,
recipientAddress,
selectedEvmChain,
};
}, [evmUserAccount, selectedWallet, recipientAddress, selectedEvmChain]);

// ensure evm wallet connection when selected EVM chain changes
/* biome-ignore lint/correctness/useExhaustiveDependencies: */
useEffect(() => {
if (!selectedEvmChain) {
return;
}
connectEVMWallet().then((_) => {});
}, [selectedEvmChain]);

const connectEVMWallet = async () => {
if (!selectedEvmChain) {
// select default chain if none selected, then return. effect handles retriggering.
selectEvmChain(defaultEvmChainOption.value);
return;
}

addNotification({
modalOpts: {
modalType: NotificationType.INFO,
title: "Connect EVM Wallet",
component: <EthWalletConnector />,
onCancel: () => {
const currentState = latestState.current;
setRecipientAddress("");
selectEvmChain(null);
if (currentState.selectedWallet) {
currentState.selectedWallet = undefined;
}
},
onConfirm: () => {
const currentState = latestState.current;
if (!currentState.evmUserAccount) {
setRecipientAddress("");
selectEvmChain(null);
} else {
setRecipientAddress(currentState.evmUserAccount.address);
}
},
},
});
};

// TODO - also set evm balance

const sendBalance = async () => {
if (!selectedIbcChain || !selectedIbcCurrency) {
addNotification({
Expand All @@ -196,11 +148,11 @@ export default function DepositCard(): React.ReactElement {
});
return;
}
if (!fromAddress) {
if (!fromAddress || !recipientAddress) {
addNotification({
toastOpts: {
toastType: NotificationType.WARNING,
message: "Please connect your Keplr wallet first.",
message: "Please connect your Keplr and EVM wallet first.",
onAcknowledge: () => {},
},
});
Expand Down Expand Up @@ -368,6 +320,16 @@ export default function DepositCard(): React.ReactElement {
Address: {recipientAddress}
</p>
)}
{recipientAddress && !isLoadingEvmBalance && (
<p className="mt-2 has-text-grey-lighter has-text-weight-semibold">
Balance: {evmBalance}
</p>
)}
{recipientAddress && isLoadingEvmBalance && (
<p className="mt-2 has-text-grey-lighter has-text-weight-semibold">
Balance: <i className="fas fa-spinner fa-pulse" />
</p>
)}
</div>
)}
</div>
Expand Down
91 changes: 13 additions & 78 deletions web/src/components/WithdrawCard/WithdrawCard.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
import type React from "react";
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useContext, useEffect, useMemo, useState } from "react";

import AnimatedArrowSpacer from "components/AnimatedDownArrowSpacer/AnimatedDownArrowSpacer";
import Dropdown, { type DropdownOption } from "components/Dropdown/Dropdown";
import type { EvmChainInfo, IbcChainInfo } from "config/chainConfigs";
import { useConfig } from "config/hooks/useConfig";
import { useIbcChainSelection } from "features/IbcChainSelector";
import {
EthWalletConnector,
getAstriaWithdrawerService,
} from "features/EthWallet";
import { getAstriaWithdrawerService } from "features/EthWallet";
import { useEthWallet } from "features/EthWallet/hooks/useEthWallet";
import { useEvmChainSelection } from "features/EthWallet/hooks/useEvmChainSelection";
import { NotificationType } from "features/Notifications/components/Notification/types";
import { NotificationsContext } from "features/Notifications/contexts/NotificationsContext";

export default function WithdrawCard(): React.ReactElement {
const { addNotification } = useContext(NotificationsContext);
const { userAccount: evmUserAccount, selectedWallet } = useEthWallet();
const { selectedWallet } = useEthWallet();
const { evmChains, ibcChains } = useConfig();

const {
evmAccountAddress: fromAddress,
selectEvmChain,
evmChainsOptions,
selectedEvmChain,
selectEvmCurrency,
evmCurrencyOptions,
selectedEvmCurrency,
evmBalance,
isLoadingEvmBalance,
connectEVMWallet,
} = useEvmChainSelection(evmChains);
const defaultEvmChainOption = useMemo(() => {
return evmChainsOptions[0] || null;
}, [evmChainsOptions]);
const defaultEvmCurrencyOption = useMemo(() => {
return evmCurrencyOptions[0] || null;
}, [evmCurrencyOptions]);
Expand Down Expand Up @@ -92,9 +90,6 @@ export default function WithdrawCard(): React.ReactElement {
};
}, [selectedEvmCurrency, selectedIbcChain, defaultIbcCurrencyOption]);

const [fromAddress, setFromAddress] = useState<string>("");
const [balance, setBalance] = useState<string>("0");
const [isLoadingBalance, setIsLoadingBalance] = useState<boolean>(false);
const [amount, setAmount] = useState<string>("");
const [isAmountValid, setIsAmountValid] = useState<boolean>(false);

Expand All @@ -104,41 +99,14 @@ export default function WithdrawCard(): React.ReactElement {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isAnimating, setIsAnimating] = useState<boolean>(false);

// create refs to hold the latest state values
const latestState = useRef({
evmUserAccount,
selectedWallet,
recipientAddress,
selectedEvmChain,
});

// update the ref whenever the state changes
useEffect(() => {
latestState.current = {
evmUserAccount,
selectedWallet,
recipientAddress,
selectedEvmChain,
};
}, [evmUserAccount, selectedWallet, recipientAddress, selectedEvmChain]);

useEffect(() => {
if (evmUserAccount?.address) {
setFromAddress(evmUserAccount.address);
}
// TODO - get balance for selected currency
if (evmUserAccount?.balance) {
setBalance(`${evmUserAccount.balance} ${selectedEvmCurrency?.coinDenom}`);
}
}, [evmUserAccount, selectedEvmCurrency]);

useEffect(() => {
if (amount || recipientAddress) {
setHasTouchedForm(true);
}
checkIsFormValid(amount, recipientAddress);
}, [amount, recipientAddress]);

/* biome-ignore lint/correctness/useExhaustiveDependencies: */
useEffect(() => {
if (!selectedEvmChain) {
return;
Expand All @@ -165,45 +133,13 @@ export default function WithdrawCard(): React.ReactElement {
setIsRecipientAddressValid(isRecipientAddressValid);
};

const connectEVMWallet = async () => {
if (!selectedEvmChain) {
// select default chain if none selected, then return. effect handles retriggering.
selectEvmChain(defaultEvmChainOption.value);
return;
}

addNotification({
modalOpts: {
modalType: NotificationType.INFO,
title: "Connect EVM Wallet",
component: <EthWalletConnector />,
onCancel: () => {
const currentState = latestState.current;
setFromAddress("");
selectEvmChain(null);
if (currentState.selectedWallet) {
currentState.selectedWallet = undefined;
}
},
onConfirm: () => {
const currentState = latestState.current;
if (!currentState.evmUserAccount) {
setFromAddress("");
selectEvmChain(null);
} else {
setFromAddress(currentState.evmUserAccount.address);
}
},
},
});
};

const handleWithdraw = async () => {
if (
!selectedWallet ||
!selectedEvmCurrency ||
!isAmountValid ||
!recipientAddress
!recipientAddress ||
!fromAddress
) {
console.error(
"Withdrawal cannot proceed: missing required fields or fields are invalid",
Expand Down Expand Up @@ -321,20 +257,19 @@ export default function WithdrawCard(): React.ReactElement {
</div>
)}
</div>
{/* TODO - show balance of whatever coin selected */}
{fromAddress && (
<div className="field-info-box py-2 px-3">
{fromAddress && (
<p className="has-text-grey-light has-text-weight-semibold">
Address: {fromAddress}
</p>
)}
{fromAddress && !isLoadingBalance && (
{fromAddress && !isLoadingEvmBalance && (
<p className="mt-2 has-text-grey-lighter has-text-weight-semibold">
Balance: {balance}
Balance: {evmBalance}
</p>
)}
{fromAddress && isLoadingBalance && (
{fromAddress && isLoadingEvmBalance && (
<p className="mt-2 has-text-grey-lighter has-text-weight-semibold">
Balance: <i className="fas fa-spinner fa-pulse" />
</p>
Expand Down
9 changes: 9 additions & 0 deletions web/src/config/chainConfigs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ export type EvmCurrency = {
iconClass?: string;
};

export function evmCurrencyBelongsToChain(
currency: EvmCurrency,
chain: EvmChainInfo,
): boolean {
// FIXME - what if two chains have currencies with the same coinDenom?
// e.g. USDC on Noble and USDC on Celestia
return chain.currencies?.includes(currency);
}

// EvmChains type maps labels to EvmChainInfo objects
export type EvmChains = {
[label: string]: EvmChainInfo;
Expand Down
Loading

0 comments on commit a500688

Please sign in to comment.