From 35c19ffa326be2a2682cda9edd82f38060f981df Mon Sep 17 00:00:00 2001 From: Sergei Novikov Date: Fri, 29 Dec 2023 13:40:21 +0300 Subject: [PATCH 1/2] problem: amount field max value is not rebalancing --- .../transaction/workflow/TxBuilder.spec.ts | 8 ++-- .../src/transaction/workflow/TxBuilder.ts | 43 +++++++++++-------- .../create-tx/CreateErc20ApproveTx.ts | 24 ++++++++--- .../SetupTransaction/SetupTransaction.tsx | 37 +++++++++++++--- .../blockchain/ethereum/erc20/approve.tsx | 27 ++---------- .../blockchain/ethereum/erc20/convert.tsx | 27 ++---------- .../flow/blockchain/ethereum/recovery.tsx | 2 +- .../SetupTransaction/flow/common/transfer.tsx | 2 +- .../flow/components/ApproveAmount.tsx | 12 ++++++ .../handler/blockchain/ethereum/prepare.ts | 13 ++++++ 10 files changed, 113 insertions(+), 82 deletions(-) diff --git a/packages/core/src/transaction/workflow/TxBuilder.spec.ts b/packages/core/src/transaction/workflow/TxBuilder.spec.ts index 8daefff5d..0084305cf 100644 --- a/packages/core/src/transaction/workflow/TxBuilder.spec.ts +++ b/packages/core/src/transaction/workflow/TxBuilder.spec.ts @@ -21,7 +21,7 @@ describe('TxBuilder', () => { const tokenRegistry = new TokenRegistry([tokenData]); const btcEntry: BitcoinEntry = { - id: '7d395b44-0bac-49b9-98de-47e88dbc5a28-0', + id: '50391c5d-a517-4b7a-9c42-1411e0603d30-0', address: { type: 'xpub', value: 'vpub_common', @@ -46,7 +46,7 @@ describe('TxBuilder', () => { addresses: [], }; const ethEntry1: EthereumEntry = { - id: '50391c5d-a517-4b7a-9c42-1411e0603d30-0', + id: '50391c5d-a517-4b7a-9c42-1411e0603d30-1', address: { type: 'single', value: '0x0', @@ -60,7 +60,7 @@ describe('TxBuilder', () => { createdAt: new Date(), }; const ethEntry2: EthereumEntry = { - id: '50391c5d-a517-4b7a-9c42-1411e0603d30-1', + id: '50391c5d-a517-4b7a-9c42-1411e0603d30-2', address: { type: 'single', value: '0x1', @@ -74,7 +74,7 @@ describe('TxBuilder', () => { createdAt: new Date(), }; const etcEntry: EthereumEntry = { - id: '50391c5d-a517-4b7a-9c42-1411e0603d30-2', + id: '50391c5d-a517-4b7a-9c42-1411e0603d30-3', address: { type: 'single', value: '0x2', diff --git a/packages/core/src/transaction/workflow/TxBuilder.ts b/packages/core/src/transaction/workflow/TxBuilder.ts index f129403b0..858a24332 100644 --- a/packages/core/src/transaction/workflow/TxBuilder.ts +++ b/packages/core/src/transaction/workflow/TxBuilder.ts @@ -148,33 +148,24 @@ export class TxBuilder implements BuilderOrigin { return this.convertEthereumTx(createTx); } - this.mergeEthereumFee(createTx); this.mergeEthereumTx(transaction, createTx); } - if (isErc20ApproveCreateTx(createTx)) { - if (isChanged) { - createTx = this.transformErc20ApproveTx(createTx); + if (isChanged) { + if (isErc20ApproveCreateTx(createTx)) { + return this.transformErc20ApproveTx(createTx); } - this.mergeEthereumFee(createTx); - } - - if (isErc20ConvertCreateTx(createTx)) { - if (isChanged) { - createTx = this.transformErc20ConvertTx(createTx); + if (isErc20ConvertCreateTx(createTx)) { + return this.transformErc20ConvertTx(createTx); } - this.mergeEthereumFee(createTx); - } - - if (isEtherRecoveryCreateTx(createTx) || isErc20RecoveryCreateTx(createTx)) { - if (isChanged) { + if (isEtherRecoveryCreateTx(createTx) || isErc20RecoveryCreateTx(createTx)) { return this.convertEthereumRecoveryTx(createTx); } - - this.mergeEthereumFee(createTx); } + + this.mergeEthereumFee(createTx); } } @@ -338,6 +329,8 @@ export class TxBuilder implements BuilderOrigin { createTx.gasPrice = feeRange.stdMaxGasPrice; createTx.maxGasPrice = feeRange.stdMaxGasPrice; createTx.priorityGasPrice = feeRange.stdPriorityGasPrice; + + createTx.rebalance(); } } @@ -375,11 +368,17 @@ export class TxBuilder implements BuilderOrigin { } private transformErc20ApproveTx(createTx: CreateErc20ApproveTx): CreateErc20ApproveTx { - const { asset, entry, tokenRegistry } = this; + const { asset, entry, feeRange, tokenRegistry } = this; const { getBalance } = this.dataProvider; const blockchain = blockchainIdToCode(entry.blockchain); + if (blockchain !== createTx.blockchain && isEthereumFeeRange(feeRange)) { + createTx.gasPrice = feeRange.stdMaxGasPrice; + createTx.maxGasPrice = feeRange.stdMaxGasPrice; + createTx.priorityGasPrice = feeRange.stdPriorityGasPrice; + } + let tokenAddress = asset; if (!tokenRegistry.hasAddress(blockchain, tokenAddress)) { @@ -399,11 +398,17 @@ export class TxBuilder implements BuilderOrigin { } private transformErc20ConvertTx(createTx: CreateErc20ConvertTx): CreateErc20ConvertTx { - const { asset, entry, tokenRegistry } = this; + const { asset, entry, feeRange, tokenRegistry } = this; const { getBalance } = this.dataProvider; const blockchain = blockchainIdToCode(entry.blockchain); + if (blockchain !== createTx.blockchain && isEthereumFeeRange(feeRange)) { + createTx.gasPrice = feeRange.stdMaxGasPrice; + createTx.maxGasPrice = feeRange.stdMaxGasPrice; + createTx.priorityGasPrice = feeRange.stdPriorityGasPrice; + } + createTx.asset = asset; let tokenAddress = asset; diff --git a/packages/core/src/transaction/workflow/create-tx/CreateErc20ApproveTx.ts b/packages/core/src/transaction/workflow/create-tx/CreateErc20ApproveTx.ts index 10b2aa6be..cabd35ac0 100644 --- a/packages/core/src/transaction/workflow/create-tx/CreateErc20ApproveTx.ts +++ b/packages/core/src/transaction/workflow/create-tx/CreateErc20ApproveTx.ts @@ -192,13 +192,13 @@ export class CreateErc20ApproveTx implements Erc20ApproveTxDetails { this._target = value; switch (value) { - case ApproveTarget.MAX_AVAILABLE: - this._amount = this.totalTokenBalance; - - break; case ApproveTarget.INFINITE: this._amount = this.token.getAmount(INFINITE_ALLOWANCE); + break; + case ApproveTarget.MAX_AVAILABLE: + this._amount = this.totalTokenBalance; + break; } } @@ -255,11 +255,23 @@ export class CreateErc20ApproveTx implements Erc20ApproveTxDetails { } rebalance(): void { - // Nothing + const { amount, target, totalTokenBalance, token } = this; + + switch (target) { + case ApproveTarget.INFINITE: + this._amount = token.getAmount(INFINITE_ALLOWANCE); + + break; + case ApproveTarget.MAX_AVAILABLE: + this._amount = totalTokenBalance; + + break; + default: + this._amount = new TokenAmount(amount, token.getUnits(), token); + } } setToken(token: Token, totalBalance: WeiAny, totalTokenBalance: TokenAmount, iep1559 = false): void { - this._amount = new TokenAmount(this.amount, token.getUnits(), token); this._token = token; this.totalBalance = totalBalance; diff --git a/packages/react-app/src/transaction/SetupTransaction/SetupTransaction.tsx b/packages/react-app/src/transaction/SetupTransaction/SetupTransaction.tsx index 673b81062..8912e8494 100644 --- a/packages/react-app/src/transaction/SetupTransaction/SetupTransaction.tsx +++ b/packages/react-app/src/transaction/SetupTransaction/SetupTransaction.tsx @@ -91,10 +91,15 @@ const SetupTransaction: React.FC = ({ setStage, setTransaction, }) => { + const entryId = React.useRef(); const mounted = React.useRef(true); React.useEffect(() => { - prepareTransaction({ action, entries, entry }); + if (entry.id !== entryId.current) { + entryId.current = entry.id; + + prepareTransaction({ action, entries, entry }); + } }, [action, entries, entry, storedTx, prepareTransaction]); React.useEffect(() => { @@ -138,9 +143,33 @@ export default connect( walletId = EntryIdOp.of(entryId).extractWalletId(); } - const { entries } = accounts.selectors.findWallet(state, walletId) ?? {}; + const tokenRegistry = new TokenRegistry(state.application.tokens); + + const entries = + accounts.selectors.findWallet(state, walletId)?.entries.filter(({ blockchain, id, receiveDisabled }) => { + let valid = !receiveDisabled; + + if (action === TxAction.APPROVE || action === TxAction.CONVERT) { + const blockchainCode = blockchainIdToCode(blockchain); + + switch (action) { + case TxAction.APPROVE: + valid &&= tokenRegistry.hasAnyToken(blockchainCode); + + break; + case TxAction.CONVERT: + valid &&= tokenRegistry.hasWrappedToken(blockchainCode); - if (entries == null || entries.length === 0) { + break; + } + + valid &&= accounts.selectors.getBalance(state, id, amountFactory(blockchainCode)(0)).isPositive(); + } + + return valid; + }) ?? []; + + if (entries.length === 0) { throw new Error('Something went wrong while getting entries from wallet'); } @@ -160,8 +189,6 @@ export default connect( const blockchain = blockchainIdToCode(entry.blockchain); - const tokenRegistry = new TokenRegistry(state.application.tokens); - const getBalance = (entry: WalletEntry, asset: string, ownerAddress?: string): BigAmount => { if (tokenRegistry.hasAddress(blockchain, asset)) { const token = tokenRegistry.byAddress(blockchain, asset); diff --git a/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/approve.tsx b/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/approve.tsx index 0bdd2c4c4..e57277154 100644 --- a/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/approve.tsx +++ b/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/approve.tsx @@ -1,5 +1,5 @@ -import { EthereumEntry, isEthereumEntry } from '@emeraldpay/emerald-vault-core'; -import { Blockchains, blockchainIdToCode, workflow } from '@emeraldwallet/core'; +import { EthereumEntry } from '@emeraldpay/emerald-vault-core'; +import { workflow } from '@emeraldwallet/core'; import { FormLabel, FormRow } from '@emeraldwallet/ui'; import * as React from 'react'; import { SelectAsset } from '../../../../../../common/SelectAsset'; @@ -21,32 +21,13 @@ export class Erc20ApproveFlow extends EthereumCommonFlow { } private renderFrom(): React.ReactElement { - const { entries, tokenRegistry } = this.data; - const { getBalance } = this.dataProvider; + const { entry, entries } = this.data; const { setEntry } = this.handler; - const approvingEntries = entries - .filter((entry): entry is EthereumEntry => isEthereumEntry(entry)) - .filter((entry) => { - const blockchain = blockchainIdToCode(entry.blockchain); - - return ( - !entry.receiveDisabled && - tokenRegistry.hasAnyToken(blockchain) && - getBalance(entry, Blockchains[blockchain].params.coinTicker).isPositive() - ); - }); - - let { entry } = this.data; - - if (!approvingEntries.some(({ id }) => id === entry.id)) { - [entry] = approvingEntries; - } - return ( From - + ); } diff --git a/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/convert.tsx b/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/convert.tsx index a319f765b..01f68c308 100644 --- a/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/convert.tsx +++ b/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/erc20/convert.tsx @@ -1,5 +1,5 @@ -import { EthereumEntry, isEthereumEntry } from '@emeraldpay/emerald-vault-core'; -import { Blockchains, blockchainIdToCode, workflow } from '@emeraldwallet/core'; +import { EthereumEntry } from '@emeraldpay/emerald-vault-core'; +import { Blockchains, workflow } from '@emeraldwallet/core'; import { FormLabel, FormRow } from '@emeraldwallet/ui'; import { ToggleButton, ToggleButtonGroup } from '@material-ui/lab'; import * as React from 'react'; @@ -41,32 +41,13 @@ export class Erc20ConvertFlow extends EthereumCommonFlow { } private renderFrom(): React.ReactElement { - const { entries, ownerAddress, tokenRegistry } = this.data; - const { getBalance } = this.dataProvider; + const { entry, entries, ownerAddress } = this.data; const { setEntry } = this.handler; - const convertEntries = entries - .filter((entry): entry is EthereumEntry => isEthereumEntry(entry)) - .filter((entry) => { - const blockchain = blockchainIdToCode(entry.blockchain); - - return ( - !entry.receiveDisabled && - tokenRegistry.hasWrappedToken(blockchain) && - getBalance(entry, Blockchains[blockchain].params.coinTicker).isPositive() - ); - }); - - let { entry } = this.data; - - if (!convertEntries.some(({ id }) => id === entry.id)) { - [entry] = convertEntries; - } - return ( From - + ); } diff --git a/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/recovery.tsx b/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/recovery.tsx index c54b542a7..d8d854d44 100644 --- a/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/recovery.tsx +++ b/packages/react-app/src/transaction/SetupTransaction/flow/blockchain/ethereum/recovery.tsx @@ -25,7 +25,7 @@ export class EthereumRecoveryFlow extends EthereumCommonFlow { return entries .filter((entry): entry is EthereumEntry => isEthereumEntry(entry)) - .filter(({ blockchain, receiveDisabled }) => !receiveDisabled && blockchain === entry.blockchain); + .filter(({ blockchain }) => blockchain === entry.blockchain); } private renderTo(entries: EthereumEntry[]): React.ReactElement { diff --git a/packages/react-app/src/transaction/SetupTransaction/flow/common/transfer.tsx b/packages/react-app/src/transaction/SetupTransaction/flow/common/transfer.tsx index f03514c98..e128ac928 100644 --- a/packages/react-app/src/transaction/SetupTransaction/flow/common/transfer.tsx +++ b/packages/react-app/src/transaction/SetupTransaction/flow/common/transfer.tsx @@ -29,7 +29,7 @@ export abstract class TransferFlow extends CommonFlow { From !entry.receiveDisabled)} + entries={entries} ownerAddress={ownerAddress} selectedEntry={entry} onSelect={setEntry} diff --git a/packages/react-app/src/transaction/SetupTransaction/flow/components/ApproveAmount.tsx b/packages/react-app/src/transaction/SetupTransaction/flow/components/ApproveAmount.tsx index 956e29119..4359656cd 100644 --- a/packages/react-app/src/transaction/SetupTransaction/flow/components/ApproveAmount.tsx +++ b/packages/react-app/src/transaction/SetupTransaction/flow/components/ApproveAmount.tsx @@ -28,6 +28,8 @@ interface OwnProps { export const ApproveAmount: React.FC = ({ createTx, setTransaction }) => { const styles = useStyles(); + const maxAmountUnits = React.useRef(createTx.amount.units); + const [allowInfinite, setAllowInfinite] = React.useState(createTx.target === workflow.ApproveTarget.INFINITE); const [maxAmount, setMaxAmount] = React.useState(); @@ -55,6 +57,16 @@ export const ApproveAmount: React.FC = ({ createTx, setTransaction }) setMaxAmount(createTx.amount.getNumberByUnit(createTx.amount.units.top)); }; + React.useEffect(() => { + const { units } = createTx.amount; + + if (createTx.target === workflow.ApproveTarget.MAX_AVAILABLE && !units.equals(maxAmountUnits.current)) { + maxAmountUnits.current = units; + + setMaxAmount(createTx.amount.getNumberByUnit(units.top)); + } + }, [createTx]); + return ( <> diff --git a/packages/store/src/txstash/handler/blockchain/ethereum/prepare.ts b/packages/store/src/txstash/handler/blockchain/ethereum/prepare.ts index 53bd3e5c3..e42545e80 100644 --- a/packages/store/src/txstash/handler/blockchain/ethereum/prepare.ts +++ b/packages/store/src/txstash/handler/blockchain/ethereum/prepare.ts @@ -12,6 +12,7 @@ import { import { TokenBalanceBelong, accounts, tokens } from '../../../..'; import { getTokens } from '../../../../application/selectors'; import { setAsset, setPreparing, setTransaction } from '../../../actions'; +import { getTransaction } from '../../../selectors'; import { EntryHandler } from '../../types'; import { fetchFee } from './fee'; @@ -61,6 +62,12 @@ export const prepareErc20ApproveTx: EntryHandler = (data, storePr }) ?? token.getAmount(0); } + const tx = getTransaction(state); + + if (tx != null && workflow.isErc20ApprovePlainTx(tx)) { + createTx.target = tx.target; + } + storeProvider.dispatch(setAsset(createTx.asset)); storeProvider.dispatch(setTransaction(createTx.dump())); storeProvider.dispatch(setPreparing(false)); @@ -93,6 +100,12 @@ export const prepareErc20ConvertTx: EntryHandler = (data, storePr }) ?? token.getAmount(0); } + const tx = getTransaction(state); + + if (tx != null && workflow.isErc20ConvertPlainTx(tx)) { + createTx.target = tx.target; + } + storeProvider.dispatch(setAsset(createTx.asset)); storeProvider.dispatch(setTransaction(createTx.dump())); storeProvider.dispatch(setPreparing(false)); From 198f7567c9e042a4fd22daf54cb1d86f2c6c76d7 Mon Sep 17 00:00:00 2001 From: Sergei Novikov Date: Fri, 29 Dec 2023 14:16:30 +0300 Subject: [PATCH 2/2] problem: change fee for restored tx --- .../src/transaction/workflow/TxBuilder.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/core/src/transaction/workflow/TxBuilder.ts b/packages/core/src/transaction/workflow/TxBuilder.ts index 858a24332..bd9eada2e 100644 --- a/packages/core/src/transaction/workflow/TxBuilder.ts +++ b/packages/core/src/transaction/workflow/TxBuilder.ts @@ -148,24 +148,33 @@ export class TxBuilder implements BuilderOrigin { return this.convertEthereumTx(createTx); } + this.mergeEthereumFee(createTx); this.mergeEthereumTx(transaction, createTx); } - if (isChanged) { - if (isErc20ApproveCreateTx(createTx)) { + if (isErc20ApproveCreateTx(createTx)) { + if (isChanged) { return this.transformErc20ApproveTx(createTx); } - if (isErc20ConvertCreateTx(createTx)) { + this.mergeEthereumFee(createTx); + } + + if (isErc20ConvertCreateTx(createTx)) { + if (isChanged) { return this.transformErc20ConvertTx(createTx); } - if (isEtherRecoveryCreateTx(createTx) || isErc20RecoveryCreateTx(createTx)) { + this.mergeEthereumFee(createTx); + } + + if (isEtherRecoveryCreateTx(createTx) || isErc20RecoveryCreateTx(createTx)) { + if (isChanged) { return this.convertEthereumRecoveryTx(createTx); } - } - this.mergeEthereumFee(createTx); + this.mergeEthereumFee(createTx); + } } }