Skip to content

Commit

Permalink
Fix QR send (#1916)
Browse files Browse the repository at this point in the history
* Fix QR send

* Working for more types

* More comments

* Remove unnecessary export

* Warning for smart and token address

* Fixes and new warning info adjustments

* Remove comment

* Add comments

* Show error for erc721 and erc20

* Fix bold style
  • Loading branch information
andrepimenta authored Nov 6, 2020
1 parent d439a1e commit 5baf8de
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 37 deletions.
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?.();

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"
},
"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

0 comments on commit 5baf8de

Please sign in to comment.