From 7882eccac48ef3046febc99f298b2a7a4428a3be Mon Sep 17 00:00:00 2001 From: Kevin Peters Date: Fri, 7 Jan 2022 16:06:33 +0000 Subject: [PATCH] bridge_ui: Terra fee denomination support --- bridge_ui/src/components/Attest/Create.tsx | 6 +- bridge_ui/src/components/Attest/Send.tsx | 8 +- .../src/components/LowBalanceWarning.tsx | 22 ++-- bridge_ui/src/components/NFT/Redeem.tsx | 5 + bridge_ui/src/components/NFT/Send.tsx | 5 + .../src/components/TerraFeeDenomPicker.tsx | 107 ++++++++++++++++++ bridge_ui/src/components/Transfer/Redeem.tsx | 5 + bridge_ui/src/components/Transfer/Send.tsx | 6 +- .../src/components/WithdrawTokensTerra.tsx | 18 ++- bridge_ui/src/hooks/useHandleAttest.tsx | 12 +- .../src/hooks/useHandleCreateWrapped.tsx | 18 ++- bridge_ui/src/hooks/useHandleRedeem.tsx | 14 ++- bridge_ui/src/hooks/useHandleTransfer.tsx | 12 +- bridge_ui/src/hooks/useTransactionFees.tsx | 75 +++++++++--- bridge_ui/src/store/feeSlice.ts | 25 ++++ bridge_ui/src/store/index.ts | 2 + bridge_ui/src/store/selectors.ts | 4 + bridge_ui/src/utils/consts.ts | 1 + bridge_ui/src/utils/terra.ts | 7 +- 19 files changed, 303 insertions(+), 49 deletions(-) create mode 100644 bridge_ui/src/components/TerraFeeDenomPicker.tsx create mode 100644 bridge_ui/src/store/feeSlice.ts diff --git a/bridge_ui/src/components/Attest/Create.tsx b/bridge_ui/src/components/Attest/Create.tsx index 8a70ee695..65912a2f1 100644 --- a/bridge_ui/src/components/Attest/Create.tsx +++ b/bridge_ui/src/components/Attest/Create.tsx @@ -1,3 +1,4 @@ +import { CHAIN_ID_TERRA } from "@certusone/wormhole-sdk"; import { CircularProgress, makeStyles } from "@material-ui/core"; import { useSelector } from "react-redux"; import useFetchForeignAsset from "../../hooks/useFetchForeignAsset"; @@ -10,6 +11,7 @@ import { } from "../../store/selectors"; import ButtonWithLoader from "../ButtonWithLoader"; import KeyAndBalance from "../KeyAndBalance"; +import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; const useStyles = makeStyles((theme) => ({ @@ -45,7 +47,9 @@ function Create() { return ( <> - + {targetChain === CHAIN_ID_TERRA && ( + + )} {foreignAssetInfo.isFetching ? ( <>
diff --git a/bridge_ui/src/components/Attest/Send.tsx b/bridge_ui/src/components/Attest/Send.tsx index b1cfdf166..528994751 100644 --- a/bridge_ui/src/components/Attest/Send.tsx +++ b/bridge_ui/src/components/Attest/Send.tsx @@ -1,4 +1,4 @@ -import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; +import { CHAIN_ID_SOLANA, CHAIN_ID_TERRA } from "@certusone/wormhole-sdk"; import { Alert } from "@material-ui/lab"; import { Link, makeStyles } from "@material-ui/core"; import { useMemo } from "react"; @@ -17,6 +17,7 @@ import KeyAndBalance from "../KeyAndBalance"; import TransactionProgress from "../TransactionProgress"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; import { SOLANA_TOKEN_METADATA_PROGRAM_URL } from "../../utils/consts"; +import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; const useStyles = makeStyles((theme) => ({ alert: { @@ -62,6 +63,9 @@ function Send() { return ( <> + {sourceChain === CHAIN_ID_TERRA && ( + + )} Attest - {sourceChain === CHAIN_ID_SOLANA ? : null} + {sourceChain === CHAIN_ID_SOLANA && } {warningMessage} - - {"Current balance: " + transactionFeeWarning.balanceString} - + {chainId !== CHAIN_ID_TERRA ? ( + + {"Current balance: " + transactionFeeWarning.balanceString} + + ) : null} ); diff --git a/bridge_ui/src/components/NFT/Redeem.tsx b/bridge_ui/src/components/NFT/Redeem.tsx index 29facf6a8..94ad98f40 100644 --- a/bridge_ui/src/components/NFT/Redeem.tsx +++ b/bridge_ui/src/components/NFT/Redeem.tsx @@ -1,3 +1,4 @@ +import { CHAIN_ID_TERRA } from "@certusone/wormhole-sdk"; import { useSelector } from "react-redux"; import { useHandleNFTRedeem } from "../../hooks/useHandleNFTRedeem"; import useIsWalletReady from "../../hooks/useIsWalletReady"; @@ -5,6 +6,7 @@ import { selectNFTTargetChain } from "../../store/selectors"; import ButtonWithLoader from "../ButtonWithLoader"; import KeyAndBalance from "../KeyAndBalance"; import StepDescription from "../StepDescription"; +import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; function Redeem() { @@ -15,6 +17,9 @@ function Redeem() { <> Receive the NFT on the target chain + {targetChain === CHAIN_ID_TERRA && ( + + )} + {sourceChain === CHAIN_ID_TERRA && ( + + )} This will initiate the transfer on {CHAINS_BY_ID[sourceChain].name} and wait for finalization. If you navigate away from this page before diff --git a/bridge_ui/src/components/TerraFeeDenomPicker.tsx b/bridge_ui/src/components/TerraFeeDenomPicker.tsx new file mode 100644 index 000000000..820bdd26d --- /dev/null +++ b/bridge_ui/src/components/TerraFeeDenomPicker.tsx @@ -0,0 +1,107 @@ +import { + MenuItem, + makeStyles, + TextField, + Typography, + ListItemIcon, +} from "@material-ui/core"; +import { useConnectedWallet } from "@terra-money/wallet-provider"; +import { useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { setTerraFeeDenom } from "../store/feeSlice"; +import { selectTerraFeeDenom } from "../store/selectors"; +import useTerraNativeBalances from "../hooks/useTerraNativeBalances"; +import { formatNativeDenom, getNativeTerraIcon } from "../utils/terra"; + +const useStyles = makeStyles((theme) => ({ + feePickerContainer: { + display: "flex", + flexDirection: "column", + margin: `${theme.spacing(1)}px auto`, + maxWidth: 200, + width: "100%", + }, + select: { + "& .MuiSelect-root": { + display: "flex", + alignItems: "center", + }, + }, + listItemIcon: { + minWidth: 40, + }, + icon: { + height: 24, + maxWidth: 24, + }, +})); + +type TerraFeeDenomPickerProps = { + disabled: boolean; +}; + +export default function TerraFeeDenomPicker(props: TerraFeeDenomPickerProps) { + const terraFeeDenom = useSelector(selectTerraFeeDenom); + const wallet = useConnectedWallet(); + const { balances } = useTerraNativeBalances(wallet?.walletAddress); + const dispatch = useDispatch(); + const classes = useStyles(); + + const feeDenomItems = useMemo(() => { + const items = []; + if (balances) { + for (const [denom, amount] of Object.entries(balances)) { + if (amount === "0") continue; + const symbol = formatNativeDenom(denom); + if (symbol) { + items.push({ + denom, + symbol, + icon: getNativeTerraIcon(symbol), + }); + } + } + } + // prevent an out-of-range value from being selected + if (!items.find((item) => item.denom === terraFeeDenom)) { + const symbol = formatNativeDenom(terraFeeDenom); + items.push({ + denom: terraFeeDenom, + symbol, + icon: getNativeTerraIcon(symbol), + }); + } + return items; + }, [balances, terraFeeDenom]); + + return ( +
+ Fee Denomination + dispatch(setTerraFeeDenom(event.target.value))} + disabled={props.disabled} + className={classes.select} + > + {feeDenomItems.map((item) => { + return ( + + + {item.symbol} + + {item.symbol} + + ); + })} + +
+ ); +} diff --git a/bridge_ui/src/components/Transfer/Redeem.tsx b/bridge_ui/src/components/Transfer/Redeem.tsx index 7c8302653..82d806588 100644 --- a/bridge_ui/src/components/Transfer/Redeem.tsx +++ b/bridge_ui/src/components/Transfer/Redeem.tsx @@ -6,6 +6,7 @@ import { CHAIN_ID_OASIS, CHAIN_ID_POLYGON, CHAIN_ID_SOLANA, + CHAIN_ID_TERRA, isEVMChain, WSOL_ADDRESS, } from "@certusone/wormhole-sdk"; @@ -41,6 +42,7 @@ import KeyAndBalance from "../KeyAndBalance"; import SmartAddress from "../SmartAddress"; import { SolanaCreateAssociatedAddressAlternate } from "../SolanaCreateAssociatedAddress"; import StepDescription from "../StepDescription"; +import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import AddToMetamask from "./AddToMetamask"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; @@ -112,6 +114,9 @@ function Redeem() { <> Receive the tokens on the target chain + {targetChain === CHAIN_ID_TERRA && ( + + )} {isNativeEligible && ( + {sourceChain === CHAIN_ID_TERRA && ( + + )} This will initiate the transfer on {CHAINS_BY_ID[sourceChain].name} and wait for finalization. If you navigate away from this page before diff --git a/bridge_ui/src/components/WithdrawTokensTerra.tsx b/bridge_ui/src/components/WithdrawTokensTerra.tsx index 0ff179fbe..ea1478e0f 100644 --- a/bridge_ui/src/components/WithdrawTokensTerra.tsx +++ b/bridge_ui/src/components/WithdrawTokensTerra.tsx @@ -22,6 +22,9 @@ import { postWithFees, waitForTerraExecution } from "../utils/terra"; import ButtonWithLoader from "./ButtonWithLoader"; import { useSnackbar } from "notistack"; import { Alert } from "@material-ui/lab"; +import { useSelector } from "react-redux"; +import { selectTerraFeeDenom } from "../store/selectors"; +import TerraFeeDenomPicker from "./TerraFeeDenomPicker"; const useStyles = makeStyles((theme) => ({ formControl: { @@ -33,7 +36,11 @@ const useStyles = makeStyles((theme) => ({ }, })); -const withdraw = async (wallet: ConnectedWallet, token: string) => { +const withdraw = async ( + wallet: ConnectedWallet, + token: string, + feeDenom: string +) => { const withdraw = new MsgExecuteContract( wallet.walletAddress, TERRA_TOKEN_BRIDGE_ADDRESS, @@ -51,7 +58,8 @@ const withdraw = async (wallet: ConnectedWallet, token: string) => { const txResult = await postWithFees( wallet, [withdraw], - "Wormhole - Withdraw Tokens" + "Wormhole - Withdraw Tokens", + [feeDenom] ); await waitForTerraExecution(txResult); }; @@ -62,13 +70,14 @@ export default function WithdrawTokensTerra() { const [isLoading, setIsLoading] = useState(false); const classes = useStyles(); const { enqueueSnackbar } = useSnackbar(); + const feeDenom = useSelector(selectTerraFeeDenom); const handleClick = useCallback(() => { if (wallet) { (async () => { setIsLoading(true); try { - await withdraw(wallet, token); + await withdraw(wallet, token, feeDenom); enqueueSnackbar(null, { content: Transaction confirmed., }); @@ -81,7 +90,7 @@ export default function WithdrawTokensTerra() { setIsLoading(false); })(); } - }, [wallet, token, enqueueSnackbar]); + }, [wallet, token, enqueueSnackbar, feeDenom]); return ( @@ -103,6 +112,7 @@ export default function WithdrawTokensTerra() { ))} + { if (isEVMChain(sourceChain) && !!signer) { @@ -218,7 +223,7 @@ export function useHandleAttest() { } else if (sourceChain === CHAIN_ID_SOLANA && !!solanaWallet && !!solPK) { solana(dispatch, enqueueSnackbar, solPK, sourceAsset, solanaWallet); } else if (sourceChain === CHAIN_ID_TERRA && !!terraWallet) { - terra(dispatch, enqueueSnackbar, terraWallet, sourceAsset); + terra(dispatch, enqueueSnackbar, terraWallet, sourceAsset, terraFeeDenom); } else { } }, [ @@ -230,6 +235,7 @@ export function useHandleAttest() { solPK, terraWallet, sourceAsset, + terraFeeDenom, ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx index 2f9c73e77..79054b305 100644 --- a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx +++ b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx @@ -28,6 +28,7 @@ import { setCreateTx, setIsCreating } from "../store/attestSlice"; import { selectAttestIsCreating, selectAttestTargetChain, + selectTerraFeeDenom, } from "../store/selectors"; import { getTokenBridgeAddressForChain, @@ -133,7 +134,8 @@ async function terra( enqueueSnackbar: any, wallet: ConnectedWallet, signedVAA: Uint8Array, - shouldUpdate: boolean + shouldUpdate: boolean, + feeDenom: string ) { dispatch(setIsCreating(true)); try { @@ -151,7 +153,8 @@ async function terra( const result = await postWithFees( wallet, [msg], - "Wormhole - Create Wrapped" + "Wormhole - Create Wrapped", + [feeDenom] ); dispatch( setCreateTx({ id: result.result.txhash, block: result.result.height }) @@ -177,6 +180,7 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) { const isCreating = useSelector(selectAttestIsCreating); const { signer } = useEthereumProvider(); const terraWallet = useConnectedWallet(); + const terraFeeDenom = useSelector(selectTerraFeeDenom); const handleCreateClick = useCallback(() => { if (isEVMChain(targetChain) && !!signer && !!signedVAA) { evm( @@ -202,7 +206,14 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) { shouldUpdate ); } else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && !!signedVAA) { - terra(dispatch, enqueueSnackbar, terraWallet, signedVAA, shouldUpdate); + terra( + dispatch, + enqueueSnackbar, + terraWallet, + signedVAA, + shouldUpdate, + terraFeeDenom + ); } else { // enqueueSnackbar( // "Creating wrapped tokens on this chain is not yet supported", @@ -221,6 +232,7 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) { signedVAA, signer, shouldUpdate, + terraFeeDenom, ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useHandleRedeem.tsx b/bridge_ui/src/hooks/useHandleRedeem.tsx index 3d4e9407d..b07af2afc 100644 --- a/bridge_ui/src/hooks/useHandleRedeem.tsx +++ b/bridge_ui/src/hooks/useHandleRedeem.tsx @@ -24,6 +24,7 @@ import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import useTransferSignedVAA from "./useTransferSignedVAA"; import { + selectTerraFeeDenom, selectTransferIsRedeeming, selectTransferTargetChain, } from "../store/selectors"; @@ -132,7 +133,8 @@ async function terra( dispatch: any, enqueueSnackbar: any, wallet: ConnectedWallet, - signedVAA: Uint8Array + signedVAA: Uint8Array, + feeDenom: string ) { dispatch(setIsRedeeming(true)); try { @@ -144,7 +146,8 @@ async function terra( const result = await postWithFees( wallet, [msg], - "Wormhole - Complete Transfer" + "Wormhole - Complete Transfer", + [feeDenom] ); dispatch( setRedeemTx({ id: result.result.txhash, block: result.result.height }) @@ -168,6 +171,7 @@ export function useHandleRedeem() { const solPK = solanaWallet?.publicKey; const { signer } = useEthereumProvider(); const terraWallet = useConnectedWallet(); + const terraFeeDenom = useSelector(selectTerraFeeDenom); const signedVAA = useTransferSignedVAA(); const isRedeeming = useSelector(selectTransferIsRedeeming); const handleRedeemClick = useCallback(() => { @@ -188,7 +192,7 @@ export function useHandleRedeem() { false ); } else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) { - terra(dispatch, enqueueSnackbar, terraWallet, signedVAA); + terra(dispatch, enqueueSnackbar, terraWallet, signedVAA, terraFeeDenom); } else { } }, [ @@ -200,6 +204,7 @@ export function useHandleRedeem() { solanaWallet, solPK, terraWallet, + terraFeeDenom, ]); const handleRedeemNativeClick = useCallback(() => { @@ -220,7 +225,7 @@ export function useHandleRedeem() { true ); } else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) { - terra(dispatch, enqueueSnackbar, terraWallet, signedVAA); //TODO isNative = true + terra(dispatch, enqueueSnackbar, terraWallet, signedVAA, terraFeeDenom); //TODO isNative = true } else { } }, [ @@ -232,6 +237,7 @@ export function useHandleRedeem() { solanaWallet, solPK, terraWallet, + terraFeeDenom, ]); return useMemo( diff --git a/bridge_ui/src/hooks/useHandleTransfer.tsx b/bridge_ui/src/hooks/useHandleTransfer.tsx index 020ce914a..2883db740 100644 --- a/bridge_ui/src/hooks/useHandleTransfer.tsx +++ b/bridge_ui/src/hooks/useHandleTransfer.tsx @@ -32,6 +32,7 @@ import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { + selectTerraFeeDenom, selectTransferAmount, selectTransferIsSendComplete, selectTransferIsSending, @@ -216,7 +217,8 @@ async function terra( amount: string, decimals: number, targetChain: ChainId, - targetAddress: Uint8Array + targetAddress: Uint8Array, + feeDenom: string ) { dispatch(setIsSending(true)); try { @@ -233,7 +235,8 @@ async function terra( const result = await postWithFees( wallet, msgs, - "Wormhole - Initiate Transfer" + "Wormhole - Initiate Transfer", + [feeDenom] ); const info = await waitForTerraExecution(result); @@ -286,6 +289,7 @@ export function useHandleTransfer() { const solanaWallet = useSolanaWallet(); const solPK = solanaWallet?.publicKey; const terraWallet = useConnectedWallet(); + const terraFeeDenom = useSelector(selectTerraFeeDenom); const sourceParsedTokenAccount = useSelector( selectTransferSourceParsedTokenAccount ); @@ -353,7 +357,8 @@ export function useHandleTransfer() { amount, decimals, targetChain, - targetAddress + targetAddress, + terraFeeDenom ); } else { } @@ -374,6 +379,7 @@ export function useHandleTransfer() { originAsset, originChain, isNative, + terraFeeDenom, ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useTransactionFees.tsx b/bridge_ui/src/hooks/useTransactionFees.tsx index 6ceb6bd86..e844f67b8 100644 --- a/bridge_ui/src/hooks/useTransactionFees.tsx +++ b/bridge_ui/src/hooks/useTransactionFees.tsx @@ -37,9 +37,14 @@ export type MethodType = "nft" | "createWrapped" | "transfer"; //rather than a hardcoded value. const SOLANA_THRESHOLD_LAMPORTS: bigint = BigInt(300000); const ETHEREUM_THRESHOLD_WEI: bigint = BigInt(35000000000000000); -const TERRA_THRESHOLD_ULUNA: bigint = BigInt(500000); +const TERRA_THRESHOLD_ULUNA: bigint = BigInt(100000); +const TERRA_THRESHOLD_UUSD: bigint = BigInt(10000000); -const isSufficientBalance = (chainId: ChainId, balance: bigint | undefined) => { +const isSufficientBalance = ( + chainId: ChainId, + balance: bigint | undefined, + terraFeeDenom?: string +) => { if (balance === undefined || !chainId) { return true; } @@ -49,13 +54,33 @@ const isSufficientBalance = (chainId: ChainId, balance: bigint | undefined) => { if (isEVMChain(chainId)) { return balance > ETHEREUM_THRESHOLD_WEI; } - if (CHAIN_ID_TERRA === chainId) { + if (terraFeeDenom === "uluna") { return balance > TERRA_THRESHOLD_ULUNA; } + if (terraFeeDenom === "uusd") { + return balance > TERRA_THRESHOLD_UUSD; + } return true; }; +type TerraBalance = { + denom: string; + balance: bigint; +}; + +const isSufficientBalanceTerra = (balances: TerraBalance[]) => { + return balances.some(({ denom, balance }) => { + if (denom === "uluna") { + return balance > TERRA_THRESHOLD_ULUNA; + } + if (denom === "uusd") { + return balance > TERRA_THRESHOLD_UUSD; + } + return false; + }); +}; + //TODO move to more generic location const getBalanceSolana = async (walletAddress: string) => { const connection = new Connection(SOLANA_HOST); @@ -77,18 +102,25 @@ const getBalanceEvm = async (walletAddress: string, provider: Provider) => { return provider.getBalance(walletAddress).then((result) => result.toBigInt()); }; -const getBalanceTerra = async (walletAddress: string) => { - const TARGET_DENOM = "uluna"; +const getBalancesTerra = async (walletAddress: string) => { + const TARGET_DENOMS = ["uluna", "uusd"]; const lcd = new LCDClient(TERRA_HOST); return lcd.bank .balance(walletAddress) .then((coins) => { - // coins doesn't support reduce - const balancePairs = coins.map(({ amount, denom }) => [denom, amount]); - const targetCoin = balancePairs.find((coin) => coin[0] === TARGET_DENOM); - if (targetCoin) { - return BigInt(targetCoin[1].toString()); + const balances = coins + .filter(({ denom }) => { + return TARGET_DENOMS.includes(denom); + }) + .map(({ amount, denom }) => { + return { + denom, + balance: BigInt(amount.toString()), + }; + }); + if (balances) { + return balances; } else { return Promise.reject(); } @@ -115,6 +147,7 @@ export default function useTransactionFees(chainId: ChainId) { const { walletAddress, isReady } = useIsWalletReady(chainId); const { provider } = useEthereumProvider(); const [balance, setBalance] = useState(undefined); + const [terraBalances, setTerraBalances] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(""); @@ -157,12 +190,17 @@ export default function useTransactionFees(chainId: ChainId) { } } else if (chainId === CHAIN_ID_TERRA && isReady && walletAddress) { loadStart(); - getBalanceTerra(walletAddress).then( - (result) => { - const adjustedresult = - result === undefined || result === null ? BigInt(0) : result; + getBalancesTerra(walletAddress).then( + (results) => { + const adjustedResults = results.map(({ denom, balance }) => { + return { + denom, + balance: + balance === undefined || balance === null ? BigInt(0) : balance, + }; + }); setIsLoading(false); - setBalance(adjustedresult); + setTerraBalances(adjustedResults); }, (error) => { setIsLoading(false); @@ -174,13 +212,16 @@ export default function useTransactionFees(chainId: ChainId) { const results = useMemo(() => { return { - isSufficientBalance: isSufficientBalance(chainId, balance), + isSufficientBalance: + chainId === CHAIN_ID_TERRA + ? isSufficientBalanceTerra(terraBalances) + : isSufficientBalance(chainId, balance), balance, balanceString: toBalanceString(balance, chainId), isLoading, error, }; - }, [balance, chainId, isLoading, error]); + }, [balance, terraBalances, chainId, isLoading, error]); return results; } diff --git a/bridge_ui/src/store/feeSlice.ts b/bridge_ui/src/store/feeSlice.ts new file mode 100644 index 000000000..a28a3f1a7 --- /dev/null +++ b/bridge_ui/src/store/feeSlice.ts @@ -0,0 +1,25 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { TERRA_DEFAULT_FEE_DENOM } from "../utils/consts"; + +export interface FeeSliceState { + terraFeeDenom: string; +} + +const initialState: FeeSliceState = { + terraFeeDenom: TERRA_DEFAULT_FEE_DENOM, +}; + +export const feeSlice = createSlice({ + name: "fee", + initialState, + reducers: { + setTerraFeeDenom: (state, action: PayloadAction) => { + state.terraFeeDenom = action.payload; + }, + reset: () => initialState, + }, +}); + +export const { setTerraFeeDenom, reset } = feeSlice.actions; + +export default feeSlice.reducer; diff --git a/bridge_ui/src/store/index.ts b/bridge_ui/src/store/index.ts index 83433585c..f04cad5cf 100644 --- a/bridge_ui/src/store/index.ts +++ b/bridge_ui/src/store/index.ts @@ -3,6 +3,7 @@ import attestReducer from "./attestSlice"; import nftReducer from "./nftSlice"; import transferReducer from "./transferSlice"; import tokenReducer from "./tokenSlice"; +import feeReducer from "./feeSlice"; export const store = configureStore({ reducer: { @@ -10,6 +11,7 @@ export const store = configureStore({ nft: nftReducer, transfer: transferReducer, tokens: tokenReducer, + fee: feeReducer, }, }); diff --git a/bridge_ui/src/store/selectors.ts b/bridge_ui/src/store/selectors.ts index bb792fc12..cae063eed 100644 --- a/bridge_ui/src/store/selectors.ts +++ b/bridge_ui/src/store/selectors.ts @@ -307,3 +307,7 @@ export const selectTerraTokenMap = (state: RootState) => { export const selectMarketsMap = (state: RootState) => { return state.tokens.marketsMap; }; + +export const selectTerraFeeDenom = (state: RootState) => { + return state.fee.terraFeeDenom; +}; diff --git a/bridge_ui/src/utils/consts.ts b/bridge_ui/src/utils/consts.ts index 15bf8f726..4613b508b 100644 --- a/bridge_ui/src/utils/consts.ts +++ b/bridge_ui/src/utils/consts.ts @@ -789,6 +789,7 @@ export const getMigrationAssetMap = (chainId: ChainId) => { }; export const SUPPORTED_TERRA_TOKENS = ["uluna", "uusd"]; +export const TERRA_DEFAULT_FEE_DENOM = SUPPORTED_TERRA_TOKENS[0]; export const TERRA_FCD_BASE = CLUSTER === "mainnet" diff --git a/bridge_ui/src/utils/terra.ts b/bridge_ui/src/utils/terra.ts index b3ef70d3f..db702f938 100644 --- a/bridge_ui/src/utils/terra.ts +++ b/bridge_ui/src/utils/terra.ts @@ -66,7 +66,8 @@ export const isValidTerraAddress = (address: string) => { export async function postWithFees( wallet: ConnectedWallet, msgs: any[], - memo: string + memo: string, + feeDenoms: string[] ) { // don't try/catch, let errors propagate const lcd = new LCDClient(TERRA_HOST); @@ -81,7 +82,7 @@ export async function postWithFees( [...msgs], { memo, - feeDenoms: ["uluna"], + feeDenoms, gasPrices, } ); @@ -89,7 +90,7 @@ export async function postWithFees( const result = await wallet.post({ msgs: [...msgs], memo, - feeDenoms: ["uluna"], + feeDenoms, gasPrices, fee: feeEstimate, });