From 4b92ba6d21d30c4787110cd53d5e2124bee20d27 Mon Sep 17 00:00:00 2001 From: hbulgarini Date: Mon, 30 Aug 2021 10:17:12 -0300 Subject: [PATCH 1/4] Work in progress --- src/actions/transactionActions.ts | 42 +++++++++---- src/components/EstimatedFee.tsx | 17 ++++++ src/components/Transfer.tsx | 25 ++++---- src/contexts/TransactionContext.tsx | 18 +++--- .../context/useSendersBalancesContext.ts | 2 +- .../transactions/useEstimatedFeePayload.ts | 27 +++++++-- .../transactions/useSenderBalanceUpdates.ts | 59 +++++++++++++++++++ src/reducers/accountReducer.ts | 2 +- src/reducers/transactionReducer.ts | 59 +++++++++++++++---- src/util/transactions/reducer/index.ts | 34 ++++++++++- 10 files changed, 234 insertions(+), 51 deletions(-) create mode 100644 src/hooks/transactions/useSenderBalanceUpdates.ts diff --git a/src/actions/transactionActions.ts b/src/actions/transactionActions.ts index 9baf197c..b31086b5 100644 --- a/src/actions/transactionActions.ts +++ b/src/actions/transactionActions.ts @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges UI. If not, see . +import { BalanceState } from '../types/accountTypes'; import { CreateType } from '../types/apiCallsTypes'; import { SourceTargetState } from '../types/sourceTargetTypes'; @@ -32,7 +33,8 @@ enum TransactionActionTypes { SET_RECEIVER = 'SET_RECEIVER', SET_RECEIVER_ADDRESS = 'SET_RECEIVER_ADDRESS', SET_RECEIVER_VALIDATION = 'SET_RECEIVER_VALIDATION', - SET_SENDER_AND_ACTION = 'SET_SENDER_AND_ACTION', + SET_SENDER = 'SET_SENDER', + SET_ACTION = 'SET_ACTION', SET_PAYLOAD_ESTIMATED_FEE = 'SET_PAYLOAD_ESTIMATED_FEE', CREATE_TRANSACTION_STATUS = 'CREATE_TRANSACTION_STATUS', UPDATE_CURRENT_TRANSACTION_STATUS = 'UPDATE_CURRENT_TRANSACTION_STATUS', @@ -42,6 +44,7 @@ enum TransactionActionTypes { SET_WEIGHT_INPUT = 'SET_WEIGHT_INPUT', UPDATE_TRANSACTIONS_STATUS = 'UPDATE_TRANSACTIONS_STATUS', SET_BATCH_PAYLOAD_ESTIMATED_FEE = 'SET_BATCH_PAYLOAD_ESTIMATED_FEE', + UPDATE_SENDER_BALANCES = 'UPDATE_SENDER_BALANCES', RESET = 'RESET' } @@ -58,7 +61,9 @@ const setPayloadEstimatedFee = ( payloadEstimatedFeeLoading: boolean, sourceTargetDetails: SourceTargetState, createType: CreateType, - isBridged: boolean + isBridged: boolean, + senderAccountBalance: BalanceState | null, + senderCompanionAccountBalance: BalanceState | null ) => ({ payload: { payloadEstimatedFee, @@ -66,7 +71,9 @@ const setPayloadEstimatedFee = ( payloadEstimatedFeeLoading, sourceTargetDetails, createType, - isBridged + isBridged, + senderAccountBalance, + senderCompanionAccountBalance }, type: TransactionActionTypes.SET_PAYLOAD_ESTIMATED_FEE }); @@ -114,13 +121,14 @@ const setTransactionRunning = (transactionRunning: boolean) => ({ type: TransactionActionTypes.SET_TRANSACTION_RUNNING }); -type SenderAndActionInput = { - senderAccount: string | null; - action: TransactionTypes; -}; -const setSenderAndAction = ({ senderAccount, action }: SenderAndActionInput) => ({ - payload: { senderAccount, action }, - type: TransactionActionTypes.SET_SENDER_AND_ACTION +const setAction = (action: TransactionTypes) => ({ + payload: { action }, + type: TransactionActionTypes.SET_ACTION +}); + +const setSender = (senderAccount: string | null) => ({ + payload: { senderAccount }, + type: TransactionActionTypes.SET_SENDER }); const setRemarkInput = (remarkInput: string | null) => ({ @@ -143,8 +151,19 @@ const setBatchedEvaluationPayloadEstimatedFee = (batchedTransactionState: Transa type: TransactionActionTypes.SET_BATCH_PAYLOAD_ESTIMATED_FEE }); +type UpdateSenderBalances = { + senderAccountBalance: BalanceState | null; + senderCompanionAccountBalance: BalanceState | null; +}; + +const updateSenderBalances = ({ senderAccountBalance, senderCompanionAccountBalance }: UpdateSenderBalances) => ({ + payload: { senderAccountBalance, senderCompanionAccountBalance }, + type: TransactionActionTypes.UPDATE_SENDER_BALANCES +}); + const TransactionActionCreators = { - setSenderAndAction, + setSender, + setAction, setReceiverAddress, setReceiver, setTransferAmount, @@ -157,6 +176,7 @@ const TransactionActionCreators = { updateTransactionStatus, updateTransactionsStatus, setBatchedEvaluationPayloadEstimatedFee, + updateSenderBalances, reset }; diff --git a/src/components/EstimatedFee.tsx b/src/components/EstimatedFee.tsx index 83556a87..0207090b 100644 --- a/src/components/EstimatedFee.tsx +++ b/src/components/EstimatedFee.tsx @@ -20,6 +20,7 @@ import React from 'react'; import { useSourceTarget } from '../contexts/SourceTargetContextProvider'; import { useTransactionContext } from '../contexts/TransactionContext'; import { transformToBaseUnit } from '../util/evalUnits'; +import { TransactionTypes } from '../types/transactionTypes'; const useStyles = makeStyles(() => ({ container: { @@ -54,3 +55,19 @@ export const EstimatedFee = (): React.ReactElement => { ); }; + +/* export const EstimatedFee = (): React.ReactElement => { + const { action } = useTransactionContext(); + + useEffect((): void => { + estimatedFee && setEnoughForPayFee(new BN(balance.free).sub(new BN(estimatedFee)).isNeg()); + senderCompanionAccountBalance && + transferAmount && + setEnoughForTransfer(new BN(senderCompanionAccountBalance.free).sub(transferAmount).isNeg()); + }, [transferAmount, estimatedFee, balance, senderCompanionAccountBalance]); + + if (action === TransactionTypes.TRANSFER) { + + } +}; + */ diff --git a/src/components/Transfer.tsx b/src/components/Transfer.tsx index 01bef27f..5aaa569d 100644 --- a/src/components/Transfer.tsx +++ b/src/components/Transfer.tsx @@ -55,16 +55,18 @@ const useStyles = makeStyles((theme) => ({ function Transfer() { const { dispatchTransaction } = useUpdateTransactionContext(); const classes = useStyles(); - const [amountNotCorrect, setAmountNotCorrect] = useState(false); + const [enoughForTransfer, setEnoughForTransfer] = useState(false); + const [enoughForPayFee, setEnoughForPayFee] = useState(false); const { sourceChainDetails, targetChainDetails } = useSourceTarget(); const { isBridged } = useGUIContext(); - const { account } = useAccountContext(); + const { account, senderCompanionAccountBalance } = useAccountContext(); const { estimatedFee, transferAmount, transferAmountError, transactionRunning, - transactionReadyToExecute + transactionReadyToExecute, + evaluateTransactionStatusError } = useTransactionContext(); const { api } = sourceChainDetails.apiConnection; const balance = useBalance(api, account?.address || ''); @@ -99,12 +101,6 @@ function Transfer() { transactionRunning && transferAmount && dispatchCallback(''); }, [dispatchCallback, transactionRunning, transferAmount]); - useEffect((): void => { - estimatedFee && - transferAmount && - setAmountNotCorrect(new BN(balance.free).sub(transferAmount).add(new BN(estimatedFee)).isNeg()); - }, [transferAmount, estimatedFee, balance]); - const buttonLabel = isBridged ? `Send bridge transfer from ${sourceChainDetails.chain} to ${targetChainDetails.chain}` : `Send internal transfer to ${sourceChainDetails.chain}`; @@ -126,13 +122,14 @@ function Transfer() { /> - + {buttonLabel} - {amountNotCorrect ? ( - - Account's amount (including fees: {estimatedFee}) is not enough for this transaction. - + {evaluateTransactionStatusError ? ( + {evaluateTransactionStatusError} ) : ( )} diff --git a/src/contexts/TransactionContext.tsx b/src/contexts/TransactionContext.tsx index 80c55ddf..3094b31b 100644 --- a/src/contexts/TransactionContext.tsx +++ b/src/contexts/TransactionContext.tsx @@ -26,6 +26,7 @@ import { useGUIContext } from './GUIContextProvider'; import { initTransactionState } from '../reducers/initReducersStates/initTransactionState'; import { useSourceTarget } from './SourceTargetContextProvider'; import { encodeAddress } from '@polkadot/util-crypto'; +import useSenderBalanceUpdates from '../hooks/transactions/useSenderBalanceUpdates'; interface TransactionContextProviderProps { children: React.ReactElement; @@ -51,7 +52,7 @@ export function useUpdateTransactionContext() { export function TransactionContextProvider(props: TransactionContextProviderProps): React.ReactElement { const { children = null } = props; - const { account } = useAccountContext(); + const { account, senderAccountBalance, senderCompanionAccountBalance } = useAccountContext(); const { action } = useGUIContext(); const { sourceChainDetails: { @@ -61,18 +62,17 @@ export function TransactionContextProvider(props: TransactionContextProviderProp const [transactionsState, dispatchTransaction] = useReducer(transactionReducer, initTransactionState); useResetTransactionState(action, dispatchTransaction); - useEstimatedFeePayload(transactionsState, dispatchTransaction); useTransactionsStatus(transactionsState.transactions, transactionsState.evaluatingTransactions, dispatchTransaction); + useSenderBalanceUpdates(senderAccountBalance, senderCompanionAccountBalance, dispatchTransaction); + + useEffect((): void => { + account && dispatchTransaction(TransactionActionCreators.setSender(encodeAddress(account.address, ss58Format))); + }, [account, ss58Format]); useEffect((): void => { - dispatchTransaction( - TransactionActionCreators.setSenderAndAction({ - senderAccount: account ? encodeAddress(account.address, ss58Format) : null, - action - }) - ); - }, [account, action, ss58Format]); + action && dispatchTransaction(TransactionActionCreators.setAction(action)); + }, [action]); return ( diff --git a/src/hooks/context/useSendersBalancesContext.ts b/src/hooks/context/useSendersBalancesContext.ts index 60c01662..76850aa4 100644 --- a/src/hooks/context/useSendersBalancesContext.ts +++ b/src/hooks/context/useSendersBalancesContext.ts @@ -84,5 +84,5 @@ export default function useSendersBalancesContext( if (blocksReached) { updateAccounts(); } - }, [blocksReached, updateAccounts, timerId]); + }, [blocksReached, updateAccounts, timerId, accountState.account]); } diff --git a/src/hooks/transactions/useEstimatedFeePayload.ts b/src/hooks/transactions/useEstimatedFeePayload.ts index b0ff5889..dd18263f 100644 --- a/src/hooks/transactions/useEstimatedFeePayload.ts +++ b/src/hooks/transactions/useEstimatedFeePayload.ts @@ -47,7 +47,7 @@ export const useEstimatedFeePayload = ( chain: targetChain } } = sourceTargetDetails; - const { account } = useAccountContext(); + const { account, senderAccountBalance, senderCompanionAccountBalance } = useAccountContext(); const { action, isBridged } = useGUIContext(); const { estimatedFeeMethodName } = getSubstrateDynamicNames(targetChain); const previousPayloadEstimatedFeeLoading = usePrevious(transactionState.payloadEstimatedFeeLoading); @@ -61,10 +61,19 @@ export const useEstimatedFeePayload = ( loading, sourceTargetDetails, createType, - isBridged + isBridged, + senderAccountBalance, + senderCompanionAccountBalance ) ), - [createType, dispatchTransaction, isBridged, sourceTargetDetails] + [ + createType, + dispatchTransaction, + isBridged, + senderAccountBalance, + senderCompanionAccountBalance, + sourceTargetDetails + ] ); const calculateFeeAndPayload = useCallback( @@ -126,7 +135,15 @@ export const useEstimatedFeePayload = ( useEffect(() => { const { batchedTransactionState, payloadEstimatedFeeLoading } = transactionState; - if (previousPayloadEstimatedFeeLoading && !payloadEstimatedFeeLoading && batchedTransactionState) { + + if ( + previousPayloadEstimatedFeeLoading && + !payloadEstimatedFeeLoading && + batchedTransactionState && + senderAccountBalance && + senderCompanionAccountBalance + ) { + console.log('CALLING USEESTI', senderAccountBalance, senderCompanionAccountBalance); genericCall({ call: () => calculateFeeAndPayload(batchedTransactionState), dispatch, @@ -140,6 +157,8 @@ export const useEstimatedFeePayload = ( dispatch, dispatchTransaction, previousPayloadEstimatedFeeLoading, + senderAccountBalance, + senderCompanionAccountBalance, transactionState ]); }; diff --git a/src/hooks/transactions/useSenderBalanceUpdates.ts b/src/hooks/transactions/useSenderBalanceUpdates.ts new file mode 100644 index 00000000..22cbd716 --- /dev/null +++ b/src/hooks/transactions/useSenderBalanceUpdates.ts @@ -0,0 +1,59 @@ +// 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 { Dispatch, useEffect } from 'react'; +import isEqual from 'lodash/isEqual'; +import { TransactionActionCreators } from '../../actions/transactionActions'; + +import { TransactionsActionType } from '../../types/transactionTypes'; +import usePrevious from '../react/usePrevious'; +import { BalanceState } from '../../types/accountTypes'; + +const useSenderBalanceUpdates = ( + senderAccountBalance: BalanceState | null, + senderCompanionAccountBalance: BalanceState | null, + dispatchTransaction: Dispatch +) => { + const prevSenderAccountBalance = usePrevious(senderAccountBalance); + const prevSenderCompanionAccountBalance = usePrevious(senderCompanionAccountBalance); + + useEffect((): void => { + console.log( + '!isEqual(prevSenderAccountBalance, senderAccountBalance)', + !isEqual(prevSenderAccountBalance, senderAccountBalance) + ); + if ( + (senderAccountBalance && + senderCompanionAccountBalance && + !isEqual(prevSenderAccountBalance, senderAccountBalance)) || + !isEqual(prevSenderCompanionAccountBalance, senderCompanionAccountBalance) + ) + dispatchTransaction( + TransactionActionCreators.updateSenderBalances({ + senderAccountBalance, + senderCompanionAccountBalance + }) + ); + }, [ + dispatchTransaction, + prevSenderAccountBalance, + prevSenderCompanionAccountBalance, + senderAccountBalance, + senderCompanionAccountBalance + ]); +}; + +export default useSenderBalanceUpdates; diff --git a/src/reducers/accountReducer.ts b/src/reducers/accountReducer.ts index cb961b65..d6baee7b 100644 --- a/src/reducers/accountReducer.ts +++ b/src/reducers/accountReducer.ts @@ -43,7 +43,7 @@ export default function accountReducer(state: AccountState, action: AccountsActi const companionAccount = getDeriveAccount(toDerive); - return { ...state, account, companionAccount }; + return { ...state, account, companionAccount, senderAccountBalance: null, senderCompanionAccountBalance: null }; } case AccountActionsTypes.SET_SENDER_BALANCES: return { diff --git a/src/reducers/transactionReducer.ts b/src/reducers/transactionReducer.ts index 07545e79..3f800644 100644 --- a/src/reducers/transactionReducer.ts +++ b/src/reducers/transactionReducer.ts @@ -20,7 +20,8 @@ import { updateTransaction, isReadyToExecute, setReceiver, - shouldCalculatePayloadFee + shouldCalculatePayloadFee, + enoughFundsEvaluation } from '../util/transactions/reducer'; import { TransactionsActionType, TransactionState } from '../types/transactionTypes'; import logger from '../util/logger'; @@ -29,6 +30,7 @@ import { getTransactionDisplayPayload } from '../util/transactions'; import { isHex } from '@polkadot/util'; export default function transactionReducer(state: TransactionState, action: TransactionsActionType): TransactionState { + console.log(action); const transactionReadyToExecute = isReadyToExecute({ ...state, ...action.payload }); switch (action.type) { case TransactionActionTypes.SET_PAYLOAD_ESTIMATED_FEE: { @@ -38,12 +40,23 @@ export default function transactionReducer(state: TransactionState, action: Tran payloadEstimatedFeeLoading, sourceTargetDetails, createType, - isBridged + isBridged, + senderAccountBalance, + senderCompanionAccountBalance } = action.payload; - const { senderAccount, transferAmount, receiverAddress } = state; + const { senderAccount, receiverAddress, transferAmount } = state; - const readyToExecute = payloadEstimatedFeeLoading ? false : transactionReadyToExecute; + const { evaluateTransactionStatusError, notEnoughFundsToTransfer, notEnoughToPayFee } = enoughFundsEvaluation({ + transferAmount, + senderCompanionAccountBalance, + senderAccountBalance, + estimatedFee + }); + + const readyToExecute = payloadEstimatedFeeLoading + ? false + : transactionReadyToExecute && !notEnoughToPayFee && !notEnoughFundsToTransfer; let payloadHex = null; let transactionDisplayPayload = null; @@ -78,7 +91,8 @@ export default function transactionReducer(state: TransactionState, action: Tran transactionReadyToExecute: readyToExecute, shouldEvaluatePayloadEstimatedFee: false, payloadHex, - transactionDisplayPayload + transactionDisplayPayload, + evaluateTransactionStatusError }; } @@ -186,6 +200,7 @@ export default function transactionReducer(state: TransactionState, action: Tran case TransactionActionTypes.RESET: return { ...state, + evaluateTransactionStatusError: null, resetedAt: Date.now().toString(), derivedReceiverAccount: null, estimatedFee: null, @@ -228,17 +243,41 @@ export default function transactionReducer(state: TransactionState, action: Tran return setReceiver(state, action.payload.receiverPayload); case TransactionActionTypes.SET_TRANSACTION_RUNNING: return { ...state, transactionRunning: action.payload.transactionRunning, transactionReadyToExecute: false }; - case TransactionActionTypes.SET_SENDER_AND_ACTION: { - const { senderAccount, action: transactionType } = action.payload; + case TransactionActionTypes.SET_ACTION: { + const { action: transactionType } = action.payload; + + return { + ...state, + action: transactionType + }; + } + case TransactionActionTypes.SET_SENDER: { + const { senderAccount } = action.payload; + + return { + ...state, + senderAccount: senderAccount + }; + } + case TransactionActionTypes.UPDATE_SENDER_BALANCES: { + const { senderAccountBalance, senderCompanionAccountBalance } = action.payload; + const { transferAmount, estimatedFee, transactionReadyToExecute, action: transactionType, senderAccount } = state; + const { evaluateTransactionStatusError, notEnoughFundsToTransfer, notEnoughToPayFee } = enoughFundsEvaluation({ + transferAmount, + senderCompanionAccountBalance, + senderAccountBalance, + estimatedFee + }); + const shouldEvaluatePayloadEstimatedFee = shouldCalculatePayloadFee(state, { senderAccount, action: transactionType }); + return { ...state, - senderAccount: senderAccount, - action: transactionType, - shouldEvaluatePayloadEstimatedFee + shouldEvaluatePayloadEstimatedFee, + transactionReadyToExecute: false }; } diff --git a/src/util/transactions/reducer/index.ts b/src/util/transactions/reducer/index.ts index 450a8096..f90390f7 100644 --- a/src/util/transactions/reducer/index.ts +++ b/src/util/transactions/reducer/index.ts @@ -21,6 +21,8 @@ import { INCORRECT_FORMAT, GENERIC } from '../../../constants'; import { getValidAddressFormat } from '../../accounts'; import getReceiverAddress from '../../getReceiverAddress'; import logger from '../../logger'; +import BN from 'bn.js'; +import { BalanceState } from '../../../types/accountTypes'; const validateAccount = (receiver: string, sourceChainDetails: ChainState, targetChainDetails: ChainState) => { try { @@ -42,6 +44,36 @@ const validateAccount = (receiver: string, sourceChainDetails: ChainState, targe } }; +interface EnoughFundsEvaluation { + transferAmount: BN | null; + senderAccountBalance: BalanceState; + senderCompanionAccountBalance: BalanceState; + estimatedFee: string | null; +} + +const enoughFundsEvaluation = ({ + transferAmount, + senderCompanionAccountBalance, + senderAccountBalance, + estimatedFee +}: EnoughFundsEvaluation) => { + let evaluateTransactionStatusError = null; + let notEnoughFundsToTransfer = false; + let notEnoughToPayFee = false; + + if (transferAmount && senderCompanionAccountBalance && senderAccountBalance && estimatedFee) { + notEnoughFundsToTransfer = new BN(senderCompanionAccountBalance.free).sub(transferAmount).isNeg(); + notEnoughToPayFee = new BN(senderAccountBalance.free).sub(new BN(estimatedFee)).isNeg(); + if (notEnoughFundsToTransfer) { + evaluateTransactionStatusError = "Account's amount is not enough for this transaction."; + } + if (notEnoughToPayFee) { + evaluateTransactionStatusError = `Account's amount is not enough for pay fee transaction: ${estimatedFee}.`; + } + } + return { evaluateTransactionStatusError, notEnoughFundsToTransfer, notEnoughToPayFee }; +}; + const shouldCalculatePayloadFee = (state: TransactionState, payload: Payload) => { const nextState = { ...state, ...payload }; const { @@ -274,4 +306,4 @@ const setReceiver = (state: TransactionState, payload: ReceiverPayload): Transac }; }; -export { updateTransaction, isReadyToExecute, setReceiver, shouldCalculatePayloadFee }; +export { updateTransaction, isReadyToExecute, setReceiver, shouldCalculatePayloadFee, enoughFundsEvaluation }; From 6ec8d754209bd57336b82b5a1715e3674b93a9de Mon Sep 17 00:00:00 2001 From: hbulgarini Date: Mon, 30 Aug 2021 15:13:37 -0300 Subject: [PATCH 2/4] Finishing logic for splitting enough fee and amount --- src/actions/transactionActions.ts | 7 +++++ src/components/EstimatedFee.tsx | 29 +++++++----------- src/components/Transfer.tsx | 26 ++++------------ .../transactions/useEstimatedFeePayload.ts | 9 ++++-- .../transactions/useSenderBalanceUpdates.ts | 4 --- src/reducers/transactionReducer.ts | 22 +++++++------- src/screens/Main.tsx | 15 +++++++--- src/util/transactions/index.ts | 13 ++++++++ src/util/transactions/reducer/index.ts | 30 +++++++++++++++---- 9 files changed, 87 insertions(+), 68 deletions(-) diff --git a/src/actions/transactionActions.ts b/src/actions/transactionActions.ts index b31086b5..21c79e44 100644 --- a/src/actions/transactionActions.ts +++ b/src/actions/transactionActions.ts @@ -45,6 +45,7 @@ enum TransactionActionTypes { UPDATE_TRANSACTIONS_STATUS = 'UPDATE_TRANSACTIONS_STATUS', SET_BATCH_PAYLOAD_ESTIMATED_FEE = 'SET_BATCH_PAYLOAD_ESTIMATED_FEE', UPDATE_SENDER_BALANCES = 'UPDATE_SENDER_BALANCES', + SET_TRANSFER_TYPE = 'SET_TRANSFER_TYPE', RESET = 'RESET' } @@ -161,6 +162,11 @@ const updateSenderBalances = ({ senderAccountBalance, senderCompanionAccountBala type: TransactionActionTypes.UPDATE_SENDER_BALANCES }); +const setTransferType = (transferType: TransactionTypes) => ({ + payload: { transferType }, + type: TransactionActionTypes.SET_TRANSFER_TYPE +}); + const TransactionActionCreators = { setSender, setAction, @@ -177,6 +183,7 @@ const TransactionActionCreators = { updateTransactionsStatus, setBatchedEvaluationPayloadEstimatedFee, updateSenderBalances, + setTransferType, reset }; diff --git a/src/components/EstimatedFee.tsx b/src/components/EstimatedFee.tsx index 0207090b..98193e06 100644 --- a/src/components/EstimatedFee.tsx +++ b/src/components/EstimatedFee.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { useSourceTarget } from '../contexts/SourceTargetContextProvider'; import { useTransactionContext } from '../contexts/TransactionContext'; import { transformToBaseUnit } from '../util/evalUnits'; -import { TransactionTypes } from '../types/transactionTypes'; +import { Alert } from '.'; const useStyles = makeStyles(() => ({ container: { @@ -31,7 +31,12 @@ const useStyles = makeStyles(() => ({ export const EstimatedFee = (): React.ReactElement => { const classes = useStyles(); const { sourceChainDetails } = useSourceTarget(); - const { estimatedFee, payloadEstimatedFeeLoading, transactionRunning } = useTransactionContext(); + const { + estimatedFee, + payloadEstimatedFeeLoading, + transactionRunning, + evaluateTransactionStatusError + } = useTransactionContext(); const srcChainDecimals = sourceChainDetails.apiConnection.api.registry.chainDecimals[0]; const { chainTokens } = sourceChainDetails.apiConnection.api.registry; @@ -43,7 +48,9 @@ export const EstimatedFee = (): React.ReactElement => { const feeLabel = `Estimated ${sourceChainDetails.chain} fee`; - return ( + return evaluateTransactionStatusError ? ( + {evaluateTransactionStatusError} + ) : (
{payloadEstimatedFeeLoading && !transactionRunning @@ -55,19 +62,3 @@ export const EstimatedFee = (): React.ReactElement => {
); }; - -/* export const EstimatedFee = (): React.ReactElement => { - const { action } = useTransactionContext(); - - useEffect((): void => { - estimatedFee && setEnoughForPayFee(new BN(balance.free).sub(new BN(estimatedFee)).isNeg()); - senderCompanionAccountBalance && - transferAmount && - setEnoughForTransfer(new BN(senderCompanionAccountBalance.free).sub(transferAmount).isNeg()); - }, [transferAmount, estimatedFee, balance, senderCompanionAccountBalance]); - - if (action === TransactionTypes.TRANSFER) { - - } -}; - */ diff --git a/src/components/Transfer.tsx b/src/components/Transfer.tsx index 5aaa569d..ca8f75f9 100644 --- a/src/components/Transfer.tsx +++ b/src/components/Transfer.tsx @@ -14,21 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges UI. If not, see . -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useEffect, useCallback } from 'react'; import { Box, makeStyles } from '@material-ui/core'; import { useSourceTarget } from '../contexts/SourceTargetContextProvider'; import { useTransactionContext } from '../contexts/TransactionContext'; -import { useAccountContext } from '../contexts/AccountContextProvider'; import { TransactionActionCreators } from '../actions/transactionActions'; import { useUpdateTransactionContext } from '../contexts/TransactionContext'; -import useBalance from '../hooks/subscriptions/useBalance'; import useSendMessage from '../hooks/chain/useSendMessage'; import { TransactionTypes } from '../types/transactionTypes'; import { TokenSymbol } from './TokenSymbol'; import Receiver from './Receiver'; -import { Alert, ButtonSubmit } from '../components'; +import { ButtonSubmit } from '../components'; import { EstimatedFee } from '../components/EstimatedFee'; -import BN from 'bn.js'; import { DebouncedTextField } from './DebouncedTextField'; import { useInternalTransfer } from '../hooks/chain/useInternalTransfer'; import { useGUIContext } from '../contexts/GUIContextProvider'; @@ -55,21 +52,15 @@ const useStyles = makeStyles((theme) => ({ function Transfer() { const { dispatchTransaction } = useUpdateTransactionContext(); const classes = useStyles(); - const [enoughForTransfer, setEnoughForTransfer] = useState(false); - const [enoughForPayFee, setEnoughForPayFee] = useState(false); const { sourceChainDetails, targetChainDetails } = useSourceTarget(); const { isBridged } = useGUIContext(); - const { account, senderCompanionAccountBalance } = useAccountContext(); const { - estimatedFee, transferAmount, transferAmountError, transactionRunning, - transactionReadyToExecute, - evaluateTransactionStatusError + transactionReadyToExecute } = useTransactionContext(); const { api } = sourceChainDetails.apiConnection; - const balance = useBalance(api, account?.address || ''); const executeInternalTransfer = useInternalTransfer(); const dispatchCallback = useCallback( @@ -122,17 +113,10 @@ function Transfer() { /> - + {buttonLabel} - {evaluateTransactionStatusError ? ( - {evaluateTransactionStatusError} - ) : ( - - )} + ); } diff --git a/src/hooks/transactions/useEstimatedFeePayload.ts b/src/hooks/transactions/useEstimatedFeePayload.ts index dd18263f..e77d37f4 100644 --- a/src/hooks/transactions/useEstimatedFeePayload.ts +++ b/src/hooks/transactions/useEstimatedFeePayload.ts @@ -41,7 +41,10 @@ export const useEstimatedFeePayload = ( const laneId = useLaneId(); const sourceTargetDetails = useSourceTarget(); const { - sourceChainDetails: { chain: sourceChain }, + sourceChainDetails: { + chain: sourceChain, + apiConnection: { api: sourceApi } + }, targetChainDetails: { apiConnection: { api: targetApi }, chain: targetChain @@ -82,6 +85,7 @@ export const useEstimatedFeePayload = ( action, account, targetApi, + sourceApi, transactionState: currentTransactionState }); @@ -112,7 +116,7 @@ export const useEstimatedFeePayload = ( const estimatedFee = estimatedFeeType.toString(); return { estimatedFee, payload }; }, - [account, action, createType, estimatedFeeMethodName, laneId, sourceChain, stateCall, targetApi] + [account, action, createType, estimatedFeeMethodName, laneId, sourceApi, sourceChain, stateCall, targetApi] ); useEffect(() => { @@ -143,7 +147,6 @@ export const useEstimatedFeePayload = ( senderAccountBalance && senderCompanionAccountBalance ) { - console.log('CALLING USEESTI', senderAccountBalance, senderCompanionAccountBalance); genericCall({ call: () => calculateFeeAndPayload(batchedTransactionState), dispatch, diff --git a/src/hooks/transactions/useSenderBalanceUpdates.ts b/src/hooks/transactions/useSenderBalanceUpdates.ts index 22cbd716..d0f38980 100644 --- a/src/hooks/transactions/useSenderBalanceUpdates.ts +++ b/src/hooks/transactions/useSenderBalanceUpdates.ts @@ -31,10 +31,6 @@ const useSenderBalanceUpdates = ( const prevSenderCompanionAccountBalance = usePrevious(senderCompanionAccountBalance); useEffect((): void => { - console.log( - '!isEqual(prevSenderAccountBalance, senderAccountBalance)', - !isEqual(prevSenderAccountBalance, senderAccountBalance) - ); if ( (senderAccountBalance && senderCompanionAccountBalance && diff --git a/src/reducers/transactionReducer.ts b/src/reducers/transactionReducer.ts index 3f800644..08ac5a77 100644 --- a/src/reducers/transactionReducer.ts +++ b/src/reducers/transactionReducer.ts @@ -30,7 +30,6 @@ import { getTransactionDisplayPayload } from '../util/transactions'; import { isHex } from '@polkadot/util'; export default function transactionReducer(state: TransactionState, action: TransactionsActionType): TransactionState { - console.log(action); const transactionReadyToExecute = isReadyToExecute({ ...state, ...action.payload }); switch (action.type) { case TransactionActionTypes.SET_PAYLOAD_ESTIMATED_FEE: { @@ -51,7 +50,8 @@ export default function transactionReducer(state: TransactionState, action: Tran transferAmount, senderCompanionAccountBalance, senderAccountBalance, - estimatedFee + estimatedFee, + action: state.action }); const readyToExecute = payloadEstimatedFeeLoading @@ -260,14 +260,7 @@ export default function transactionReducer(state: TransactionState, action: Tran }; } case TransactionActionTypes.UPDATE_SENDER_BALANCES: { - const { senderAccountBalance, senderCompanionAccountBalance } = action.payload; - const { transferAmount, estimatedFee, transactionReadyToExecute, action: transactionType, senderAccount } = state; - const { evaluateTransactionStatusError, notEnoughFundsToTransfer, notEnoughToPayFee } = enoughFundsEvaluation({ - transferAmount, - senderCompanionAccountBalance, - senderAccountBalance, - estimatedFee - }); + const { action: transactionType, senderAccount } = state; const shouldEvaluatePayloadEstimatedFee = shouldCalculatePayloadFee(state, { senderAccount, @@ -280,7 +273,6 @@ export default function transactionReducer(state: TransactionState, action: Tran transactionReadyToExecute: false }; } - case TransactionActionTypes.UPDATE_TRANSACTIONS_STATUS: { const { evaluateTransactionStatusError, transactions, evaluatingTransactions } = action.payload; return { @@ -290,6 +282,14 @@ export default function transactionReducer(state: TransactionState, action: Tran evaluateTransactionStatusError }; } + case TransactionActionTypes.SET_TRANSFER_TYPE: { + const { transferType } = action.payload; + return { + ...state, + action: transferType + }; + } + default: throw new Error(`Unknown type: ${action.type}`); } diff --git a/src/screens/Main.tsx b/src/screens/Main.tsx index 0639bce8..db7035ed 100644 --- a/src/screens/Main.tsx +++ b/src/screens/Main.tsx @@ -29,9 +29,10 @@ import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; import Transactions from '../components/Transactions'; import { useGUIContext } from '../contexts/GUIContextProvider'; import { TransactionTypes } from '../types/transactionTypes'; - +import { TransactionActionCreators } from '../actions/transactionActions'; import BridgedLocalWrapper from '../components/BridgedLocalWrapper'; import { useCallback } from 'react'; +import { useUpdateTransactionContext } from '../contexts/TransactionContext'; const useStyles = makeStyles(() => ({ root: { @@ -50,12 +51,18 @@ const ActionComponents = { function Main() { const classes = useStyles(); const { actions, action, setAction, isBridged, setBridged } = useGUIContext(); + const { dispatchTransaction } = useUpdateTransactionContext(); const handleOnSwitch = useCallback( - (event: React.MouseEvent, newAlignment: string | null) => { - setBridged(Boolean(newAlignment)); + (event: React.MouseEvent, isBridged: boolean) => { + setBridged(isBridged); + dispatchTransaction( + TransactionActionCreators.setTransferType( + isBridged ? TransactionTypes.TRANSFER : TransactionTypes.INTERNAL_TRANSFER + ) + ); }, - [setBridged] + [dispatchTransaction, setBridged] ); // TODO #242: ToggleButtonGroup needs to contain the colors designed by custom css. diff --git a/src/util/transactions/index.ts b/src/util/transactions/index.ts index a15d2e9b..c11e7bb1 100644 --- a/src/util/transactions/index.ts +++ b/src/util/transactions/index.ts @@ -90,6 +90,7 @@ interface TransactionCallWeightInput { action: TransactionTypes; account: Account; targetApi: ApiPromise; + sourceApi: ApiPromise; transactionState: TransactionState; } @@ -97,6 +98,7 @@ export async function getTransactionCallWeight({ action, account, targetApi, + sourceApi, transactionState }: TransactionCallWeightInput) { let weight: number = 0; @@ -123,6 +125,17 @@ export async function getTransactionCallWeight({ ).weight.toNumber(); } break; + case TransactionTypes.INTERNAL_TRANSFER: + if (receiverAddress) { + call = (await sourceApi.tx.balances.transfer(receiverAddress, transferAmount || 0)).toU8a(); + // TODO [#121] Figure out what the extra bytes are about + call = call.slice(2); + logger.info(`balances::transfer: ${u8aToHex(call)}`); + weight = ( + await targetApi.tx.balances.transfer(receiverAddress, transferAmount || 0).paymentInfo(account) + ).weight.toNumber(); + } + break; case TransactionTypes.CUSTOM: if (customCallInput) { call = isHex(customCallInput) ? hexToU8a(customCallInput.toString()) : null; diff --git a/src/util/transactions/reducer/index.ts b/src/util/transactions/reducer/index.ts index f90390f7..e2aee0cb 100644 --- a/src/util/transactions/reducer/index.ts +++ b/src/util/transactions/reducer/index.ts @@ -49,27 +49,42 @@ interface EnoughFundsEvaluation { senderAccountBalance: BalanceState; senderCompanionAccountBalance: BalanceState; estimatedFee: string | null; + action: TransactionTypes; } const enoughFundsEvaluation = ({ transferAmount, senderCompanionAccountBalance, senderAccountBalance, - estimatedFee + estimatedFee, + action }: EnoughFundsEvaluation) => { let evaluateTransactionStatusError = null; let notEnoughFundsToTransfer = false; let notEnoughToPayFee = false; - if (transferAmount && senderCompanionAccountBalance && senderAccountBalance && estimatedFee) { - notEnoughFundsToTransfer = new BN(senderCompanionAccountBalance.free).sub(transferAmount).isNeg(); + if (senderAccountBalance && estimatedFee) { notEnoughToPayFee = new BN(senderAccountBalance.free).sub(new BN(estimatedFee)).isNeg(); - if (notEnoughFundsToTransfer) { - evaluateTransactionStatusError = "Account's amount is not enough for this transaction."; - } if (notEnoughToPayFee) { evaluateTransactionStatusError = `Account's amount is not enough for pay fee transaction: ${estimatedFee}.`; } + + if (action === TransactionTypes.TRANSFER && transferAmount && senderCompanionAccountBalance) { + notEnoughFundsToTransfer = new BN(senderCompanionAccountBalance.free).sub(new BN(estimatedFee)).isNeg(); + if (notEnoughFundsToTransfer) { + evaluateTransactionStatusError = "Companion account's amount is not enough for this transaction."; + } + } + + if (action === TransactionTypes.INTERNAL_TRANSFER && transferAmount) { + notEnoughFundsToTransfer = new BN(senderAccountBalance.free) + .sub(transferAmount) + .sub(new BN(estimatedFee)) + .isNeg(); + if (notEnoughFundsToTransfer) { + evaluateTransactionStatusError = "Account's amount is not enough for this transaction."; + } + } } return { evaluateTransactionStatusError, notEnoughFundsToTransfer, notEnoughToPayFee }; }; @@ -86,7 +101,9 @@ const shouldCalculatePayloadFee = (state: TransactionState, payload: Payload) => senderAccount, action } = nextState; + switch (action) { + case TransactionTypes.INTERNAL_TRANSFER: case TransactionTypes.TRANSFER: { return Boolean(transferAmount && receiverAddress && senderAccount); } @@ -122,6 +139,7 @@ const updateTransaction = (state: TransactionState, payload: Payload): Transacti const isInputReady = (state: TransactionState): boolean => { switch (state.action) { + case TransactionTypes.INTERNAL_TRANSFER: case TransactionTypes.TRANSFER: { return Boolean(state.transferAmount) && Boolean(state.receiverAddress); } From a570331913e3cc0c7bd2fc8bd638cc6eb68877c7 Mon Sep 17 00:00:00 2001 From: hbulgarini Date: Mon, 30 Aug 2021 15:29:37 -0300 Subject: [PATCH 3/4] removing unwanted changes --- src/hooks/context/useSendersBalancesContext.ts | 2 +- src/util/transactions/index.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hooks/context/useSendersBalancesContext.ts b/src/hooks/context/useSendersBalancesContext.ts index 76850aa4..60c01662 100644 --- a/src/hooks/context/useSendersBalancesContext.ts +++ b/src/hooks/context/useSendersBalancesContext.ts @@ -84,5 +84,5 @@ export default function useSendersBalancesContext( if (blocksReached) { updateAccounts(); } - }, [blocksReached, updateAccounts, timerId, accountState.account]); + }, [blocksReached, updateAccounts, timerId]); } diff --git a/src/util/transactions/index.ts b/src/util/transactions/index.ts index c11e7bb1..3915388e 100644 --- a/src/util/transactions/index.ts +++ b/src/util/transactions/index.ts @@ -128,11 +128,10 @@ export async function getTransactionCallWeight({ case TransactionTypes.INTERNAL_TRANSFER: if (receiverAddress) { call = (await sourceApi.tx.balances.transfer(receiverAddress, transferAmount || 0)).toU8a(); - // TODO [#121] Figure out what the extra bytes are about call = call.slice(2); logger.info(`balances::transfer: ${u8aToHex(call)}`); weight = ( - await targetApi.tx.balances.transfer(receiverAddress, transferAmount || 0).paymentInfo(account) + await sourceApi.tx.balances.transfer(receiverAddress, transferAmount || 0).paymentInfo(account) ).weight.toNumber(); } break; From 128d03f8379b87444fb42dca2748683fb0ba7480 Mon Sep 17 00:00:00 2001 From: hbulgarini Date: Mon, 30 Aug 2021 16:44:04 -0300 Subject: [PATCH 4/4] Addressing bug with local transfer --- .../transactions/useEstimatedFeePayload.ts | 23 ++++++++++++-- src/reducers/transactionReducer.ts | 8 ++--- src/types/transactionTypes.ts | 30 +++++++++++-------- src/util/transactions/index.ts | 26 ++++++++-------- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/hooks/transactions/useEstimatedFeePayload.ts b/src/hooks/transactions/useEstimatedFeePayload.ts index e77d37f4..a5df71db 100644 --- a/src/hooks/transactions/useEstimatedFeePayload.ts +++ b/src/hooks/transactions/useEstimatedFeePayload.ts @@ -25,8 +25,13 @@ import type { InterfaceTypes } from '@polkadot/types/types'; import useLaneId from '../chain/useLaneId'; import { getSubstrateDynamicNames } from '../../util/getSubstrateDynamicNames'; import { genericCall } from '../../util/apiUtlis'; -import { PayloadEstimatedFee, TransactionsActionType, TransactionState } from '../../types/transactionTypes'; -import { getTransactionCallWeight } from '../../util/transactions/'; +import { + PayloadEstimatedFee, + TransactionsActionType, + TransactionState, + TransactionTypes +} from '../../types/transactionTypes'; +import { getFeeAndWeightForInternals, getTransactionCallWeight } from '../../util/transactions/'; import { useGUIContext } from '../../contexts/GUIContextProvider'; import usePrevious from '../react/usePrevious'; @@ -81,11 +86,23 @@ export const useEstimatedFeePayload = ( const calculateFeeAndPayload = useCallback( async (currentTransactionState: TransactionState) => { + if (currentTransactionState.action === TransactionTypes.INTERNAL_TRANSFER) { + const { estimatedFee, weight } = await getFeeAndWeightForInternals({ + api: sourceApi, + transactionState: currentTransactionState + }); + const payload = { + sourceAccount: currentTransactionState.senderAccount, + transferAmount: currentTransactionState.transferAmount!.toNumber(), + receiverAddress: currentTransactionState.receiverAddress, + weight + }; + return { estimatedFee, payload }; + } const { call, weight } = await getTransactionCallWeight({ action, account, targetApi, - sourceApi, transactionState: currentTransactionState }); diff --git a/src/reducers/transactionReducer.ts b/src/reducers/transactionReducer.ts index 08ac5a77..079581d2 100644 --- a/src/reducers/transactionReducer.ts +++ b/src/reducers/transactionReducer.ts @@ -61,8 +61,8 @@ export default function transactionReducer(state: TransactionState, action: Tran let payloadHex = null; let transactionDisplayPayload = null; - if (senderAccount) { - if (payload && isBridged) { + if (senderAccount && payload) { + if (isBridged) { const updated = getTransactionDisplayPayload({ payload, account: senderAccount, @@ -76,7 +76,8 @@ export default function transactionReducer(state: TransactionState, action: Tran transactionDisplayPayload = { sourceAccount: senderAccount, transferAmount: transferAmount.toNumber(), - receiverAddress: receiverAddress + receiverAddress: receiverAddress, + weight: payload.weight }; } } @@ -87,7 +88,6 @@ export default function transactionReducer(state: TransactionState, action: Tran payloadEstimatedFeeError, payloadEstimatedFeeLoading, payload: payloadEstimatedFeeError ? null : payload, - transactionReadyToExecute: readyToExecute, shouldEvaluatePayloadEstimatedFee: false, payloadHex, diff --git a/src/types/transactionTypes.ts b/src/types/transactionTypes.ts index f8a8ee38..b5d02a24 100644 --- a/src/types/transactionTypes.ts +++ b/src/types/transactionTypes.ts @@ -40,7 +40,8 @@ export enum SwitchTabEnum { PAYLOAD = 'PAYLOAD', DECODED = 'DECODED' } -export interface TransactionPayload { + +export interface BridgedTransactionPayload { call: Uint8Array; origin: { SourceAccount: Uint8Array; @@ -49,20 +50,28 @@ export interface TransactionPayload { weight: number; } +export interface InternalTransferPayload { + sourceAccount: string | null; + transferAmount: number; + receiverAddress: string | null; + weight: number; +} + +export type TransactionPayload = BridgedTransactionPayload | InternalTransferPayload; + export interface TransactionDisplayPayload { call: Object; origin: Object; spec_version: string; - weight: string; + weight: number; } -export interface LocalTransactionDisplayPayload { - sourceAccount: string; - transferAmount: number; - receiverAddress: string; -} +export type PayloadEstimatedFee = { + payload: TransactionPayload | null; + estimatedFee: string | null; +}; -export type DisplayPayload = TransactionDisplayPayload | LocalTransactionDisplayPayload; +export type DisplayPayload = TransactionDisplayPayload | InternalTransferPayload; export interface TransactionStatusType extends UpdatedTransactionStatusType { input: string; @@ -138,8 +147,3 @@ export interface ReceiverPayload { targetChainDetails: ChainState; isBridged: boolean; } - -export type PayloadEstimatedFee = { - payload: TransactionPayload | null; - estimatedFee: string | null; -}; diff --git a/src/util/transactions/index.ts b/src/util/transactions/index.ts index 3915388e..bd88100f 100644 --- a/src/util/transactions/index.ts +++ b/src/util/transactions/index.ts @@ -90,7 +90,6 @@ interface TransactionCallWeightInput { action: TransactionTypes; account: Account; targetApi: ApiPromise; - sourceApi: ApiPromise; transactionState: TransactionState; } @@ -98,7 +97,6 @@ export async function getTransactionCallWeight({ action, account, targetApi, - sourceApi, transactionState }: TransactionCallWeightInput) { let weight: number = 0; @@ -125,16 +123,6 @@ export async function getTransactionCallWeight({ ).weight.toNumber(); } break; - case TransactionTypes.INTERNAL_TRANSFER: - if (receiverAddress) { - call = (await sourceApi.tx.balances.transfer(receiverAddress, transferAmount || 0)).toU8a(); - call = call.slice(2); - logger.info(`balances::transfer: ${u8aToHex(call)}`); - weight = ( - await sourceApi.tx.balances.transfer(receiverAddress, transferAmount || 0).paymentInfo(account) - ).weight.toNumber(); - } - break; case TransactionTypes.CUSTOM: if (customCallInput) { call = isHex(customCallInput) ? hexToU8a(customCallInput.toString()) : null; @@ -147,6 +135,20 @@ export async function getTransactionCallWeight({ } return { call, weight }; } + +interface FeeWeightInternal { + api: ApiPromise; + transactionState: TransactionState; +} + +export async function getFeeAndWeightForInternals({ api, transactionState }: FeeWeightInternal) { + const { receiverAddress, transferAmount, senderAccount } = transactionState; + const transfer = api.tx.balances.transfer(receiverAddress!, transferAmount || 0); + + const { partialFee, weight } = await transfer.paymentInfo(senderAccount!); + return { estimatedFee: partialFee.toString(), weight: weight.toNumber() }; +} + const stepEvaluator = (transactionValue: string | number | null, chainValue: string | number | null): boolean => { if (!transactionValue || !chainValue) return false;