Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: native field as input in swap #702

Merged
merged 21 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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