Skip to content

Commit

Permalink
feat: native field as input in swap (#702)
Browse files Browse the repository at this point in the history
  • Loading branch information
estebanmino authored Jul 3, 2023
1 parent ebc778b commit 204b80d
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 100 deletions.
40 changes: 34 additions & 6 deletions e2e/serial/1_swapFlow1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,11 @@ it('should be able to select same asset than asset to buy as asset to sell and r
});

it('should be able to open press max on token to sell input', async () => {
const fiatValueText = await getTextFromText({
id: 'token-to-sell-info-fiat-value',
const fiatValueText = await getTextFromTextInput({
id: 'token-to-sell-info-fiat-value-input',
driver,
});
expect(fiatValueText).toBe('$0.00');
expect(fiatValueText).toBe('');
await findElementByTestIdAndClick({
id: 'token-to-sell-info-max-button',
driver,
Expand All @@ -339,11 +339,11 @@ it('should be able to open press max on token to sell input', async () => {
driver,
});
expect(ethValueBeforeGas).toEqual('10000');
const fiatValueTextAfterMax = await getTextFromText({
id: 'token-to-sell-info-fiat-value',
const fiatValueTextAfterMax = await getTextFromTextInput({
id: 'token-to-sell-info-fiat-value-input',
driver,
});
expect(fiatValueTextAfterMax).not.toEqual('$0.00');
expect(fiatValueTextAfterMax).not.toEqual('0.00');
});

it('should be able to remove token to sell and select it again', async () => {
Expand Down Expand Up @@ -390,6 +390,34 @@ it('should be able to open token to buy input and select assets', async () => {
expect(toBuyInputDaiSelected).toBeTruthy();
});

it('should be able to type native amount on sell input', async () => {
await typeOnTextInput({
id: `token-to-sell-info-fiat-value-input`,
text: 1,
driver,
});
const fiatValueText = await getTextFromTextInput({
id: 'token-to-sell-info-fiat-value-input',
driver,
});
expect(fiatValueText).toBe('1');

await delayTime('very-long');
await delayTime('very-long');

const assetToSellInputText = await getTextFromTextInput({
id: `${SWAP_VARIABLES.ETH_MAINNET_ID}-token-to-sell-swap-token-input-swap-input-mask`,
driver,
});
expect(assetToSellInputText).not.toBe('');

const assetToBuyInputText = await getTextFromTextInput({
id: `${SWAP_VARIABLES.DAI_MAINNET_ID}-token-to-buy-swap-token-input-swap-input-mask`,
driver,
});
expect(assetToBuyInputText).not.toBe('');
});

it('should be able to open remove token to buy and check favorites and verified lists are visible', async () => {
await findElementByTestIdAndClick({
id: `${SWAP_VARIABLES.DAI_MAINNET_ID}-token-to-buy-token-input-remove`,
Expand Down
11 changes: 11 additions & 0 deletions src/core/utils/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,17 @@ export const handleSignificantDecimals = (
: resultBN.toFormat();
};

export const handleSignificantDecimalsAsNumber = (
value: BigNumberish,
decimals: number,
): string => {
return new BigNumber(
new BigNumber(multiply(value, new BigNumber(10).pow(decimals))).toFixed(0),
)
.dividedBy(new BigNumber(10).pow(decimals))
.toFixed();
};

/**
* @desc convert from asset BigNumber amount to native price BigNumber amount
*/
Expand Down
45 changes: 40 additions & 5 deletions src/entries/popup/hooks/swap/useSwapInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ParsedSearchAsset } from '~/core/types/assets';
import { GasFeeLegacyParams, GasFeeParams } from '~/core/types/gas';
import { POPUP_DIMENSIONS } from '~/core/utils/dimensions';
import {
convertAmountFromNativeValue,
convertAmountToRawAmount,
convertRawAmountToBalance,
handleSignificantDecimals,
Expand All @@ -20,7 +21,7 @@ const focusOnInput = (inputRef: React.RefObject<HTMLInputElement>) => {
}, 100);
};

export type IndependentField = 'sellField' | 'buyField';
export type IndependentField = 'sellField' | 'buyField' | 'sellNativeField';

export const useSwapInputs = ({
assetToSell,
Expand All @@ -44,9 +45,11 @@ export const useSwapInputs = ({
inputToOpenOnMount !== 'buy',
);
const [assetToSellValue, setAssetToSellValue] = useState('');
const [assetToSellNativeValue, setAssetToSellNativeValue] = useState('');
const [assetToBuyValue, setAssetToBuyValue] = useState('');

const assetToSellInputRef = useRef<HTMLInputElement>(null);
const assetToSellNativeInputRef = useRef<HTMLInputElement>(null);
const assetToBuyInputRef = useRef<HTMLInputElement>(null);

const [independentField, setIndependentField] =
Expand All @@ -60,6 +63,25 @@ export const useSwapInputs = ({
setIndependentValue(value);
}, []);

const setAssetToSellInputNativeValue = useCallback(
(value: string) => {
setAssetToSellDropdownClosed(true);
setAssetToSellNativeValue(value);
setIndependentField('sellNativeField');
setIndependentValue(value);
setAssetToSellValue(
value
? convertAmountFromNativeValue(
value || 0,
assetToSell?.price?.value || 0,
assetToSell?.decimals,
)
: '',
);
},
[assetToSell?.decimals, assetToSell?.price?.value],
);

const setAssetToBuyInputValue = useCallback((value: string) => {
setAssetToBuyDropdownClosed(true);
setAssetToBuyValue(value);
Expand Down Expand Up @@ -110,7 +132,7 @@ export const useSwapInputs = ({
setAssetToSellValue(assetToSellMaxValue.amount);
setIndependentValue(assetToSellMaxValue.amount);
setIndependentField('sellField');
}, [assetToSellMaxValue.amount, setAssetToSellValue]);
}, [assetToSellMaxValue.amount]);

const flipAssets = useCallback(() => {
const isCrosschainSwap =
Expand All @@ -125,9 +147,17 @@ export const useSwapInputs = ({
setAssetToSellValue(independentValue);
setIndependentField('sellField');
focusOnInput(assetToSellInputRef);
} else {
} else if (
independentField === 'sellField' ||
independentField === 'sellNativeField'
) {
setAssetToSellValue('');
setAssetToBuyValue(independentValue);
setAssetToBuyValue(
independentField === 'sellNativeField'
? assetToSellValue
: independentValue,
);
setAssetToSellNativeValue('');
setIndependentField('buyField');
focusOnInput(assetToBuyInputRef);
}
Expand All @@ -139,6 +169,7 @@ export const useSwapInputs = ({
assetToBuy,
assetToBuyValue,
assetToSell,
assetToSellValue,
independentField,
independentValue,
setAssetToBuy,
Expand All @@ -155,7 +186,7 @@ export const useSwapInputs = ({

const assetToBuyDisplay = useMemo(
() =>
independentField === 'sellField'
independentField === 'sellField' || independentField === 'sellNativeField'
? assetToBuyValue && handleSignificantDecimals(assetToBuyValue, 5)
: assetToBuyValue,
[assetToBuyValue, independentField],
Expand All @@ -164,8 +195,10 @@ export const useSwapInputs = ({
return {
assetToBuyInputRef,
assetToSellInputRef,
assetToSellNativeInputRef,
assetToSellMaxValue,
assetToSellValue,
assetToSellNativeValue,
assetToBuyValue,
assetToSellDisplay,
assetToBuyDisplay,
Expand All @@ -179,6 +212,8 @@ export const useSwapInputs = ({
setAssetToBuyInputValue,
setAssetToSellValue,
setAssetToSellInputValue,
setAssetToSellInputNativeValue,
setAssetToSellMaxValue,
setIndependentField,
};
};
103 changes: 65 additions & 38 deletions src/entries/popup/hooks/swap/useSwapNativeAmounts.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { CrosschainQuote, Quote } from '@rainbow-me/swaps';
import { useMemo } from 'react';

import { supportedCurrencies } from '~/core/references';
import { useCurrentCurrencyStore } from '~/core/state';
import { ParsedSearchAsset } from '~/core/types/assets';
import { ChainId } from '~/core/types/chains';
import {
convertAmountAndPriceToNativeDisplay,
convertRawAmountToNativeDisplay,
handleSignificantDecimalsAsNumber,
} from '~/core/utils/numbers';

import { useNativeAssetForNetwork } from '../useNativeAssetForNetwork';
Expand Down Expand Up @@ -35,32 +37,45 @@ export const useSwapNativeAmounts = ({
chainId: assetToBuy?.chainId || ChainId.mainnet,
});

const assetToSellNativeValue = useMemo(() => {
const assetToSellNativeDisplay = useMemo(() => {
let nativeDisplay = null;
if (isWrapOrUnwrapEth) {
return !quote?.sellAmount || !assetToSell?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.sellAmount?.toString(),
assetToSell?.decimals || 18,
assetToSell?.price?.value,
currentCurrency,
);
nativeDisplay =
!quote?.sellAmount || !assetToSell?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.sellAmount?.toString(),
assetToSell?.decimals || 18,
assetToSell?.price?.value,
currentCurrency,
);
} else if (assetToSell?.native?.price?.amount && assetToSellValue) {
return convertAmountAndPriceToNativeDisplay(
nativeDisplay = convertAmountAndPriceToNativeDisplay(
assetToSellValue,
assetToSell?.native?.price?.amount,
currentCurrency,
);
} else {
return !quote?.sellAmountInEth || !sellNativeAsset?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.sellAmountInEth.toString(),
sellNativeAsset?.decimals || 18,
sellNativeAsset?.price?.value,
currentCurrency,
);
nativeDisplay =
!quote?.sellAmountInEth || !sellNativeAsset?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.sellAmountInEth.toString(),
sellNativeAsset?.decimals || 18,
sellNativeAsset?.price?.value,
currentCurrency,
);
}

return nativeDisplay
? {
amount: handleSignificantDecimalsAsNumber(
nativeDisplay?.amount,
supportedCurrencies[currentCurrency].decimals,
),
display: nativeDisplay?.display,
}
: nativeDisplay;
}, [
isWrapOrUnwrapEth,
assetToSell?.native?.price?.amount,
Expand All @@ -74,32 +89,44 @@ export const useSwapNativeAmounts = ({
sellNativeAsset?.decimals,
]);

const assetToBuyNativeValue = useMemo(() => {
const assetToBuyNativeDisplay = useMemo(() => {
let nativeDisplay = null;
if (isWrapOrUnwrapEth) {
return !quote?.buyAmount || !assetToBuy?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.buyAmount?.toString(),
assetToBuy?.decimals || 18,
assetToBuy?.price?.value,
currentCurrency,
);
nativeDisplay =
!quote?.buyAmount || !assetToBuy?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.buyAmount?.toString(),
assetToBuy?.decimals || 18,
assetToBuy?.price?.value,
currentCurrency,
);
} else if (assetToBuy?.native?.price?.amount && assetToBuyValue) {
return convertAmountAndPriceToNativeDisplay(
nativeDisplay = convertAmountAndPriceToNativeDisplay(
assetToBuyValue,
assetToBuy?.native?.price?.amount,
currentCurrency,
);
} else {
return !quote?.buyAmountInEth || !buyNativeAsset?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.buyAmountInEth.toString(),
buyNativeAsset?.decimals || 18,
buyNativeAsset?.price?.value,
currentCurrency,
);
nativeDisplay =
!quote?.buyAmountInEth || !buyNativeAsset?.price?.value
? null
: convertRawAmountToNativeDisplay(
quote?.buyAmountInEth.toString(),
buyNativeAsset?.decimals || 18,
buyNativeAsset?.price?.value,
currentCurrency,
);
}
return nativeDisplay
? {
amount: handleSignificantDecimalsAsNumber(
nativeDisplay?.amount,
supportedCurrencies[currentCurrency].decimals,
),
display: nativeDisplay?.display,
}
: nativeDisplay;
}, [
isWrapOrUnwrapEth,
assetToBuy?.native?.price?.amount,
Expand All @@ -114,7 +141,7 @@ export const useSwapNativeAmounts = ({
]);

return {
assetToSellNativeValue,
assetToBuyNativeValue,
assetToSellNativeDisplay,
assetToBuyNativeDisplay,
};
};
12 changes: 10 additions & 2 deletions src/entries/popup/hooks/swap/useSwapPriceImpact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export interface SwapPriceImpact {
export const useSwapPriceImpact = ({
assetToSellNativeValue,
assetToBuyNativeValue,
isLoading,
}: {
assetToSellNativeValue: { amount: string; display: string } | null;
assetToBuyNativeValue: { amount: string; display: string } | null;
isLoading: boolean;
}) => {
const { currentCurrency } = useCurrentCurrencyStore();

Expand All @@ -51,14 +53,20 @@ export const useSwapPriceImpact = ({
return { impactDisplay, priceImpact };
}, [assetToBuyNativeValue, currentCurrency, assetToSellNativeValue]);

if (greaterThanOrEqualTo(priceImpact, severePriceImpactThreshold)) {
if (
!isLoading &&
greaterThanOrEqualTo(priceImpact, severePriceImpactThreshold)
) {
return {
priceImpact: {
type: SwapPriceImpactType.severe,
impactDisplay,
},
};
} else if (greaterThanOrEqualTo(priceImpact, highPriceImpactThreshold)) {
} else if (
!isLoading &&
greaterThanOrEqualTo(priceImpact, highPriceImpactThreshold)
) {
return {
priceImpact: {
type: SwapPriceImpactType.high,
Expand Down
Loading

0 comments on commit 204b80d

Please sign in to comment.