Skip to content

Commit

Permalink
feat: handle wallet_switchEthereumChain modal and autoswitch between …
Browse files Browse the repository at this point in the history
…network
  • Loading branch information
abretonc7s committed Jul 12, 2023
1 parent 1b8430c commit 77c0b36
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 14 deletions.
53 changes: 39 additions & 14 deletions app/core/WalletConnect/WalletConnectV2.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Minimizer } from '../NativeModules';
import AppConstants from '../AppConstants';
import BackgroundBridge from '../BackgroundBridge/BackgroundBridge';
import { Minimizer } from '../NativeModules';
import getRpcMethodMiddleware, {
ApprovalTypes,
} from '../RPCMethods/RPCMethodMiddleware';
Expand All @@ -16,9 +16,7 @@ import {
WalletDevice,
} from '@metamask/transaction-controller';

// disable linting as core is included from se-sdk,
// including it in package.json overwrites sdk deps and create error
// eslint-disable-next-line import/no-extraneous-dependencies
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Core } from '@walletconnect/core';
import { ErrorResponse } from '@walletconnect/jsonrpc-types';
import Client, {
Expand All @@ -31,9 +29,10 @@ import Engine from '../Engine';
import getAllUrlParams from '../SDKConnect/utils/getAllUrlParams.util';
import { waitForKeychainUnlocked } from '../SDKConnect/utils/wait.util';
import WalletConnect from './WalletConnect';
import parseWalletConnectUri from './wc-utils';
import AsyncStorage from '@react-native-async-storage/async-storage';
import METHODS_TO_REDIRECT from './wc-config';
import parseWalletConnectUri, {
waitForNetworkModalOnboarding,
} from './wc-utils';

const { PROJECT_ID } = AppConstants.WALLET_CONNECT;
export const isWC2Enabled =
Expand All @@ -50,13 +49,19 @@ const ERROR_MESSAGES = {
const ERROR_CODES = {
USER_REJECT_CODE: 5000,
};

const RPC_WALLET_SWITCHETHEREUMCHAIN = 'wallet_switchEthereumChain';

class WalletConnect2Session {
private backgroundBridge: BackgroundBridge;
private web3Wallet: Client;
private deeplink: boolean;
private session: SessionTypes.Struct;
private requestsToRedirect: { [request: string]: boolean } = {};
private topicByRequestId: { [requestId: string]: string } = {};
private requestByRequestId: {
[requestId: string]: SingleEthereumTypes.SessionRequest;
} = {};

constructor({
web3Wallet,
Expand Down Expand Up @@ -149,6 +154,26 @@ class WalletConnect2Session {

approveRequest = async ({ id, result }: { id: string; result: unknown }) => {
const topic = this.topicByRequestId[id];
const initialRequest = this.requestByRequestId[id];

// Special case for eth_switchNetwork to wait for the modal to be closed
if (
initialRequest?.params.request.method === RPC_WALLET_SWITCHETHEREUMCHAIN
) {
try {
const params = initialRequest.params.request.params as unknown[];
const { chainId } = params[0] as { chainId: string };

if (chainId) {
await waitForNetworkModalOnboarding({
chainId: parseInt(chainId) + '',
});
}
} catch (err) {
// Ignore error as it is not critical when timeout for modal is reached
// It allows to safely continue and prevent pilling up the requests.
}
}

try {
await this.web3Wallet.approveRequest({
Expand Down Expand Up @@ -223,6 +248,7 @@ class WalletConnect2Session {

handleRequest = async (requestEvent: SingleEthereumTypes.SessionRequest) => {
this.topicByRequestId[requestEvent.id] = requestEvent.topic;
this.requestByRequestId[requestEvent.id] = requestEvent;

const verified = requestEvent.verifyContext?.verified;
const hostname = verified?.origin;
Expand Down Expand Up @@ -385,20 +411,19 @@ export class WC2Manager {
}
} catch (err) {
console.warn(`WC2::init Init failed due to ${err}`);
throw err;
}

let web3Wallet;
const options: SingleEthereumTypes.Options = {
core: core as any,
metadata: AppConstants.WALLET_CONNECT.METADATA,
};
try {
web3Wallet = await SingleEthereum.init({
core: core as any,
metadata: AppConstants.WALLET_CONNECT.METADATA,
});
web3Wallet = await SingleEthereum.init(options);
} catch (err) {
// TODO Sometime needs to init twice --- not sure why...
web3Wallet = await SingleEthereum.init({
core: core as any,
metadata: AppConstants.WALLET_CONNECT.METADATA,
});
web3Wallet = await SingleEthereum.init(options);
}

let deeplinkSessions = {};
Expand Down
30 changes: 30 additions & 0 deletions app/core/WalletConnect/wc-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { RelayerTypes } from '@walletconnect/types';
import { parseRelayParams } from '@walletconnect/utils';
import qs from 'qs';
import { store } from '../../../app/store';
import { wait } from '../SDKConnect/utils/wait.util';

export interface WCMultiVersionParams {
protocol: string;
Expand Down Expand Up @@ -52,4 +54,32 @@ export const isValidWCURI = (uri: string): boolean => {
return false;
};

const MAX_LOOP_COUNTER = 60;
export const waitForNetworkModalOnboarding = async ({
chainId,
}: {
chainId: string;
}): Promise<void> => {
let waitForNetworkModalOnboarded = true;

// throw timeout error after 30sec
let loopCounter = 0;

while (waitForNetworkModalOnboarded) {
loopCounter += 1;
const { networkOnboarded } = store.getState();
const { networkOnboardedState } = networkOnboarded;

if (networkOnboardedState[chainId]) {
waitForNetworkModalOnboarded = false;
// exit the looop
} else {
await wait(1000);
}

if (loopCounter >= MAX_LOOP_COUNTER) {
throw new Error('Timeout error');
}
}
};
export default parseWalletConnectUri;

0 comments on commit 77c0b36

Please sign in to comment.