diff --git a/README.md b/README.md index eb14117..b87b753 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,29 @@ the Astria bridge. * main application component * define routes * use context providers -* `src/chainInfos` - Celestia and Astria chain information -* `src/components` - React components -* `src/contexts` - React context definitions +* `src/components` - More general React components for the app, e.g. Navbar, + Dropdown, CopyToClipboardButton, etc +* `src/config` - Configuration for the web app + * `src/config/chainConfigs` - Celestia and Astria chain information + * `src/config/contexts` - Config context and context provider + * `src/config/hooks` - Custom hook to make config easy to use + * `src/config/env.ts` - Environment variable definitions plus utilities for + consuming them + * `src/config/index.ts` - AppConfig and exports +* `src/features` - Organizes components, contexts, hooks, services, types, and + utils for different features + * `src/features/EthWallet` - Used for interacting with EVM wallets + * `src/features/KeplrWallet` - User for interacting with Keplr wallet + * `src/features/Notifications` - Used for displaying notifications and toasts * `src/pages` * React components for each page * `src/pages/Layout.tsx` * page layout component using `` * contains ``, `` -* `src/providers` - React context provider definitions -* `src/services` - * api services - * Keplr services - * IBC services - * 3rd party wrappers * `src/styles` * all style definitions * using scss * using [bulma](https://bulma.io/documentation/) css framework -* `src/types` - type definitions -* `src/utils` - utility functions ## Commands @@ -53,19 +56,24 @@ just web build * How to add new chain configs for a new environment (e.g. you want to add new chain configs for "mainnet") * create file that will contain the config values + ```sh touch web/src/config/chainConfigs/ChainConfigsMainnet.ts ``` + * import new configs in `astria-bridge-web-app/web/src/config/chainConfigs/index.ts`, while renaming them + ```typescript import { evmChains as mainnetEvmChains, ibcChains as mainnetIbcChains, } from "./ChainConfigsMainnet"; ``` + * add entry to `EVM_CHAIN_CONFIGS` + ```typescript const ENV_CHAIN_CONFIGS = { local: { evm: localEvmChains, ibc: localIbcChains }, diff --git a/web/package.json b/web/package.json index ec15acd..7b59f8e 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "astria-bridge-web-app", - "version": "0.0.1", + "version": "0.10.0", "private": true, "dependencies": { "@cosmjs/launchpad": "^0.27.1", diff --git a/web/src/App.test.tsx b/web/src/App.test.tsx index 2e16cf5..9a92a64 100644 --- a/web/src/App.test.tsx +++ b/web/src/App.test.tsx @@ -1,5 +1,4 @@ import type React from "react"; -import { screen } from "@testing-library/react"; import App from "./App"; import { renderWithRouter } from "testHelpers"; diff --git a/web/src/components/DepositCard/DepositCard.tsx b/web/src/components/DepositCard/DepositCard.tsx index 85398b9..d789dbf 100644 --- a/web/src/components/DepositCard/DepositCard.tsx +++ b/web/src/components/DepositCard/DepositCard.tsx @@ -3,8 +3,8 @@ import { useEffect, useMemo, useState } from "react"; import { Dec, DecUtils } from "@keplr-wallet/unit"; import AnimatedArrowSpacer from "components/AnimatedDownArrowSpacer/AnimatedDownArrowSpacer"; -import Dropdown, { type DropdownOption } from "components/Dropdown/Dropdown"; -import { useConfig, type EvmChainInfo, type IbcChainInfo } from "config"; +import Dropdown from "components/Dropdown/Dropdown"; +import { useConfig } from "config"; import { useEvmChainSelection } from "features/EthWallet"; import { sendIbcTransfer, useIbcChainSelection } from "features/KeplrWallet"; import { useNotifications, NotificationType } from "features/Notifications"; @@ -18,55 +18,29 @@ export default function DepositCard(): React.ReactElement { selectEvmChain, evmChainsOptions, selectedEvmChain, + selectedEvmChainOption, + defaultEvmCurrencyOption, selectEvmCurrency, evmCurrencyOptions, evmBalance, isLoadingEvmBalance, connectEVMWallet, } = useEvmChainSelection(evmChains); - const defaultEvmCurrencyOption = useMemo(() => { - return evmCurrencyOptions[0] || null; - }, [evmCurrencyOptions]); const { ibcAccountAddress: fromAddress, selectIbcChain, ibcChainsOptions, selectedIbcChain, + selectedIbcChainOption, + defaultIbcCurrencyOption, selectIbcCurrency, - ibcCurrencyOptions, selectedIbcCurrency, + ibcCurrencyOptions, ibcBalance, isLoadingIbcBalance, connectKeplrWallet, } = useIbcChainSelection(ibcChains); - const defaultIbcCurrencyOption = useMemo(() => { - return ibcCurrencyOptions[0] || null; - }, [ibcCurrencyOptions]); - - // selectedIbcChainOption allows us to ensure the label is set properly - // in the dropdown when connecting via an "additional option"s action, - // e.g. the "Connect Keplr Wallet" option in the dropdown - const selectedIbcChainOption = useMemo(() => { - if (!selectedIbcChain) { - return null; - } - return { - label: selectedIbcChain?.chainName || "", - value: selectedIbcChain, - leftIconClass: selectedIbcChain?.iconClass || "", - } as DropdownOption; - }, [selectedIbcChain]); - const selectedEvmChainOption = useMemo(() => { - if (!selectedEvmChain) { - return null; - } - return { - label: selectedEvmChain?.chainName || "", - value: selectedEvmChain, - leftIconClass: selectedEvmChain?.iconClass || "", - } as DropdownOption; - }, [selectedEvmChain]); // the evm currency selection is controlled by the sender's chosen ibc currency, // and should be updated when an ibc currency or evm chain is selected diff --git a/web/src/components/WithdrawCard/WithdrawCard.tsx b/web/src/components/WithdrawCard/WithdrawCard.tsx index 203c8d1..945794c 100644 --- a/web/src/components/WithdrawCard/WithdrawCard.tsx +++ b/web/src/components/WithdrawCard/WithdrawCard.tsx @@ -1,9 +1,9 @@ import type React from "react"; import { useEffect, useMemo, useState } from "react"; -import { useConfig, type EvmChainInfo, type IbcChainInfo } from "config"; +import { useConfig } from "config"; import AnimatedArrowSpacer from "components/AnimatedDownArrowSpacer/AnimatedDownArrowSpacer"; -import Dropdown, { type DropdownOption } from "components/Dropdown/Dropdown"; +import Dropdown from "components/Dropdown/Dropdown"; import { getAstriaWithdrawerService, useEthWallet, @@ -22,6 +22,9 @@ export default function WithdrawCard(): React.ReactElement { selectEvmChain, evmChainsOptions, selectedEvmChain, + selectedEvmChainOption, + withdrawFeeDisplay, + defaultEvmCurrencyOption, selectEvmCurrency, evmCurrencyOptions, selectedEvmCurrency, @@ -29,47 +32,20 @@ export default function WithdrawCard(): React.ReactElement { isLoadingEvmBalance, connectEVMWallet, } = useEvmChainSelection(evmChains); - const defaultEvmCurrencyOption = useMemo(() => { - return evmCurrencyOptions[0] || null; - }, [evmCurrencyOptions]); const { ibcAccountAddress: recipientAddress, selectIbcChain, ibcChainsOptions, selectedIbcChain, + selectedIbcChainOption, + defaultIbcCurrencyOption, selectIbcCurrency, ibcCurrencyOptions, ibcBalance, isLoadingIbcBalance, connectKeplrWallet, } = useIbcChainSelection(ibcChains); - const defaultIbcCurrencyOption = useMemo(() => { - return ibcCurrencyOptions[0] || null; - }, [ibcCurrencyOptions]); - - // selectedIbcChainOption allows us to ensure the label is set properly - // in the dropdown when connecting via additional action - const selectedIbcChainOption = useMemo(() => { - if (!selectedIbcChain) { - return null; - } - return { - label: selectedIbcChain?.chainName || "", - value: selectedIbcChain, - leftIconClass: selectedIbcChain?.iconClass || "", - } as DropdownOption; - }, [selectedIbcChain]); - const selectedEvmChainOption = useMemo(() => { - if (!selectedEvmChain) { - return null; - } - return { - label: selectedEvmChain?.chainName || "", - value: selectedEvmChain, - leftIconClass: selectedEvmChain?.iconClass || "", - } as DropdownOption; - }, [selectedEvmChain]); // the ibc currency selection is controlled by the sender's chosen evm currency, // and should be updated when an ibc currency or ibc chain is selected @@ -338,6 +314,11 @@ export default function WithdrawCard(): React.ReactElement { Balance:

)} + {withdrawFeeDisplay && ( +
+ Withdrawal fee: {withdrawFeeDisplay} +
+ )} )} diff --git a/web/src/config/chainConfigs/types.ts b/web/src/config/chainConfigs/types.ts index 1da78f4..842663a 100644 --- a/web/src/config/chainConfigs/types.ts +++ b/web/src/config/chainConfigs/types.ts @@ -1,5 +1,4 @@ import type { ChainInfo } from "@keplr-wallet/types"; -import { ethers } from "ethers"; /** * Represents information about an IBC chain. diff --git a/web/src/config/contexts/ConfigContext.tsx b/web/src/config/contexts/ConfigContext.tsx index 5be5ff1..f03ed66 100644 --- a/web/src/config/contexts/ConfigContext.tsx +++ b/web/src/config/contexts/ConfigContext.tsx @@ -1,11 +1,7 @@ import React from "react"; import type { AppConfig } from "config"; -import type { - EvmChainInfo, - EvmChains, - IbcChains, -} from "config/chainConfigs/types"; +import type { EvmChainInfo } from "config/chainConfigs/types"; import { getEnvChainConfigs } from "config/chainConfigs"; import { getEnvVariable } from "config/env"; diff --git a/web/src/features/EthWallet/hooks/useEvmChainSelection.tsx b/web/src/features/EthWallet/hooks/useEvmChainSelection.tsx index 3093d7d..c649f09 100644 --- a/web/src/features/EthWallet/hooks/useEvmChainSelection.tsx +++ b/web/src/features/EthWallet/hooks/useEvmChainSelection.tsx @@ -5,6 +5,8 @@ import React, { useRef, useState, } from "react"; +import { ethers } from "ethers"; + import type { DropdownOption } from "components/Dropdown/Dropdown"; import { type EvmChainInfo, @@ -12,7 +14,7 @@ import { type EvmCurrency, evmCurrencyBelongsToChain, } from "config"; -import { useNotifications, NotificationType } from "features/Notifications"; +import { NotificationType, useNotifications } from "features/Notifications"; import { useEthWallet } from "features/EthWallet/hooks/useEthWallet"; import EthWalletConnector from "features/EthWallet/components/EthWalletConnector/EthWalletConnector"; @@ -93,6 +95,18 @@ export function useEvmChainSelection(evmChains: EvmChains) { evmAccountAddress, ]); + const selectedEvmChainNativeToken = useMemo(() => { + return selectedEvmChain?.currencies[0]; + }, [selectedEvmChain]); + + const withdrawFeeDisplay = useMemo(() => { + if (!selectedEvmChainNativeToken || !selectedEvmCurrency) { + return ""; + } + const fee = ethers.formatUnits(selectedEvmCurrency.ibcWithdrawalFeeWei, 18); + return `${fee} ${selectedEvmChainNativeToken.coinDenom}`; + }, [selectedEvmChainNativeToken, selectedEvmCurrency]); + const evmChainsOptions = useMemo(() => { return Object.entries(evmChains).map( ([chainLabel, chain]): DropdownOption => ({ @@ -103,6 +117,20 @@ export function useEvmChainSelection(evmChains: EvmChains) { ); }, [evmChains]); + // selectedEvmChainOption allows us to ensure the label is set properly + // in the dropdown when connecting via an "additional option"s action, + // e.g. the "Connect Keplr Wallet" option in the dropdown + const selectedEvmChainOption = useMemo(() => { + if (!selectedEvmChain) { + return null; + } + return { + label: selectedEvmChain?.chainName || "", + value: selectedEvmChain, + leftIconClass: selectedEvmChain?.iconClass || "", + } as DropdownOption; + }, [selectedEvmChain]); + const selectEvmChain = useCallback((chain: EvmChainInfo | null) => { setSelectedEvmChain(chain); }, []); @@ -112,7 +140,7 @@ export function useEvmChainSelection(evmChains: EvmChains) { return []; } - // can only withdraw the currency if it has a withdraw contract address defined + // can only withdraw the currency if it has a withdrawer contract address defined const withdrawableTokens = selectedEvmChain.currencies?.filter( (currency) => currency.erc20ContractAddress || @@ -128,6 +156,10 @@ export function useEvmChainSelection(evmChains: EvmChains) { ); }, [selectedEvmChain]); + const defaultEvmCurrencyOption = useMemo(() => { + return evmCurrencyOptions[0] || null; + }, [evmCurrencyOptions]); + const selectEvmCurrency = useCallback((currency: EvmCurrency) => { setSelectedEvmCurrency(currency); }, []); @@ -191,7 +223,11 @@ export function useEvmChainSelection(evmChains: EvmChains) { selectEvmCurrency, selectedEvmChain, + selectedEvmChainNativeToken, + withdrawFeeDisplay, selectedEvmCurrency, + defaultEvmCurrencyOption, + selectedEvmChainOption, evmAccountAddress, evmBalance, diff --git a/web/src/features/KeplrWallet/hooks/useIbcChainSelection.tsx b/web/src/features/KeplrWallet/hooks/useIbcChainSelection.tsx index d98bc49..aa490a9 100644 --- a/web/src/features/KeplrWallet/hooks/useIbcChainSelection.tsx +++ b/web/src/features/KeplrWallet/hooks/useIbcChainSelection.tsx @@ -115,6 +115,24 @@ export function useIbcChainSelection(ibcChains: IbcChains) { ); }, [selectedIbcChain]); + const defaultIbcCurrencyOption = useMemo(() => { + return ibcCurrencyOptions[0] || null; + }, [ibcCurrencyOptions]); + + // selectedIbcChainOption allows us to ensure the label is set properly + // in the dropdown when connecting via an "additional option"s action, + // e.g. the "Connect Keplr Wallet" option in the dropdown + const selectedIbcChainOption = useMemo(() => { + if (!selectedIbcChain) { + return null; + } + return { + label: selectedIbcChain?.chainName || "", + value: selectedIbcChain, + leftIconClass: selectedIbcChain?.iconClass || "", + } as DropdownOption; + }, [selectedIbcChain]); + const selectIbcCurrency = useCallback((currency: IbcCurrency) => { setSelectedIbcCurrency(currency); }, []); @@ -192,6 +210,8 @@ export function useIbcChainSelection(ibcChains: IbcChains) { selectedIbcChain, selectedIbcCurrency, + defaultIbcCurrencyOption, + selectedIbcChainOption, ibcAccountAddress, ibcBalance, diff --git a/web/src/styles/icons.scss b/web/src/styles/icons.scss index 6210d22..3510531 100644 --- a/web/src/styles/icons.scss +++ b/web/src/styles/icons.scss @@ -48,7 +48,6 @@ i.i-flame { } i.i-noble { - // this was downloaded from figma background-image: url('https://avatars.githubusercontent.com/u/133800472?s=200&v=4'); background-repeat: no-repeat; background-size: contain;