Skip to content

Commit

Permalink
Merge pull request #1370 from emeraldpay/fix/amount-field-rebalance
Browse files Browse the repository at this point in the history
problem: amount field max value is not rebalancing
  • Loading branch information
splix authored Dec 31, 2023
2 parents d60903a + 198f756 commit 978132d
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 67 deletions.
8 changes: 4 additions & 4 deletions packages/core/src/transaction/workflow/TxBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand Down
22 changes: 18 additions & 4 deletions packages/core/src/transaction/workflow/TxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,15 @@ export class TxBuilder implements BuilderOrigin {

if (isErc20ApproveCreateTx(createTx)) {
if (isChanged) {
createTx = this.transformErc20ApproveTx(createTx);
return this.transformErc20ApproveTx(createTx);
}

this.mergeEthereumFee(createTx);
}

if (isErc20ConvertCreateTx(createTx)) {
if (isChanged) {
createTx = this.transformErc20ConvertTx(createTx);
return this.transformErc20ConvertTx(createTx);
}

this.mergeEthereumFee(createTx);
Expand Down Expand Up @@ -338,6 +338,8 @@ export class TxBuilder implements BuilderOrigin {
createTx.gasPrice = feeRange.stdMaxGasPrice;
createTx.maxGasPrice = feeRange.stdMaxGasPrice;
createTx.priorityGasPrice = feeRange.stdPriorityGasPrice;

createTx.rebalance();
}
}

Expand Down Expand Up @@ -375,11 +377,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)) {
Expand All @@ -399,11 +407,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,15 @@ const SetupTransaction: React.FC<OwnProps & StateProps & DispatchProps> = ({
setStage,
setTransaction,
}) => {
const entryId = React.useRef<EntryId>();
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(() => {
Expand Down Expand Up @@ -138,9 +143,33 @@ export default connect<StateProps, DispatchProps, OwnProps, IState>(
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');
}

Expand All @@ -160,8 +189,6 @@ export default connect<StateProps, DispatchProps, OwnProps, IState>(

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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 (
<FormRow>
<FormLabel>From</FormLabel>
<SelectEntry entries={approvingEntries} selectedEntry={entry} onSelect={setEntry} />
<SelectEntry entries={entries} selectedEntry={entry} onSelect={setEntry} />
</FormRow>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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 (
<FormRow>
<FormLabel>From</FormLabel>
<SelectEntry entries={convertEntries} ownerAddress={ownerAddress} selectedEntry={entry} onSelect={setEntry} />
<SelectEntry entries={entries} ownerAddress={ownerAddress} selectedEntry={entry} onSelect={setEntry} />
</FormRow>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export abstract class TransferFlow extends CommonFlow {
<FormLabel>From</FormLabel>
<SelectEntry
withAllowances
entries={entries.filter((entry) => !entry.receiveDisabled)}
entries={entries}
ownerAddress={ownerAddress}
selectedEntry={entry}
onSelect={setEntry}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ interface OwnProps {
export const ApproveAmount: React.FC<OwnProps> = ({ 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<BigNumber | undefined>();

Expand Down Expand Up @@ -55,6 +57,16 @@ export const ApproveAmount: React.FC<OwnProps> = ({ 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 (
<>
<FormRow>
Expand Down
13 changes: 13 additions & 0 deletions packages/store/src/txstash/handler/blockchain/ethereum/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -61,6 +62,12 @@ export const prepareErc20ApproveTx: EntryHandler<EthereumEntry> = (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));
Expand Down Expand Up @@ -93,6 +100,12 @@ export const prepareErc20ConvertTx: EntryHandler<EthereumEntry> = (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));
Expand Down

0 comments on commit 978132d

Please sign in to comment.