diff --git a/apps/namadillo/package.json b/apps/namadillo/package.json index 79048005ea..3580a30903 100644 --- a/apps/namadillo/package.json +++ b/apps/namadillo/package.json @@ -1,6 +1,6 @@ { "name": "@namada/namadillo", - "version": "1.0.6", + "version": "1.1.0", "description": "Namadillo", "repository": "https://github.com/anoma/namada-interface/", "author": "Heliax Dev ", diff --git a/apps/namadillo/src/App/Common/GasFeeModal.tsx b/apps/namadillo/src/App/Common/GasFeeModal.tsx new file mode 100644 index 0000000000..95fc80a489 --- /dev/null +++ b/apps/namadillo/src/App/Common/GasFeeModal.tsx @@ -0,0 +1,111 @@ +import { Modal, SkeletonLoading } from "@namada/components"; +import { chainAssetsMapAtom, nativeTokenAddressAtom } from "atoms/chain"; +import { + gasPriceForAllTokensAtom, + storageGasTokenAtom, +} from "atoms/fees/atoms"; +import { tokenPricesFamily } from "atoms/prices/atoms"; +import BigNumber from "bignumber.js"; +import { useAtomValue, useSetAtom } from "jotai"; +import { IoClose } from "react-icons/io5"; +import { twMerge } from "tailwind-merge"; +import { GasConfig } from "types"; +import { unknownAsset } from "utils/assets"; +import { getDisplayGasFee } from "utils/gas"; +import { FiatCurrency } from "./FiatCurrency"; +import { TokenCurrency } from "./TokenCurrency"; + +export const GasFeeModal = ({ + gasConfig, + onClose, +}: { + gasConfig: GasConfig; + onClose: () => void; +}): JSX.Element => { + const setStorageGasToken = useSetAtom(storageGasTokenAtom); + const gasPriceForAllTokens = useAtomValue(gasPriceForAllTokensAtom); + const chainAssetsMap = useAtomValue(chainAssetsMapAtom); + const nativeTokenAddress = useAtomValue(nativeTokenAddressAtom).data; + + const data = gasPriceForAllTokens.data ?? []; + + const tokenAddresses = data.map((item) => item.token); + const gasDollarMap = useAtomValue(tokenPricesFamily(tokenAddresses)); + + return ( + +
+ + + +
+

Select Gas Token

+
+ Gas fees deducted from your Namada accounts +
+
+
+ {!data.length ? + + : data + .sort((a, b) => + a.token === nativeTokenAddress ? -1 + : b.token === nativeTokenAddress ? 1 + : 0 + ) + .map(({ token, minDenomAmount }) => { + const asset = chainAssetsMap[token] ?? unknownAsset(token); + const symbol = asset.symbol; + const fee = getDisplayGasFee({ + gasLimit: gasConfig.gasLimit, + gasPrice: BigNumber(minDenomAmount), + gasToken: token, + asset, + }); + const price = gasDollarMap.data?.[token]; + const dollar = price ? fee.multipliedBy(price) : undefined; + + const selected = token === gasConfig.gasToken; + + return ( + + ); + }) + } +
+
+
+ ); +}; diff --git a/apps/namadillo/src/App/Common/TransactionFee.tsx b/apps/namadillo/src/App/Common/TransactionFee.tsx new file mode 100644 index 0000000000..701c94e063 --- /dev/null +++ b/apps/namadillo/src/App/Common/TransactionFee.tsx @@ -0,0 +1,21 @@ +import { GasConfig } from "types"; +import { unknownAsset } from "utils/assets"; +import { getDisplayGasFee } from "utils/gas"; +import { TokenCurrency } from "./TokenCurrency"; + +export const TransactionFee = ({ + gasConfig, +}: { + gasConfig: GasConfig; +}): JSX.Element => { + const asset = gasConfig.asset ?? unknownAsset(gasConfig.gasToken); + const symbol = asset.symbol; + const fee = getDisplayGasFee(gasConfig); + + return ( +
+ Transaction fee:{" "} + +
+ ); +}; diff --git a/apps/namadillo/src/App/Common/TransactionFeeButton.tsx b/apps/namadillo/src/App/Common/TransactionFeeButton.tsx new file mode 100644 index 0000000000..e38cbe9942 --- /dev/null +++ b/apps/namadillo/src/App/Common/TransactionFeeButton.tsx @@ -0,0 +1,30 @@ +import { useState } from "react"; +import { GasConfig } from "types"; +import { GasFeeModal } from "./GasFeeModal"; +import { TransactionFee } from "./TransactionFee"; + +export const TransactionFeeButton = ({ + gasConfig, +}: { + gasConfig: GasConfig; +}): JSX.Element => { + const [modalOpen, setModalOpen] = useState(false); + + return ( + <> + + {modalOpen && ( + setModalOpen(false)} + /> + )} + + ); +}; diff --git a/apps/namadillo/src/App/Common/TransactionFees.tsx b/apps/namadillo/src/App/Common/TransactionFees.tsx deleted file mode 100644 index 0631dd83c0..0000000000 --- a/apps/namadillo/src/App/Common/TransactionFees.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import clsx from "clsx"; -import { GasConfig } from "types"; -import { NamCurrency } from "./NamCurrency"; -import { TextLink } from "./TextLink"; - -type TransactionFeesProps = { - gasConfig: GasConfig; - className?: string; -}; - -export const TransactionFees = ({ - gasConfig, - className, -}: TransactionFeesProps): JSX.Element => { - const fee = gasConfig.gasPrice.times(gasConfig.gasLimit); - - return ( -
- Transaction fee:{" "} - -
- ); -}; diff --git a/apps/namadillo/src/App/Governance/SubmitVote.tsx b/apps/namadillo/src/App/Governance/SubmitVote.tsx index f531463832..03541740a3 100644 --- a/apps/namadillo/src/App/Governance/SubmitVote.tsx +++ b/apps/namadillo/src/App/Governance/SubmitVote.tsx @@ -12,7 +12,7 @@ import { isVoteType, voteTypes, } from "@namada/types"; -import { TransactionFees } from "App/Common/TransactionFees"; +import { TransactionFeeButton } from "App/Common/TransactionFeeButton"; import { defaultGasConfigFamily } from "atoms/fees"; import { createNotificationId, @@ -181,10 +181,9 @@ export const WithProposalId: React.FC<{ proposalId: bigint }> = ({
{gasConfig.isSuccess && ( - +
+ +
)}
{ selectedAssetAddress ); - const transactionFee = useMemo(() => { + const gasConfig = useMemo(() => { if (typeof registry !== "undefined") { - return getTransactionFee(registry); + return getIbcGasConfig(registry); } return undefined; }, [registry]); @@ -144,7 +144,7 @@ export const IbcTransfer: React.FC = () => { throw new Error("Invalid IBC destination channel"); } - if (typeof transactionFee === "undefined") { + if (typeof gasConfig === "undefined") { throw new Error("No transaction fee is set"); } @@ -179,7 +179,7 @@ export const IbcTransfer: React.FC = () => { destinationAddress, amount: displayAmount, asset: selectedAsset, - transactionFee, + gasConfig, sourceChannelId: sourceChannel.trim(), ...(shielded ? { @@ -254,7 +254,7 @@ export const IbcTransfer: React.FC = () => { isShielded: shielded, onChangeShielded: setShielded, }} - transactionFee={transactionFee} + gasConfig={gasConfig} isSubmitting={performIbcTransfer.isPending} isIbcTransfer={true} requiresIbcChannels={requiresIbcChannels} diff --git a/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx b/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx index 67de11ff37..13494e0e4f 100644 --- a/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx +++ b/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx @@ -24,7 +24,6 @@ import { broadcastTx } from "lib/query"; import { useEffect, useState } from "react"; import namadaChainRegistry from "registry/namada.json"; import { Address, PartialTransferTransactionData, TransferStep } from "types"; -import { namadaAsset } from "utils"; import { IbcTopHeader } from "./IbcTopHeader"; const defaultChainId = "cosmoshub-4"; @@ -53,15 +52,6 @@ export const IbcWithdraw: React.FC = () => { defaultGasConfigFamily(["IbcTransfer"]) ); - const transactionFee = mapUndefined( - ({ gasLimit, gasPrice }) => ({ - originalAddress: namadaAsset().address, - asset: namadaAsset(), - amount: gasPrice.multipliedBy(gasLimit), - }), - gasConfig - ); - const { walletAddress: keplrAddress, connectToChainId, @@ -203,7 +193,7 @@ export const IbcWithdraw: React.FC = () => { onChangeSourceChannel: setSourceChannel, }} onSubmitTransfer={submitIbcTransfer} - transactionFee={transactionFee} + gasConfig={gasConfig} errorMessage={generalErrorMessage} /> diff --git a/apps/namadillo/src/App/Ibc/ShieldAllPanel.tsx b/apps/namadillo/src/App/Ibc/ShieldAllPanel.tsx index 5a1d9e897b..0d6f4cde4f 100644 --- a/apps/namadillo/src/App/Ibc/ShieldAllPanel.tsx +++ b/apps/namadillo/src/App/Ibc/ShieldAllPanel.tsx @@ -6,9 +6,9 @@ import { Stack, } from "@namada/components"; import svgImg from "App/Assets/ShieldedParty.svg"; +import { TransactionFee } from "App/Common/TransactionFee"; import { SelectedWallet } from "App/Transfer/SelectedWallet"; -import { TransferTransactionFee } from "App/Transfer/TransferTransactionFee"; -import { getTransactionFee } from "integrations/utils"; +import { getIbcGasConfig } from "integrations/utils"; import { useEffect, useMemo, useState } from "react"; import { AddressWithAssetAndAmount, @@ -20,6 +20,7 @@ import { ShieldAllAssetList, } from "./ShieldAllAssetList"; import { ShieldAllContainer } from "./ShieldAllContainer"; +import ibcTransferImageBlack from "./assets/ibc-transfer-black.png"; type ShieldAllPanelProps = { registry: ChainRegistryEntry; @@ -76,7 +77,7 @@ export const ShieldAllPanel = ({ [selectableAssets] ); - const transactionFee = getTransactionFee(registry); + const gasConfig = getIbcGasConfig(registry); return ( @@ -115,13 +116,10 @@ export const ShieldAllPanel = ({ } - {transactionFee && ( - - )} +
+ + {gasConfig && } +
{ diff --git a/apps/namadillo/src/App/Transfer/assets/ibc-transfer-black.png b/apps/namadillo/src/App/Ibc/assets/ibc-transfer-black.png similarity index 100% rename from apps/namadillo/src/App/Transfer/assets/ibc-transfer-black.png rename to apps/namadillo/src/App/Ibc/assets/ibc-transfer-black.png diff --git a/apps/namadillo/src/App/Masp/MaspShield.tsx b/apps/namadillo/src/App/Masp/MaspShield.tsx index 37ad7a2976..2451c0b0d5 100644 --- a/apps/namadillo/src/App/Masp/MaspShield.tsx +++ b/apps/namadillo/src/App/Masp/MaspShield.tsx @@ -5,7 +5,6 @@ import { Timeline } from "App/Common/Timeline"; import { params } from "App/routes"; import { OnSubmitTransferParams, - TransactionFee, TransferModule, } from "App/Transfer/TransferModule"; import { allDefaultAccountsAtom } from "atoms/accounts"; @@ -71,15 +70,6 @@ export const MaspShield: React.FC = () => { defaultGasConfigFamily(["ShieldingTransfer"]) ); - const transactionFee: TransactionFee | undefined = - selectedAsset && gasConfig ? - { - originalAddress: selectedAsset.originalAddress, - asset: selectedAsset.asset, - amount: gasConfig.gasPrice.multipliedBy(gasConfig.gasLimit), - } - : undefined; - const assetImage = selectedAsset ? getAssetImageUrl(selectedAsset.asset) : ""; useEffect(() => { @@ -207,7 +197,7 @@ export const MaspShield: React.FC = () => { walletAddress: destinationAddress, isShielded: true, }} - transactionFee={transactionFee} + gasConfig={gasConfig} isSubmitting={performShieldTransfer.isPending} errorMessage={generalErrorMessage} onSubmitTransfer={onSubmitTransfer} diff --git a/apps/namadillo/src/App/Masp/MaspUnshield.tsx b/apps/namadillo/src/App/Masp/MaspUnshield.tsx index ad48817318..de9612b82e 100644 --- a/apps/namadillo/src/App/Masp/MaspUnshield.tsx +++ b/apps/namadillo/src/App/Masp/MaspUnshield.tsx @@ -5,7 +5,6 @@ import { Timeline } from "App/Common/Timeline"; import { params } from "App/routes"; import { OnSubmitTransferParams, - TransactionFee, TransferModule, } from "App/Transfer/TransferModule"; import { allDefaultAccountsAtom } from "atoms/accounts"; @@ -72,15 +71,6 @@ export const MaspUnshield: React.FC = () => { defaultGasConfigFamily(["UnshieldingTransfer"]) ); - const transactionFee: TransactionFee | undefined = - selectedAsset && gasConfig ? - { - originalAddress: selectedAsset.originalAddress, - asset: selectedAsset.asset, - amount: gasConfig.gasPrice.multipliedBy(gasConfig.gasLimit), - } - : undefined; - const assetImage = selectedAsset ? getAssetImageUrl(selectedAsset.asset) : ""; useEffect(() => { @@ -213,7 +203,7 @@ export const MaspUnshield: React.FC = () => { walletAddress: destinationAddress, isShielded: false, }} - transactionFee={transactionFee} + gasConfig={gasConfig} isSubmitting={performUnshieldTransfer.isPending} errorMessage={generalErrorMessage} onSubmitTransfer={onSubmitTransfer} diff --git a/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx b/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx index ed2c2b637b..408cf46670 100644 --- a/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx +++ b/apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx @@ -6,7 +6,6 @@ import { TransferTransactionTimeline } from "App/Transactions/TransferTransactio import { isShieldedAddress } from "App/Transfer/common"; import { OnSubmitTransferParams, - TransactionFee, TransferModule, } from "App/Transfer/TransferModule"; import { allDefaultAccountsAtom } from "atoms/accounts"; @@ -152,15 +151,6 @@ export const NamadaTransfer: React.FC = () => { } })(); - const transactionFee: TransactionFee | undefined = - selectedAsset && gasConfig ? - { - originalAddress: selectedAsset.originalAddress, - asset: selectedAsset.asset, - amount: gasConfig.gasPrice.multipliedBy(gasConfig.gasLimit), - } - : undefined; - const isSourceShielded = isShieldedAddress(source); const isTargetShielded = isShieldedAddress(target); @@ -281,7 +271,7 @@ export const NamadaTransfer: React.FC = () => { customAddress, onChangeCustomAddress: setCustomAddress, }} - transactionFee={transactionFee} + gasConfig={gasConfig} isSubmitting={isPerformingTransfer} errorMessage={generalErrorMessage} onSubmitTransfer={onSubmitTransfer} diff --git a/apps/namadillo/src/App/Staking/IncrementBonding.tsx b/apps/namadillo/src/App/Staking/IncrementBonding.tsx index 5524da130b..c2df68dff9 100644 --- a/apps/namadillo/src/App/Staking/IncrementBonding.tsx +++ b/apps/namadillo/src/App/Staking/IncrementBonding.tsx @@ -5,7 +5,7 @@ import { Info } from "App/Common/Info"; import { ModalContainer } from "App/Common/ModalContainer"; import { NamCurrency } from "App/Common/NamCurrency"; import { TableRowLoading } from "App/Common/TableRowLoading"; -import { TransactionFees } from "App/Common/TransactionFees"; +import { TransactionFeeButton } from "App/Common/TransactionFeeButton"; import { routes } from "App/routes"; import { accountBalanceAtom, defaultAccountAtom } from "atoms/accounts"; import { chainParametersAtom } from "atoms/chain"; @@ -228,10 +228,9 @@ const IncrementBonding = (): JSX.Element => { {isPerformingBonding ? "Processing..." : errorMessage || "Stake"} {gasConfig && ( - +
+ +
)} diff --git a/apps/namadillo/src/App/Staking/ReDelegateAssignStake.tsx b/apps/namadillo/src/App/Staking/ReDelegateAssignStake.tsx index bca28d3768..586f49e852 100644 --- a/apps/namadillo/src/App/Staking/ReDelegateAssignStake.tsx +++ b/apps/namadillo/src/App/Staking/ReDelegateAssignStake.tsx @@ -1,6 +1,6 @@ import { ActionButton, Panel } from "@namada/components"; import { NamCurrency } from "App/Common/NamCurrency"; -import { TransactionFees } from "App/Common/TransactionFees"; +import { TransactionFeeButton } from "App/Common/TransactionFeeButton"; import BigNumber from "bignumber.js"; import clsx from "clsx"; import { useValidatorFilter } from "hooks/useValidatorFilter"; @@ -162,10 +162,9 @@ export const ReDelegateAssignStake = ({ isPerformingRedelegation={isPerformingRedelegation} /> {gasConfig && ( - +
+ +
)} diff --git a/apps/namadillo/src/App/Staking/Unstake.tsx b/apps/namadillo/src/App/Staking/Unstake.tsx index e7201a3d37..e42b69bdfe 100644 --- a/apps/namadillo/src/App/Staking/Unstake.tsx +++ b/apps/namadillo/src/App/Staking/Unstake.tsx @@ -5,7 +5,7 @@ import { Info } from "App/Common/Info"; import { ModalContainer } from "App/Common/ModalContainer"; import { NamCurrency } from "App/Common/NamCurrency"; import { TableRowLoading } from "App/Common/TableRowLoading"; -import { TransactionFees } from "App/Common/TransactionFees"; +import { TransactionFeeButton } from "App/Common/TransactionFeeButton"; import { routes } from "App/routes"; import { defaultAccountAtom } from "atoms/accounts"; import { chainParametersAtom } from "atoms/chain"; @@ -215,10 +215,9 @@ export const Unstake = (): JSX.Element => { : validationMessage || "Unstake"}
{gasConfig && ( - +
+ +
)} diff --git a/apps/namadillo/src/App/Transfer/TransferDestination.tsx b/apps/namadillo/src/App/Transfer/TransferDestination.tsx index 5844cd6b83..ab1582f7db 100644 --- a/apps/namadillo/src/App/Transfer/TransferDestination.tsx +++ b/apps/namadillo/src/App/Transfer/TransferDestination.tsx @@ -1,14 +1,15 @@ import { Chain } from "@chain-registry/types"; import { Stack } from "@namada/components"; import { TabSelector } from "App/Common/TabSelector"; +import { TransactionFee } from "App/Common/TransactionFee"; +import { TransactionFeeButton } from "App/Common/TransactionFeeButton"; import clsx from "clsx"; -import { Address, WalletProvider } from "types"; +import { Address, GasConfig, WalletProvider } from "types"; import { ConnectProviderButton } from "./ConnectProviderButton"; import { CustomAddressForm } from "./CustomAddressForm"; import { SelectedChain } from "./SelectedChain"; import { SelectedWallet } from "./SelectedWallet"; -import { TransactionFee } from "./TransferModule"; -import { TransferTransactionFee } from "./TransferTransactionFee"; +import ibcTransferImageWhite from "./assets/ibc-transfer-white.png"; type TransferDestinationProps = { isShielded?: boolean; @@ -17,7 +18,7 @@ type TransferDestinationProps = { wallet?: WalletProvider; walletAddress?: string; className?: string; - transactionFee?: TransactionFee; + gasConfig?: GasConfig; customAddressActive?: boolean; isIbcTransfer?: boolean; openChainSelector?: () => void; @@ -36,7 +37,7 @@ export const TransferDestination = ({ isShielded, isIbcTransfer, onChangeShielded, - transactionFee, + gasConfig, customAddressActive, onToggleCustomAddress, address, @@ -119,12 +120,18 @@ export const TransferDestination = ({ /> )} - {transactionFee && ( -