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

[5.4][FIX] Swaps approval transaction #4263

Merged
merged 19 commits into from
Jul 8, 2022
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
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { View, StyleSheet, TouchableOpacity, TextInput } from 'react-native';
import React, { useCallback, useEffect, useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
TextInput,
} from 'react-native';
import { fontStyles } from '../../../../styles/common';
import Text from '../../../Base/Text';
import StyledButton from '../../StyledButton';
import { strings } from '../../../../../locales/i18n';
import { isNumber } from '../../../../util/number';
import ConnectHeader from '../../ConnectHeader';
import Device from '../../../../util/device';
import ErrorMessage from '../../../Views/SendFlow/ErrorMessage';
import { useAppThemeFromContext, mockTheme } from '../../../../util/theme';
import formatNumber from '../../../../util/formatNumber';
import { INTEGER_OR_FLOAT_REGEX } from '../../../../util/number';

const createStyles = (colors) =>
const createStyles = (colors: any) =>
StyleSheet.create({
wrapper: {
paddingHorizontal: 24,
Expand Down Expand Up @@ -99,6 +103,20 @@ const createStyles = (colors) =>
},
});

interface IEditPermissionProps {
host: string;
minimumSpendLimit: string;
spendLimitUnlimitedSelected: boolean;
tokenSymbol: string;
spendLimitCustomValue: string;
originalApproveAmount: string;
onSetApprovalAmount: () => void;
onSpendLimitCustomValueChange: (approvalCustomValue: string) => void;
onPressSpendLimitUnlimitedSelected: () => void;
onPressSpendLimitCustomSelected: () => void;
toggleEditPermission: () => void;
}

function EditPermission({
host,
minimumSpendLimit,
Expand All @@ -111,21 +129,25 @@ function EditPermission({
onPressSpendLimitUnlimitedSelected,
onPressSpendLimitCustomSelected,
toggleEditPermission,
}) {
}: IEditPermissionProps) {
const [initialState] = useState({
spendLimitUnlimitedSelected,
spendLimitCustomValue,
});
const [disableBtn, setDisableBtn] = useState(false);
const [displayErrorMsg, setDisplayErrorMsg] = useState(false);
const { colors, themeAppearance } = useAppThemeFromContext() || mockTheme;
const styles = createStyles(colors);

const displayErrorMessage = useMemo(
() =>
(!spendLimitUnlimitedSelected &&
!INTEGER_OR_FLOAT_REGEX.test(spendLimitCustomValue)) ||
Number(minimumSpendLimit) > spendLimitCustomValue,
[spendLimitUnlimitedSelected, spendLimitCustomValue, minimumSpendLimit],
);
useEffect(() => {
setDisplayErrorMsg(
Number(minimumSpendLimit) > Number(spendLimitCustomValue),
);
}, [minimumSpendLimit, spendLimitCustomValue, spendLimitUnlimitedSelected]);

useEffect(() => {
setDisableBtn(!isNumber(spendLimitCustomValue) || displayErrorMsg);
}, [spendLimitCustomValue, displayErrorMsg]);

const onSetApprovalAmount = useCallback(() => {
if (!spendLimitUnlimitedSelected && !spendLimitCustomValue) {
Expand All @@ -141,13 +163,12 @@ function EditPermission({
]);

const onBackPress = useCallback(() => {
const { spendLimitUnlimitedSelected, spendLimitCustomValue } = initialState;
if (spendLimitUnlimitedSelected) {
if (initialState.spendLimitUnlimitedSelected) {
onPressSpendLimitUnlimitedSelected();
} else {
onPressSpendLimitCustomSelected();
}
onSpendLimitCustomValueChange(spendLimitCustomValue);
onSpendLimitCustomValueChange(initialState.spendLimitCustomValue);
toggleEditPermission();
}, [
initialState,
Expand Down Expand Up @@ -249,7 +270,7 @@ function EditPermission({
returnKeyType={'done'}
keyboardAppearance={themeAppearance}
/>
{displayErrorMessage && (
{displayErrorMsg && (
<View style={styles.errorMessageWrapper}>
<ErrorMessage
errorMessage={strings(
Expand All @@ -265,7 +286,7 @@ function EditPermission({
</View>
</View>
<StyledButton
disabled={displayErrorMessage}
disabled={disableBtn}
type="confirm"
onPress={onSetApprovalAmount}
>
Expand All @@ -275,18 +296,4 @@ function EditPermission({
);
}

EditPermission.propTypes = {
host: PropTypes.string.isRequired,
minimumSpendLimit: PropTypes.string,
spendLimitUnlimitedSelected: PropTypes.bool.isRequired,
tokenSymbol: PropTypes.string.isRequired,
spendLimitCustomValue: PropTypes.string.isRequired,
originalApproveAmount: PropTypes.string.isRequired,
onPressSpendLimitUnlimitedSelected: PropTypes.func.isRequired,
onPressSpendLimitCustomSelected: PropTypes.func.isRequired,
onSpendLimitCustomValueChange: PropTypes.func.isRequired,
onSetApprovalAmount: PropTypes.func.isRequired,
toggleEditPermission: PropTypes.func.isRequired,
};

export default EditPermission;
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ function ApprovalTransactionEditionModal({
chainId,
}) {
/* Approval transaction if any */
const [customApprovalTransaction, setCustomApprovalTransaction] =
useState(approvalTransaction);
const [approvalTransactionAmount, setApprovalTransactionAmount] =
useState('');
const [approvalCustomValue, setApprovalCustomValue] =
Expand All @@ -49,10 +51,12 @@ function ApprovalTransactionEditionModal({
(approvalCustomValue) => setApprovalCustomValue(approvalCustomValue),
[],
);

const onPressSpendLimitUnlimitedSelected = useCallback(
() => setSpendLimitUnlimitedSelected(true),
[],
);

const onPressSpendLimitCustomSelected = useCallback(
() => setSpendLimitUnlimitedSelected(false),
[],
Expand All @@ -66,8 +70,9 @@ function ApprovalTransactionEditionModal({
: approvalCustomValue,
sourceToken.decimals,
swapsUtils.getSwapsContractAddress(chainId),
approvalTransaction,
customApprovalTransaction,
);
setCustomApprovalTransaction(newApprovalTransaction);
setApprovalTransaction(newApprovalTransaction);
onCancelEditQuoteTransactions();
} catch (err) {
Expand All @@ -78,27 +83,32 @@ function ApprovalTransactionEditionModal({
spendLimitUnlimitedSelected,
approvalTransactionAmount,
approvalCustomValue,
approvalTransaction,
customApprovalTransaction,
sourceToken,
chainId,
onCancelEditQuoteTransactions,
]);

useEffect(() => {
setApprovalTransaction(originalApprovalTransaction);
if (originalApprovalTransaction) {
const newApprovalTx = spendLimitUnlimitedSelected
? originalApprovalTransaction
: customApprovalTransaction;
setApprovalTransaction(newApprovalTx);
if (newApprovalTx) {
const approvalTransactionAmount = decodeApproveData(
originalApprovalTransaction.data,
newApprovalTx.data,
).encodedAmount;
const amountDec = hexToBN(approvalTransactionAmount).toString(10);
setApprovalTransactionAmount(
fromTokenMinimalUnitString(amountDec, sourceToken.decimals),
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
originalApprovalTransaction,
sourceToken.decimals,
setApprovalTransaction,
spendLimitUnlimitedSelected,
customApprovalTransaction,
]);

return (
Expand All @@ -120,7 +130,7 @@ function ApprovalTransactionEditionModal({
<KeyboardAwareScrollView
contentContainerStyle={styles.keyboardAwareWrapper}
>
{Boolean(approvalTransaction) && (
{Boolean(customApprovalTransaction) && (
<EditPermission
host={'Swaps'}
minimumSpendLimit={minimumSpendLimit}
Expand Down
10 changes: 10 additions & 0 deletions app/util/number/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,16 @@ export function toBN(value) {
return new BN(value);
}

/**
* Determines if a string is a valid number
*
* @param {*} str - Number string
* @returns {boolean} - True if the string is a valid number
*/
export function isNumber(str) {
return /^(\d+(\.\d+)?)$/.test(str);
}

/**
* Converts some unit to wei
*
Expand Down
26 changes: 26 additions & 0 deletions app/util/number/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
toHexadecimal,
safeNumberToBN,
fastSplit,
isNumber,
} from '.';

describe('Number utils :: BNToHex', () => {
Expand Down Expand Up @@ -489,3 +490,28 @@ describe('Number utils :: fastSplit', () => {
expect(fastSplit('test string', ' ')).toEqual('test');
});
});

describe('Number utils :: isNumber', () => {
it('should be a valid number ', () => {
expect(isNumber('1650.7')).toBe(true);
expect(isNumber('1000')).toBe(true);
expect(isNumber('0.0001')).toBe(true);
expect(isNumber('0001')).toBe(true);
expect(isNumber('1')).toBe(true);
});

it('should not be a valid number ', () => {
expect(isNumber('..7')).toBe(false);
expect(isNumber('1..1')).toBe(false);
expect(isNumber('0..')).toBe(false);
expect(isNumber('a.0001')).toBe(false);
expect(isNumber('00a01')).toBe(false);
expect(isNumber('1,.')).toBe(false);
expect(isNumber('1,')).toBe(false);
expect(isNumber('.')).toBe(false);
expect(isNumber('a¡1')).toBe(false);
expect(isNumber('.01')).toBe(false);
expect(isNumber(undefined)).toBe(false);
expect(isNumber(null)).toBe(false);
});
});