Skip to content

Commit

Permalink
[FEAT] Keystone integration (#3915)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustavo Antunes authored Apr 6, 2022
1 parent 03c523e commit b924556
Show file tree
Hide file tree
Showing 75 changed files with 3,121 additions and 900 deletions.
12 changes: 12 additions & 0 deletions app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import RevealPrivateCredential from '../../Views/RevealPrivateCredential';
import WalletConnectSessions from '../../Views/WalletConnectSessions';
import OfflineMode from '../../Views/OfflineMode';
import QrScanner from '../../Views/QRScanner';
import ConnectQRHardware from '../../Views/ConnectQRHardware';
import LockScreen from '../../Views/LockScreen';
import EnterPasswordSimple from '../../Views/EnterPasswordSimple';
import ChoosePassword from '../../Views/ChoosePassword';
Expand Down Expand Up @@ -321,6 +322,16 @@ const SetPasswordFlow = () => (
</Stack.Navigator>
);

const ConnectQRHardwareFlow = () => (
<Stack.Navigator
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="ConnectQRHardware" component={ConnectQRHardware} />
</Stack.Navigator>
);

const MainNavigator = () => (
<Stack.Navigator
screenOptions={{
Expand All @@ -346,6 +357,7 @@ const MainNavigator = () => (
<Stack.Screen name="Webview" component={Webview} />
<Stack.Screen name="SettingsView" component={SettingsModalStack} />
<Stack.Screen name="ImportPrivateKeyView" component={ImportPrivateKeyView} />
<Stack.Screen name="ConnectQRHardwareFlow" component={ConnectQRHardwareFlow} />
<Stack.Screen name="SendView" component={SendView} />
<Stack.Screen name="SendFlowView" component={SendFlowView} />
<Stack.Screen name="AddBookmarkView" component={AddBookmarkView} />
Expand Down
31 changes: 25 additions & 6 deletions app/components/Nav/Main/RootRPCMethodsUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ import BigNumber from 'bignumber.js';
import { getTokenList } from '../../../reducers/tokens';
import { toLowerCaseEquals } from '../../../util/general';
import { ApprovalTypes } from '../../../core/RPCMethods/RPCMethodMiddleware';
import { KEYSTONE_TX_CANCELED } from '../../../constants/error';
import AnalyticsV2 from '../../../util/analyticsV2';
import { mockTheme, useAppThemeFromContext } from '../../../util/theme';
import withQRHardwareAwareness from '../../UI/QRHardware/withQRHardwareAwareness';
import QRSigningModal from '../../UI/QRHardware/QRSigningModal';
import { networkSwitched } from '../../../actions/onboardNetwork';

const hstInterface = new ethers.utils.Interface(abi);
Expand Down Expand Up @@ -193,7 +197,7 @@ const RootRPCMethodsUI = (props) => {

const autoSign = useCallback(
async (transactionMeta) => {
const { TransactionController } = Engine.context;
const { TransactionController, KeyringController } = Engine.context;
try {
TransactionController.hub.once(`${transactionMeta.id}:finished`, (transactionMeta) => {
if (transactionMeta.status === 'submitted') {
Expand All @@ -213,12 +217,17 @@ const RootRPCMethodsUI = (props) => {
trackSwaps(ANALYTICS_EVENT_OPTS.SWAP_COMPLETED, transactionMeta);
}
});
await KeyringController.resetQRKeyringState();
await TransactionController.approveTransaction(transactionMeta.id);
} catch (error) {
Alert.alert(strings('transactions.transaction_error'), error && error.message, [
{ text: strings('navigation.ok') },
]);
Logger.error(error, 'error while trying to send transaction (Main)');
if (!error?.message.startsWith(KEYSTONE_TX_CANCELED)) {
Alert.alert(strings('transactions.transaction_error'), error && error.message, [
{ text: strings('navigation.ok') },
]);
Logger.error(error, 'error while trying to send transaction (Main)');
} else {
AnalyticsV2.trackEvent(AnalyticsV2.ANALYTICS_EVENTS.QR_HARDWARE_TRANSACTION_CANCELED);
}
}
},
[props.swapsTransactions, trackSwaps]
Expand Down Expand Up @@ -380,6 +389,13 @@ const RootRPCMethodsUI = (props) => {
</Modal>
);

const renderQRSigningModal = () => {
const { isSigningQRObject, QRState, approveModalVisible, dappTransactionModalVisible } = props;
const shouldRenderThisModal =
!showPendingApproval && !approveModalVisible && !dappTransactionModalVisible && isSigningQRObject;
return shouldRenderThisModal && <QRSigningModal isVisible={isSigningQRObject} QRState={QRState} />;
};

const onWalletConnectSessionApproval = () => {
const { peerId } = walletConnectRequestInfo;
setWalletConnectRequest(false);
Expand Down Expand Up @@ -674,6 +690,7 @@ const RootRPCMethodsUI = (props) => {
{renderSwitchCustomNetworkModal()}
{renderAccountsApprovalModal()}
{renderWatchAssetModal()}
{renderQRSigningModal()}
</React.Fragment>
);
};
Expand Down Expand Up @@ -720,6 +737,8 @@ RootRPCMethodsUI.propTypes = {
* Chain id
*/
chainId: PropTypes.string,
isSigningQRObject: PropTypes.bool,
QRState: PropTypes.object,
/**
* updates redux when network is switched
*/
Expand All @@ -744,4 +763,4 @@ const mapDispatchToProps = (dispatch) => ({
networkSwitched: ({ networkUrl, networkStatus }) => dispatch(networkSwitched({ networkUrl, networkStatus })),
});

export default connect(mapStateToProps, mapDispatchToProps)(RootRPCMethodsUI);
export default connect(mapStateToProps, mapDispatchToProps)(withQRHardwareAwareness(RootRPCMethodsUI));
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exports[`AccountApproval should render correctly 1`] = `
}
dispatch={[Function]}
networkType="ropsten"
selectedAddress="0xe7E125654064EEa56229f273dA586F10DF96B0a1"
tokensLength={0}
/>
`;
9 changes: 8 additions & 1 deletion app/components/UI/AccountApproval/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Device from '../../../util/device';
import NotificationManager from '../../../core/NotificationManager';
import AnalyticsV2 from '../../../util/analyticsV2';
import URL from 'url-parse';
import { getAddressAccountType } from '../../../util/address';
import { ThemeContext, mockTheme } from '../../../util/theme';
import { ACCOUNT_APROVAL_MODAL_CONTAINER_ID, CANCEL_BUTTON_ID } from '../../../constants/test-ids';

Expand Down Expand Up @@ -78,6 +79,10 @@ class AccountApproval extends PureComponent {
* Callback triggered on account access rejection
*/
onCancel: PropTypes.func,
/**
* A string that represents the selected address
*/
selectedAddress: PropTypes.string,
/**
* Number of tokens
*/
Expand Down Expand Up @@ -106,9 +111,10 @@ class AccountApproval extends PureComponent {

getAnalyticsParams = () => {
try {
const { currentPageInformation, chainId, networkType } = this.props;
const { currentPageInformation, chainId, networkType, selectedAddress } = this.props;
const url = new URL(currentPageInformation?.url);
return {
account_type: getAddressAccountType(selectedAddress),
dapp_host_name: url?.host,
dapp_url: currentPageInformation?.url,
network_name: networkType,
Expand Down Expand Up @@ -219,6 +225,7 @@ class AccountApproval extends PureComponent {

const mapStateToProps = (state) => ({
accountsLength: Object.keys(state.engine.backgroundState.AccountTrackerController.accounts || {}).length,
selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress,
tokensLength: state.engine.backgroundState.TokensController.tokens.length,
networkType: state.engine.backgroundState.NetworkController.provider.type,
chainId: state.engine.backgroundState.NetworkController.provider.chainId,
Expand Down
83 changes: 75 additions & 8 deletions app/components/UI/AccountInfoCard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { strings } from '../../../../locales/i18n';
import { connect } from 'react-redux';
import { renderAccountName, renderShortAddress } from '../../../util/address';
import { getTicker } from '../../../util/transactions';
import Engine from '../../../core/Engine';
import { QR_HARDWARE_WALLET_DEVICE } from '../../../constants/keyringTypes';
import Device from '../../../util/device';
import { ThemeContext, mockTheme } from '../../../util/theme';

Expand All @@ -19,7 +21,8 @@ const createStyles = (colors) =>
borderWidth: 1,
borderColor: colors.border.default,
borderRadius: 10,
padding: 16,
padding: Device.isMediumDevice() ? 8 : 16,
alignItems: 'center',
},
identicon: {
marginRight: 8,
Expand All @@ -29,6 +32,7 @@ const createStyles = (colors) =>
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'flex-start',
marginRight: 8,
},
accountNameAndAddress: {
width: '100%',
Expand All @@ -42,18 +46,40 @@ const createStyles = (colors) =>
marginRight: 2,
color: colors.text.default,
},
accountNameSmall: {
fontSize: 12,
},
accountAddress: {
flexGrow: 1,
...fontStyles.bold,
fontSize: 16,
color: colors.text.default,
},
accountAddressSmall: {
fontSize: 12,
},
balanceText: {
...fontStyles.thin,
fontSize: 14,
alignSelf: 'flex-start',
color: colors.text.default,
},
balanceTextSmall: {
fontSize: 12,
},
tag: {
borderRadius: 14,
borderWidth: 1,
borderColor: colors.text.default,
padding: 4,
minWidth: 42,
},
tagText: {
textAlign: 'center',
fontSize: 8,
...fontStyles.bold,
color: colors.text.default,
},
});

class AccountInfoCard extends PureComponent {
Expand Down Expand Up @@ -82,18 +108,44 @@ class AccountInfoCard extends PureComponent {
* Declares the operation being performed i.e. 'signing'
*/
operation: PropTypes.string,
/**
* Clarify should show fiat balance
*/
showFiatBalance: PropTypes.bool,
/**
* Current selected ticker
*/
ticker: PropTypes.string,
};

state = {
isHardwareKeyring: false,
};

componentDidMount() {
const { KeyringController } = Engine.context;
const { selectedAddress } = this.props;
KeyringController.getAccountKeyringType(selectedAddress).then((type) => {
if (type === QR_HARDWARE_WALLET_DEVICE) {
this.setState({ isHardwareKeyring: true });
}
});
}

render() {
const { accounts, selectedAddress, identities, conversionRate, currentCurrency, operation, ticker } =
this.props;
const {
accounts,
selectedAddress,
identities,
conversionRate,
currentCurrency,
operation,
ticker,
showFiatBalance = true,
} = this.props;
const { isHardwareKeyring } = this.state;
const colors = this.context.colors || mockTheme.colors;
const styles = createStyles(colors);

const weiBalance = hexToBN(accounts[selectedAddress].balance);
const balance = `(${renderFromWei(weiBalance)} ${getTicker(ticker)})`;
const accountLabel = renderAccountName(selectedAddress, identities);
Expand All @@ -104,19 +156,34 @@ class AccountInfoCard extends PureComponent {
<Identicon address={selectedAddress} diameter={40} customStyle={styles.identicon} />
<View style={styles.accountInfoRow}>
<View style={styles.accountNameAndAddress}>
<Text numberOfLines={1} style={styles.accountName}>
<Text
numberOfLines={1}
style={[styles.accountName, isHardwareKeyring ? styles.accountNameSmall : undefined]}
>
{accountLabel}
</Text>
<Text numberOfLines={1} style={styles.accountAddress}>
<Text
numberOfLines={1}
style={[styles.accountAddress, isHardwareKeyring ? styles.accountAddressSmall : undefined]}
>
({address})
</Text>
</View>
{operation === 'signing' ? null : (
<Text numberOfLines={1} style={styles.balanceText}>
{strings('signature_request.balance_title')} {dollarBalance} {balance}
<Text
numberOfLines={1}
style={[styles.balanceText, isHardwareKeyring ? styles.balanceTextSmall : undefined]}
>
{strings('signature_request.balance_title')} {showFiatBalance ? dollarBalance : ''}{' '}
{balance}
</Text>
)}
</View>
{isHardwareKeyring && (
<View style={styles.tag}>
<Text style={styles.tagText}>{strings('transaction.hardware')}</Text>
</View>
)}
</View>
);
}
Expand Down
24 changes: 13 additions & 11 deletions app/components/UI/AccountList/AccountElement/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const createStyles = (colors) =>
},
importedView: {
flex: 0.5,
alignItems: 'center',
alignItems: 'flex-start',
marginTop: 2,
},
accountMain: {
Expand All @@ -71,7 +71,6 @@ const createStyles = (colors) =>
...fontStyles.bold,
},
importedWrapper: {
width: 73,
paddingHorizontal: 10,
paddingVertical: 3,
borderRadius: 10,
Expand Down Expand Up @@ -122,18 +121,21 @@ class AccountElement extends PureComponent {

render() {
const { disabled, updatedBalanceFromStore, ticker } = this.props;
const { address, name, ens, isSelected, isImported, balanceError } = this.props.item;
const { address, name, ens, isSelected, isImported, balanceError, isQRHardware } = this.props.item;
const colors = this.context.colors || mockTheme.colors;
const styles = createStyles(colors);

const selected = isSelected ? <Icon name="check-circle" size={30} color={colors.primary.default} /> : null;
const imported = isImported ? (
<View style={styles.importedWrapper}>
<Text numberOfLines={1} style={styles.importedText}>
{strings('accounts.imported')}
</Text>
</View>
) : null;
const tag =
isImported || isQRHardware ? (
<View style={styles.importedView}>
<View style={styles.importedWrapper}>
<Text numberOfLines={1} style={styles.importedText}>
{strings(isImported ? 'accounts.imported' : 'transaction.hardware')}
</Text>
</View>
</View>
) : null;

return (
<View onStartShouldSetResponder={() => true}>
Expand Down Expand Up @@ -161,7 +163,7 @@ class AccountElement extends PureComponent {
)}
</View>
</View>
{!!imported && <View style={styles.importedView}>{imported}</View>}
{!!tag && tag}
<View style={styles.selectedWrapper}>{selected}</View>
</View>
</TouchableOpacity>
Expand Down
Loading

0 comments on commit b924556

Please sign in to comment.