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

Fix QR send #1916

Merged
merged 11 commits into from
Nov 6, 2020
41 changes: 28 additions & 13 deletions app/components/Views/QRScanner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Icon from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types';
import { parse } from 'eth-url-parser';
import { strings } from '../../../../locales/i18n';
import SharedDeeplinkManager from '../../../core/DeeplinkManager';
import AppConstants from '../../../core/AppConstants';

const styles = StyleSheet.create({
container: {
Expand Down Expand Up @@ -93,19 +95,31 @@ export default class QrScanner extends PureComponent {
this.props.navigation.goBack();
}
} else {
if (content.split('ethereum:').length > 1) {
// Let ethereum:address go forward
if (content.split('ethereum:').length > 1 && !parse(content).function_name) {
this.shouldReadBarCode = false;
data = parse(content);
let action = 'send-eth';
if (data.function_name === 'transfer') {
// Send erc20 token
action = 'send-token';
}
const action = 'send-eth';
data = { ...data, action };
} else if (
content.length === 64 ||
(content.substring(0, 2).toLowerCase() === '0x' && content.length === 66)
) {
this.mounted = false;
this.props.navigation.goBack();
this.props.navigation.state.params.onScanSuccess(data, content);
return;
}

// Checking if it can be handled like deeplinks
const handledByDeeplink = SharedDeeplinkManager.parse(content, {
origin: AppConstants.DEEPLINKS.ORIGIN_QR_CODE,
onHandled: () => this.props.navigation.pop(2)
});

if (handledByDeeplink) {
this.mounted = false;
return;
}

// I can't be handled by deeplinks, checking other options
if (content.length === 64 || (content.substring(0, 2).toLowerCase() === '0x' && content.length === 66)) {
this.shouldReadBarCode = false;
data = { private_key: content.length === 64 ? content : content.substr(2) };
} else if (content.substring(0, 2).toLowerCase() === '0x') {
Expand All @@ -121,10 +135,11 @@ export default class QrScanner extends PureComponent {
// EIP-945 allows scanning arbitrary data
data = content;
}
this.mounted = false;
this.props.navigation.goBack();
this.props.navigation.state.params.onScanSuccess(data, content);
}

this.mounted = false;
this.props.navigation.goBack();
this.props.navigation.state.params.onScanSuccess(data, content);
};

onError = error => {
Expand Down
55 changes: 48 additions & 7 deletions app/components/Views/SendFlow/ErrorMessage/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
import { colors, fontStyles } from '../../../../styles/common';
import PropTypes from 'prop-types';
import { strings } from '../../../../../locales/i18n';

const styles = StyleSheet.create({
wrapper: {
wrapperError: {
backgroundColor: colors.red000,
borderWidth: 1,
borderColor: colors.red,
Expand All @@ -17,21 +18,61 @@ const styles = StyleSheet.create({
color: colors.red,
flexDirection: 'row',
alignItems: 'center'
},
button: {
marginTop: 27
},
buttonMessage: {
...fontStyles.normal,
fontSize: 12,
color: colors.blue,
textAlign: 'center'
},
wrapperWarning: {
backgroundColor: colors.blue000,
borderWidth: 1,
borderColor: colors.blue,
borderRadius: 4,
padding: 15
},
warningMessage: {
...fontStyles.normal,
fontSize: 12,
color: colors.blue,
flexDirection: 'row',
alignItems: 'center'
}
});

export default function ErrorMessage(props) {
const { errorMessage } = props;
const { errorMessage, errorContinue, onContinue, isOnlyWarning } = props;
return (
<View style={styles.wrapper} testID={'error-message-warning'}>
<Text style={styles.errorMessage}>{errorMessage}</Text>
<View style={isOnlyWarning ? styles.wrapperWarning : styles.wrapperError} testID={'error-message-warning'}>
<Text style={isOnlyWarning ? styles.warningMessage : styles.errorMessage}>{errorMessage}</Text>
{errorContinue && (
<TouchableOpacity onPress={onContinue} style={styles.button}>
<Text style={styles.buttonMessage}>{strings('transaction.continueError')}</Text>
</TouchableOpacity>
)}
</View>
);
}

ErrorMessage.propTypes = {
/**
* Error message to display
* Error message to display, can be a string or a Text component
*/
errorMessage: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.string]),
/**
* Show continue button when it is a contract address
*/
errorContinue: PropTypes.bool,
/**
* Function that is called when continue button is pressed
*/
onContinue: PropTypes.func,
/**
* Show a warning info instead of an error
*/
errorMessage: PropTypes.string
isOnlyWarning: PropTypes.bool
};
73 changes: 59 additions & 14 deletions app/components/Views/SendFlow/SendTo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ const styles = StyleSheet.create({
...fontStyles.bold,
color: colors.black,
textDecorationLine: 'underline'
},
bold: {
...fontStyles.bold
}
});

Expand Down Expand Up @@ -277,9 +280,10 @@ class SendFlow extends PureComponent {
};

onToSelectedAddressChange = async toSelectedAddress => {
const { AssetsContractController } = Engine.context;
const { addressBook, network, identities } = this.props;
const networkAddressBook = addressBook[network] || {};
let addressError, toAddressName, toEnsName;
let addressError, toAddressName, toEnsName, errorContinue, isOnlyWarning;
let [addToAddressToAddressBook, toSelectedAddressReady] = [false, false];
if (isValidAddress(toSelectedAddress)) {
const checksummedToSelectedAddress = toChecksumAddress(toSelectedAddress);
Expand All @@ -299,6 +303,36 @@ class SendFlow extends PureComponent {
// If not in address book nor user accounts
addToAddressToAddressBook = true;
}

// Check if it's token contract address
try {
const symbol = await AssetsContractController.getAssetSymbol(toSelectedAddress);
if (symbol) {
addressError = (
<Text>
<Text>{strings('transaction.tokenContractAddressWarning_1')}</Text>
<Text style={styles.bold}>{strings('transaction.tokenContractAddressWarning_2')}</Text>
<Text>{strings('transaction.tokenContractAddressWarning_3')}</Text>
</Text>
);
errorContinue = true;
}
} catch (e) {
// Not a token address
}

/**
* Not using this for now; Import isSmartContractAddress from utils/transaction and use this for checking smart contract: await isSmartContractAddress(toSelectedAddress);
* Check if it's smart contract address
*/
/*
const smart = false; //

if (smart) {
addressError = strings('transaction.smartContractAddressWarning');
isOnlyWarning = true;
}
*/
} else if (isENS(toSelectedAddress)) {
toEnsName = toSelectedAddress;
const resolvedAddress = await doENSLookup(toSelectedAddress, network);
Expand All @@ -322,7 +356,9 @@ class SendFlow extends PureComponent {
addToAddressToAddressBook,
toSelectedAddressReady,
toSelectedAddressName: toAddressName,
toEnsName
toEnsName,
errorContinue,
isOnlyWarning
});
};

Expand Down Expand Up @@ -499,7 +535,9 @@ class SendFlow extends PureComponent {
addressError,
balanceIsZero,
toInputHighlighted,
inputWidth
inputWidth,
errorContinue,
isOnlyWarning
} = this.state;
return (
<SafeAreaView style={styles.wrapper} testID={'send-screen'}>
Expand Down Expand Up @@ -527,7 +565,12 @@ class SendFlow extends PureComponent {
</View>
{addressError && (
<View style={styles.addressErrorWrapper} testID={'address-error'}>
<ErrorMessage errorMessage={addressError} />
<ErrorMessage
errorMessage={addressError}
errorContinue={!!errorContinue}
onContinue={this.onTransactionDirectionSet}
isOnlyWarning={!!isOnlyWarning}
/>
</View>
)}

Expand Down Expand Up @@ -565,16 +608,18 @@ class SendFlow extends PureComponent {
/>
</View>
)}
<View style={styles.buttonNextWrapper}>
<StyledButton
type={'confirm'}
containerStyle={styles.buttonNext}
onPress={this.onTransactionDirectionSet}
testID={'address-book-next-button'}
>
{strings('address_book.next')}
</StyledButton>
</View>
{!errorContinue && (
<View style={styles.buttonNextWrapper}>
<StyledButton
type={'confirm'}
containerStyle={styles.buttonNext}
onPress={this.onTransactionDirectionSet}
testID={'address-book-next-button'}
>
{strings('address_book.next')}
</StyledButton>
</View>
)}
</View>
</View>
)}
Expand Down
16 changes: 15 additions & 1 deletion app/core/DeeplinkManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class DeeplinkManager {
});
}

parse(url, { browserCallBack, origin }) {
parse(url, { browserCallBack, origin, onHandled }) {
const urlObj = new URL(url);
let params;

Expand All @@ -88,12 +88,16 @@ class DeeplinkManager {
Alert.alert(strings('deeplink.invalid'), e.toString());
}
}

const handled = () => onHandled?.();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍


const { MM_UNIVERSAL_LINK_HOST } = AppConstants;

switch (urlObj.protocol.replace(':', '')) {
case 'http':
case 'https':
// Universal links
handled();
if (urlObj.hostname === MM_UNIVERSAL_LINK_HOST) {
// action is the first parth of the pathname
const action = urlObj.pathname.split('/')[1];
Expand Down Expand Up @@ -130,6 +134,8 @@ class DeeplinkManager {
}
} else {
// Normal links (same as dapp)

handled();
urlObj.set('protocol', 'https:');
this.handleBrowserUrl(urlObj.href, browserCallBack);
}
Expand All @@ -138,6 +144,7 @@ class DeeplinkManager {
// walletconnect related deeplinks
// address, transactions, etc
case 'wc':
handled();
if (!WalletConnect.isValidUri(url)) return;
// eslint-disable-next-line no-case-declarations
const redirect = params && params.redirect;
Expand All @@ -146,22 +153,29 @@ class DeeplinkManager {
WalletConnect.newSession(url, redirect, autosign);
break;
case 'ethereum':
handled();
this.handleEthereumUrl(url, origin);
break;

// Specific to the browser screen
// For ex. navigate to a specific dapp
case 'dapp':
// Enforce https
handled();
urlObj.set('protocol', 'https:');
this.handleBrowserUrl(urlObj.href, browserCallBack);
break;

// Specific to the MetaMask app
// For ex. go to settings
case 'metamask':
handled();
break;
default:
return false;
}

return true;
}
}

Expand Down
7 changes: 6 additions & 1 deletion locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,12 @@
"pending": "Pending",
"submitted": "Submitted",
"failed": "Failed",
"cancelled": "Cancelled"
"cancelled": "Cancelled",
"tokenContractAddressWarning_1": "WARNING: This address is a ",
"tokenContractAddressWarning_2": "token contract address",
"tokenContractAddressWarning_3": ". If you send tokens to this address, you will lose them.",
"smartContractAddressWarning": "This address is a smart contract address. Please make sure you understand what this address is for, otherwise you risk losing your funds.",
"continueError": "I understand the risks, continue"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

es.json?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added!

},
"custom_gas": {
"total": "Total",
Expand Down
7 changes: 6 additions & 1 deletion locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,12 @@
"pending": "Pendiente",
"submitted": "Enviada",
"failed": "Fallida",
"cancelled": "Cancelada"
"cancelled": "Cancelada",
"tokenContractAddressWarning_1": "ADVERTENCIA: Esta dirección es una dirección de ",
"tokenContractAddressWarning_2": "contrato de token",
"tokenContractAddressWarning_3": ". Si envías tokens a esta dirección, los perderás.",
"smartContractAddressWarning": "Esta dirección es una dirección de contrato inteligente. Asegúrate de comprender para qué sirve esta dirección; de lo contrario, corre el riesgo de perder sus fondos.",
"continueError": "Entiendo los riesgos, continuar"
},
"custom_gas": {
"advanced_options": "Mostrar opciones avanzadas",
Expand Down