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

Handle network changes for incoming deeplink and qr code requests. #3650

Merged
merged 13 commits into from
Feb 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
16 changes: 11 additions & 5 deletions app/components/Nav/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ const App = ({ userLoggedIn }) => {
const dispatch = useDispatch();
const triggerCheckedAuth = () => dispatch(checkedAuth('onboarding'));
const triggerSetCurrentRoute = (route) => dispatch(setCurrentRoute(route));
const frequentRpcList = useSelector(
(state) => state?.engine?.backgroundState?.PreferencesController?.frequentRpcList
);

const handleDeeplink = useCallback(({ error, params, uri }) => {
if (error) {
Expand Down Expand Up @@ -155,16 +158,19 @@ const App = ({ userLoggedIn }) => {

useEffect(() => {
SharedDeeplinkManager.init({
navigate: (routeName, opts) => {
const params = { name: routeName, params: opts };
navigator.current?.dispatch?.(CommonActions.navigate(params));
navigation: {
navigate: (routeName, opts) => {
const params = { name: routeName, params: opts };
navigator.current?.dispatch?.(CommonActions.navigate(params));
},
},
frequentRpcList,
dispatch,
});

unsubscribeFromBranch.current = branchSubscriber.subscribe();

return () => unsubscribeFromBranch.current?.();
}, [branchSubscriber]);
}, [branchSubscriber, frequentRpcList, dispatch]);

useEffect(() => {
const initAnalytics = async () => {
Expand Down
2 changes: 1 addition & 1 deletion app/components/UI/ReceiveRequest/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class ReceiveRequest extends PureComponent {
}}
>
<QRCode
value={`ethereum:${this.props.selectedAddress}`}
value={`ethereum:${this.props.selectedAddress}@${this.props.network}`}
size={Dimensions.get('window').width / 2}
/>
</TouchableOpacity>
Expand Down
29 changes: 0 additions & 29 deletions app/components/Views/Send/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,6 @@ class Send extends PureComponent {
/* dApp transaction modal visible or not
*/
dappTransactionModalVisible: PropTypes.bool,
/**
* A list of custom RPCs to provide the user
*/
frequentRpcList: PropTypes.array,
/**
* List of tokens from TokenListController
*/
Expand Down Expand Up @@ -260,9 +256,6 @@ class Send extends PureComponent {
parameters = null,
}) => {
const { addressBook, network, identities, selectedAddress } = this.props;
if (chain_id) {
this.handleNetworkSwitch(chain_id);
}

let newTxMeta = {};
switch (action) {
Expand Down Expand Up @@ -346,28 +339,6 @@ class Send extends PureComponent {
this.mounted && this.setState({ ready: true, transactionKey: Date.now() });
};

/**
* Method in charge of changing network if is needed
*
* @param switchToChainId - Corresponding chain id for new network
*/
handleNetworkSwitch = (switchToChainId) => {
const { frequentRpcList } = this.props;
const rpc = frequentRpcList.find(({ chainId }) => chainId === switchToChainId);
if (rpc) {
const { rpcUrl, chainId, ticker, nickname } = rpc;
const { NetworkController, CurrencyRateController } = Engine.context;
CurrencyRateController.setNativeCurrency(ticker);
NetworkController.setRpcTarget(rpcUrl, chainId, ticker, nickname);
this.props.showAlert({
isVisible: true,
autodismiss: 5000,
content: 'clipboard-alert',
data: { msg: strings('send.warn_network_change') + nickname },
});
}
};

/**
* Retrieves ERC20 asset information (symbol and decimals) to be used with deeplinks
*
Expand Down
131 changes: 92 additions & 39 deletions app/core/DeeplinkManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import { generateApproveData } from '../util/transactions';
import { strings } from '../../locales/i18n';
import { getNetworkTypeById } from '../util/networks';
import { WalletDevice } from '@metamask/controllers/';
import { showAlert } from '../actions/alert';

class DeeplinkManager {
constructor(_navigation) {
this.navigation = _navigation;
constructor({ navigation, frequentRpcList, dispatch }) {
this.navigation = navigation;
this.pendingDeeplink = null;
this.frequentRpcList = frequentRpcList;
this.dispatch = dispatch;
}

setDeeplink = (url) => (this.pendingDeeplink = url);
Expand All @@ -24,6 +27,52 @@ class DeeplinkManager {

expireDeeplink = () => (this.pendingDeeplink = null);

/**
* Method in charge of changing network if is needed
*
* @param switchToChainId - Corresponding chain id for new network
*/
handleNetworkSwitch = (switchToChainId) => {
const { NetworkController, CurrencyRateController } = Engine.context;

// If current network is the same as the one we want to switch to, do nothing
if (NetworkController?.state?.provider?.chainId === switchToChainId) {
return;
}

const rpc = this.frequentRpcList.find(({ chainId }) => chainId === switchToChainId);

if (rpc) {
const { rpcUrl, chainId, ticker, nickname } = rpc;
CurrencyRateController.setNativeCurrency(ticker);
NetworkController.setRpcTarget(rpcUrl, chainId, ticker, nickname);
this.dispatch(
showAlert({
isVisible: true,
autodismiss: 5000,
content: 'clipboard-alert',
data: { msg: strings('send.warn_network_change') + nickname },
})
);
return;
}

const networkType = getNetworkTypeById(switchToChainId);

if (networkType) {
CurrencyRateController.setNativeCurrency('ETH');
NetworkController.setProviderType(networkType);
this.dispatch(
showAlert({
isVisible: true,
autodismiss: 5000,
content: 'clipboard-alert',
data: { msg: strings('send.warn_network_change') + networkType },
})
);
}
};

async handleEthereumUrl(url, origin) {
let ethUrl = '';
try {
Expand All @@ -33,45 +82,49 @@ class DeeplinkManager {
return;
}

const functionName = ethUrl.function_name;
if (!functionName) {
const txMeta = { ...ethUrl, source: url };
if (ethUrl.parameters?.value) {
try {
// Validate and switch network before performing any other action
this.handleNetworkSwitch(ethUrl.chain_id);
const functionName = ethUrl.function_name;
if (!functionName) {
const txMeta = { ...ethUrl, source: url };
if (ethUrl.parameters?.value) {
this.navigation.navigate('SendView', {
screen: 'Send',
params: { txMeta: { ...txMeta, action: 'send-eth' } },
});
} else {
this.navigation.navigate('SendFlowView', { screen: 'SendTo', params: { txMeta } });
}
} else if (functionName === 'transfer') {
const txMeta = { ...ethUrl, source: url };
this.navigation.navigate('SendView', {
screen: 'Send',
params: { txMeta: { ...txMeta, action: 'send-eth' } },
params: { txMeta: { ...txMeta, action: 'send-token' } },
});
} else {
this.navigation.navigate('SendFlowView', { screen: 'SendTo', params: { txMeta } });
} else if (functionName === 'approve') {
// add approve transaction
const {
parameters: { address, uint256 },
target_address,
} = ethUrl;
const { TransactionController, PreferencesController } = Engine.context;
const txParams = {};
txParams.to = `${target_address}`;
txParams.from = `${PreferencesController.state.selectedAddress}`;
txParams.value = '0x0';
const uint256Number = Number(uint256);
if (Number.isNaN(uint256Number)) throw new Error('The parameter uint256 should be a number');
if (!Number.isInteger(uint256Number)) throw new Error('The parameter uint256 should be an integer');
const value = uint256Number.toString(16);
txParams.data = generateApproveData({ spender: address, value });
TransactionController.addTransaction(txParams, origin, WalletDevice.MM_MOBILE);
}
} else if (functionName === 'transfer') {
const txMeta = { ...ethUrl, source: url };
this.navigation.navigate('SendView', {
screen: 'Send',
params: { txMeta: { ...txMeta, action: 'send-token' } },
});
} else if (functionName === 'approve') {
// add approve transaction
const {
parameters: { address, uint256 },
target_address,
chain_id,
} = ethUrl;
const { TransactionController, PreferencesController, NetworkController } = Engine.context;
if (chain_id) {
const newNetworkType = getNetworkTypeById(chain_id);
NetworkController.setProviderType(newNetworkType);
}
const txParams = {};
txParams.to = `${target_address}`;
txParams.from = `${PreferencesController.state.selectedAddress}`;
txParams.value = '0x0';
const uint256Number = Number(uint256);
if (Number.isNaN(uint256Number)) throw new Error('The parameter uint256 should be a number');
if (!Number.isInteger(uint256Number)) throw new Error('The parameter uint256 should be an integer');
const value = uint256Number.toString(16);
txParams.data = generateApproveData({ spender: address, value });
TransactionController.addTransaction(txParams, origin, WalletDevice.MM_MOBILE);
} catch (e) {
Alert.alert(
strings('send.network_not_found_title'),
strings('send.network_not_found_description', { chain_id: ethUrl.chain_id })
);
}
}

Expand Down Expand Up @@ -202,8 +255,8 @@ class DeeplinkManager {
let instance = null;

const SharedDeeplinkManager = {
init: (navigation) => {
instance = new DeeplinkManager(navigation);
init: ({ navigation, frequentRpcList, dispatch }) => {
instance = new DeeplinkManager({ navigation, frequentRpcList, dispatch });
},
parse: (url, args) => instance.parse(url, args),
setDeeplink: (url) => instance.setDeeplink(url),
Expand Down
2 changes: 0 additions & 2 deletions ios/MetaMask.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,6 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
Expand Down Expand Up @@ -1004,7 +1003,6 @@
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
Expand Down
4 changes: 3 additions & 1 deletion locales/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,9 @@
"warn_network_change": "Network changed to ",
"send_to": "Send to",
"amount": "Amount",
"confirm": "Confirm"
"confirm": "Confirm",
"network_not_found_title": "Network not found",
"network_not_found_description": "Network with chain id {{chain_id}} not found in your wallet. Please add the network first."
},
"deposit": {
"title": "Deposit"
Expand Down