From 5bac4b29fc5d022870315fe1ec8f8cb2ddde7473 Mon Sep 17 00:00:00 2001 From: Hector Bulgarini Date: Mon, 8 Nov 2021 15:19:02 +0100 Subject: [PATCH] Select which chain pays the fee for the dispatch call fee. (#300) * First approach of basic functionality. Need styling and tests * Work in progress. Need to adjust the select on fee selector * Finishing last details. Functionality ready --- src/actions/transactionActions.ts | 12 ++- src/components/EstimatedFee.tsx | 69 ++++++--------- src/components/FeePaySelector.tsx | 82 ++++++++++++++++++ src/components/FeeValue.tsx | 85 +++++++++++++++++++ src/components/Transfer.tsx | 2 + .../transactions/useEstimatedFeePayload.ts | 3 +- src/reducers/transactionReducer.ts | 7 ++ 7 files changed, 212 insertions(+), 48 deletions(-) create mode 100644 src/components/FeePaySelector.tsx create mode 100644 src/components/FeeValue.tsx diff --git a/src/actions/transactionActions.ts b/src/actions/transactionActions.ts index 4c724efc..83e20aa1 100644 --- a/src/actions/transactionActions.ts +++ b/src/actions/transactionActions.ts @@ -24,7 +24,8 @@ import { UpdatedTransactionStatusType, ReceiverPayload, PayloadEstimatedFee, - TransactionState + TransactionState, + PayFee } from '../types/transactionTypes'; enum TransactionActionTypes { @@ -48,7 +49,8 @@ enum TransactionActionTypes { SET_TRANSFER_TYPE = 'SET_TRANSFER_TYPE', ENABLE_TX_BUTTON = 'ENABLE_TX_BUTTON', DISABLE_TX_BUTTON = 'DISABLE_TX_BUTTON', - RESET = 'RESET' + RESET = 'RESET', + CHANGE_DISPATCH_FEE_PAY_CHAIN = 'CHANGE_DISPATCH_FEE_PAY_CHAIN' } const setTransferAmount = (transferAmount: string | null, chainDecimals?: number) => { @@ -193,6 +195,11 @@ const disableTXButton = () => ({ type: TransactionActionTypes.DISABLE_TX_BUTTON }); +const changeDispatchFeePayChain = (payFee: PayFee) => ({ + payload: { payFee }, + type: TransactionActionTypes.CHANGE_DISPATCH_FEE_PAY_CHAIN +}); + const TransactionActionCreators = { setSender, setAction, @@ -212,6 +219,7 @@ const TransactionActionCreators = { setTransferType, enableTxButton, disableTXButton, + changeDispatchFeePayChain, reset }; diff --git a/src/components/EstimatedFee.tsx b/src/components/EstimatedFee.tsx index c8bf9333..e9651831 100644 --- a/src/components/EstimatedFee.tsx +++ b/src/components/EstimatedFee.tsx @@ -15,26 +15,18 @@ // along with Parity Bridges UI. If not, see . import React from 'react'; -import { Typography, Tooltip } from '@material-ui/core'; -import { fade, makeStyles } from '@material-ui/core/styles'; -import HelpOutlineIcon from '@material-ui/icons/HelpOutline'; +import { Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import { useSourceTarget } from '../contexts/SourceTargetContextProvider'; import { useTransactionContext } from '../contexts/TransactionContext'; import { Alert } from '.'; import { formatBalance } from '@polkadot/util'; +import FeeValue from './FeeValue'; const useStyles = makeStyles((theme) => ({ container: { - minHeight: '20px', - display: 'flex' - }, - tooltipIcon: { - ...theme.typography.body1, - marginTop: 2, - marginLeft: 2, - '&:not(:hover)': { - color: fade(theme.palette.text.hint, 0.75) - } + display: 'flex', + marginTop: theme.spacing(1) } })); @@ -43,7 +35,7 @@ const getFormattedAmount = (fee: string | null, chainDecimals: number, chainToke ? formatBalance(fee, { decimals: chainDecimals, withUnit: chainTokens, - withSi: true + withSi: false }) : null; @@ -74,38 +66,27 @@ export const EstimatedFee = (): React.ReactElement => { const estimatedSourceFeeAmount = getFormattedAmount(estimatedSourceFee, srcChainDecimals, srcChainTokens[0]); const targetFeeAmount = getFormattedAmount(estimatedTargetFee, tarChainDecimals, tarChainTokens[0]); - const feeLabel = `Estimated ${sourceChainDetails.chain} fee`; - const feeLabelTarget = `Estimated ${targetChainDetails.chain} fee`; - return evaluateTransactionStatusError ? ( {evaluateTransactionStatusError} ) : ( - <> -
- - {payloadEstimatedFeeLoading && !transactionRunning - ? `${feeLabel}...` - : estimatedSourceFeeAmount - ? `${feeLabel}: ${estimatedSourceFeeAmount} ` - : null} - - {!payloadEstimatedFeeLoading && !transactionRunning && estimatedFeeMessageDeliveryAmount && ( - - - - )} -
- - {payloadEstimatedFeeLoading && !transactionRunning - ? `${feeLabelTarget}...` - : targetFeeAmount - ? `${feeLabelTarget}: ${targetFeeAmount}` - : null} - - + + {payloadEstimatedFeeLoading && !transactionRunning ? ( + 'Calculating fee...' + ) : estimatedSourceFeeAmount && targetFeeAmount ? ( +
+ + Estimated Fee value + + + + +
+ ) : null} +
); }; diff --git a/src/components/FeePaySelector.tsx b/src/components/FeePaySelector.tsx new file mode 100644 index 00000000..e61a1f28 --- /dev/null +++ b/src/components/FeePaySelector.tsx @@ -0,0 +1,82 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges UI. +// +// Parity Bridges UI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Parity Bridges UI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Parity Bridges UI. If not, see . + +import React, { useCallback } from 'react'; +import { Typography, Select, MenuItem, makeStyles } from '@material-ui/core'; +import ChainLogo from './ChainLogo'; +import { PayFee } from '../types/transactionTypes'; +import { useSourceTarget } from '../contexts/SourceTargetContextProvider'; +import { useTransactionContext, useUpdateTransactionContext } from '../contexts/TransactionContext'; +import { TransactionActionCreators } from '../actions/transactionActions'; + +const useStyles = makeStyles((theme) => ({ + container: { + minHeight: theme.spacing(2), + display: 'flex', + alignItems: 'center' + }, + item: { + marginLeft: theme.spacing(0.8) + } +})); + +export default function FeePaySelector() { + const classes = useStyles(); + const sourceTargetDetails = useSourceTarget(); + const { dispatchTransaction } = useUpdateTransactionContext(); + const { payFee } = useTransactionContext(); + + const { + sourceChainDetails: { chain: sourceChain }, + targetChainDetails: { chain: targetChain } + } = sourceTargetDetails; + + const onChange = useCallback( + (event) => { + dispatchTransaction(TransactionActionCreators.changeDispatchFeePayChain(event.target.value)); + }, + [dispatchTransaction] + ); + + const chain = payFee === PayFee.AtSourceChain ? sourceChain : targetChain; + + return ( +
+ + Dispatch fee payed on + +
+ +
+ +
+ +
+
+ ); +} diff --git a/src/components/FeeValue.tsx b/src/components/FeeValue.tsx new file mode 100644 index 00000000..fbad09f6 --- /dev/null +++ b/src/components/FeeValue.tsx @@ -0,0 +1,85 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges UI. +// +// Parity Bridges UI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Parity Bridges UI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Parity Bridges UI. If not, see . + +import React from 'react'; +import { Typography, Tooltip } from '@material-ui/core'; +import { fade, makeStyles } from '@material-ui/core/styles'; +import HelpOutlineIcon from '@material-ui/icons/HelpOutline'; + +const useStyles = makeStyles((theme) => ({ + container: { + minHeight: '20px', + display: 'flex' + }, + tooltipIcon: { + ...theme.typography.body1, + marginTop: 2, + marginLeft: 2, + '&:not(:hover)': { + color: fade(theme.palette.text.hint, 0.75) + } + }, + value: { + backgroundColor: '#f5dada', + fontFamily: 'monospace', + paddingLeft: theme.spacing(0.7), + paddingRight: theme.spacing(0.7), + borderRadius: '3px' + }, + textContainer: { + marginLeft: theme.spacing(0.5) + } +})); + +interface Props { + chainTokens: string; + amount: string; + tooltip?: string; + showPlus?: boolean; +} + +interface TextStyle { + text: string; + background?: boolean; +} + +const CustomTypography = ({ text, background = false }: TextStyle) => { + const classes = useStyles(); + return ( +
+ + {text} + +
+ ); +}; + +export default function FeeValue({ tooltip = '', chainTokens, amount, showPlus = false }: Props) { + const classes = useStyles(); + return ( +
+ + + + {tooltip && ( + + + + )} + {showPlus && } +
+ ); +} diff --git a/src/components/Transfer.tsx b/src/components/Transfer.tsx index ca8f75f9..c49255a3 100644 --- a/src/components/Transfer.tsx +++ b/src/components/Transfer.tsx @@ -29,6 +29,7 @@ import { EstimatedFee } from '../components/EstimatedFee'; import { DebouncedTextField } from './DebouncedTextField'; import { useInternalTransfer } from '../hooks/chain/useInternalTransfer'; import { useGUIContext } from '../contexts/GUIContextProvider'; +import FeePaySelector from './FeePaySelector'; const useStyles = makeStyles((theme) => ({ inputAmount: { @@ -116,6 +117,7 @@ function Transfer() { {buttonLabel} + ); diff --git a/src/hooks/transactions/useEstimatedFeePayload.ts b/src/hooks/transactions/useEstimatedFeePayload.ts index 811d5961..ef98512a 100644 --- a/src/hooks/transactions/useEstimatedFeePayload.ts +++ b/src/hooks/transactions/useEstimatedFeePayload.ts @@ -28,7 +28,6 @@ import useLaneId from '../chain/useLaneId'; import { getSubstrateDynamicNames } from '../../util/getSubstrateDynamicNames'; import { genericCall } from '../../util/apiUtlis'; import { - PayFee, PayloadEstimatedFee, TransactionsActionType, TransactionState, @@ -122,7 +121,7 @@ export const useEstimatedFeePayload = ( origin: { SourceAccount: account!.addressRaw }, - dispatch_fee_payment: PayFee.AtSourceChain, + dispatch_fee_payment: currentTransactionState.payFee, spec_version: targetApi.consts.system.version.specVersion.toNumber(), weight }; diff --git a/src/reducers/transactionReducer.ts b/src/reducers/transactionReducer.ts index 7da695c7..7cdd1994 100644 --- a/src/reducers/transactionReducer.ts +++ b/src/reducers/transactionReducer.ts @@ -337,6 +337,13 @@ export default function transactionReducer(state: TransactionState, action: Tran transactionReadyToExecute: false }; } + case TransactionActionTypes.CHANGE_DISPATCH_FEE_PAY_CHAIN: { + return { + ...state, + payFee: action.payload.payFee, + shouldEvaluatePayloadEstimatedFee: true + }; + } default: throw new Error(`Unknown type: ${action.type}`); }