Skip to content

Commit

Permalink
feat: update walletconnect se-sdk (#10103)
Browse files Browse the repository at this point in the history
## **Description**

Update walletconnect se-sdk to latest 1.8.1

## **Related issues**

Fixes:

## **Manual testing steps**

1. Documentation and evidence is include in the comments

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

---------

Co-authored-by: Andrea Salvatore <andrea.salvatore@consensys.net>
Co-authored-by: sethkfman <10342624+sethkfman@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 17, 2024
1 parent 99ac349 commit a9522c9
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 101 deletions.
4 changes: 3 additions & 1 deletion app/core/RPCMethods/wallet_addEthereumChain.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ const wallet_addEthereumChain = async ({
);
}

if (Object.values(ChainId).find((value) => value === _chainId)) {
//TODO: Remove aurora from default chains in @metamask/controller-utils
const actualChains = { ...ChainId, aurora: undefined };
if (Object.values(actualChains).find((value) => value === _chainId)) {
throw rpcErrors.invalidParams(`May not specify default MetaMask chain.`);
}

Expand Down
216 changes: 140 additions & 76 deletions app/core/WalletConnect/WalletConnectV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import { updateWC2Metadata } from '../../../app/actions/sdk';
import Routes from '../../../app/constants/navigation/Routes';
import ppomUtil from '../../../app/lib/ppom/ppom-util';
import { WALLET_CONNECT_ORIGIN } from '../../../app/util/walletconnect';
import { selectChainId } from '../../selectors/networkController';
import {
selectChainId,
selectNetworkConfigurations,
} from '../../selectors/networkController';
import { store } from '../../store';
import AsyncStorage from '../../store/async-storage-wrapper';
import Device from '../../util/device';
Expand All @@ -41,6 +44,7 @@ import parseWalletConnectUri, {
hideWCLoadingState,
showWCLoadingState,
} from './wc-utils';
import { getDefaultNetworkByChainId } from '../../util/networks';

const { PROJECT_ID } = AppConstants.WALLET_CONNECT;
export const isWC2Enabled =
Expand All @@ -65,6 +69,9 @@ class WalletConnect2Session {
private navigation?: NavigationContainerRef;
private web3Wallet: Client;
private deeplink: boolean;
// timeoutRef is used on android to prevent automatic redirect on switchChain and wait for wallet_addEthereumChain.
// If addEthereumChain is not received after 3 seconds, it will redirect.
private timeoutRef: NodeJS.Timeout | null = null;
private session: SessionTypes.Struct;
private requestsToRedirect: { [request: string]: boolean } = {};
private topicByRequestId: { [requestId: string]: string } = {};
Expand Down Expand Up @@ -184,11 +191,11 @@ class WalletConnect2Session {
this.deeplink = deeplink;
};

redirect = () => {
redirect = (context?: string) => {
DevLogger.log(
`WC2::redirect isDeeplink=${this.deeplink} navigation=${
this.navigation !== undefined
}`,
`WC2::redirect context=${context} isDeeplink=${
this.deeplink
} navigation=${this.navigation !== undefined}`,
);
if (!this.deeplink) return;

Expand All @@ -208,7 +215,7 @@ class WalletConnect2Session {
needsRedirect = (id: string) => {
if (this.requestsToRedirect[id]) {
delete this.requestsToRedirect[id];
this.redirect();
this.redirect(`needsRedirect_${id}`);
}
};

Expand Down Expand Up @@ -368,6 +375,10 @@ class WalletConnect2Session {
);
this.topicByRequestId[requestEvent.id] = requestEvent.topic;
this.requestByRequestId[requestEvent.id] = requestEvent;
if (this.timeoutRef) {
// Always clear the timeout ref on new message, it is only used for wallet_switchEthereumChain auto reject on android
clearTimeout(this.timeoutRef);
}

hideWCLoadingState({ navigation: this.navigation });
const verified = requestEvent.verifyContext?.verified;
Expand All @@ -390,13 +401,62 @@ class WalletConnect2Session {
const selectedChainId = parseInt(selectChainId(store.getState()));

if (selectedChainId !== chainId) {
DevLogger.log(
`rejectRequest due to invalid chainId ${chainId} (selectedChainId=${selectedChainId})`,
);
await this.web3Wallet.rejectRequest({
id: chainId,
id: requestEvent.id,
topic: this.session.topic,
error: { code: 1, message: ERROR_MESSAGES.INVALID_CHAIN },
});
}

// Android specific logic to prevent automatic redirect on switchChain and let the dapp call wallet_addEthereumChain on error.
if (
method.toLowerCase() === RPC_WALLET_SWITCHETHEREUMCHAIN.toLowerCase() &&
Device.isAndroid()
) {
// extract first chainId param from request array
const params = requestEvent.params.request.params as [
{ chainId?: string },
];
const _chainId = params[0]?.chainId;
DevLogger.log(
`formatting chainId=>${chainId} ==> 0x${chainId.toString(16)}`,
);
const networkConfigurations = selectNetworkConfigurations(
store.getState(),
);
const existingNetworkDefault = getDefaultNetworkByChainId(_chainId);
const existingEntry = Object.entries(networkConfigurations).find(
([, networkConfiguration]) => networkConfiguration.chainId === _chainId,
);
DevLogger.log(
`rpcMiddleWare -- check for auto rejection (_chainId=${_chainId}) networkConfigurations=${JSON.stringify(
networkConfigurations,
)} existingEntry=${existingEntry} existingNetworkDefault=${existingNetworkDefault}`,
);
if (!existingEntry && !existingNetworkDefault) {
DevLogger.log(
`SKIP rpcMiddleWare -- auto rejection is detected android (_chainId=${_chainId})`,
);
await this.web3Wallet.rejectRequest({
id: requestEvent.id,
topic: requestEvent.topic,
error: { code: 32603, message: ERROR_MESSAGES.INVALID_CHAIN },
});

showWCLoadingState({ navigation: this.navigation });
this.timeoutRef = setTimeout(() => {
DevLogger.log(`wc2::timeoutRef redirecting...`);
hideWCLoadingState({ navigation: this.navigation });
// Redirect or do nothing if timer gets cleared upon receiving wallet_addEthereumChain after automatic reject
this.redirect('handleRequestTimeout');
}, 3000);
return;
}
}

// Manage redirects
if (METHODS_TO_REDIRECT[method]) {
this.requestsToRedirect[requestEvent.id] = true;
Expand Down Expand Up @@ -475,7 +535,9 @@ export class WC2Manager {
this.deeplinkSessions = deeplinkSessions;
this.navigation = navigation;

const sessions = web3Wallet.getActiveSessions() || {};
const sessions = web3Wallet.getActiveSessions
? web3Wallet.getActiveSessions()
: {};

DevLogger.log(`WC2Manager::constructor()`, navigation);

Expand All @@ -484,7 +546,7 @@ export class WC2Manager {
web3Wallet.on(
'session_delete',
async (event: SingleEthereumTypes.SessionDelete) => {
const session = sessions[event.topic];
const session = sessions?.[event.topic];
if (session && deeplinkSessions[session?.pairingTopic]) {
delete deeplinkSessions[session.pairingTopic];
await AsyncStorage.setItem(
Expand Down Expand Up @@ -514,80 +576,82 @@ export class WC2Manager {
}
).PermissionController;

Object.keys(sessions).forEach(async (sessionKey) => {
try {
const session = sessions[sessionKey];

this.sessions[sessionKey] = new WalletConnect2Session({
web3Wallet,
channelId: sessionKey,
navigation: this.navigation,
deeplink:
typeof deeplinkSessions[session.pairingTopic] !== 'undefined',
session,
});

// Find approvedAccounts for current sessions
DevLogger.log(
`WC2::init getPermittedAccounts for ${sessionKey} origin=${session.peer.metadata.url}`,
JSON.stringify(permissionController.state, null, 2),
);
const accountPermission = permissionController.getPermission(
session.peer.metadata.url,
'eth_accounts',
);

DevLogger.log(
`WC2::init accountPermission`,
JSON.stringify(accountPermission, null, 2),
);
let approvedAccounts =
(await getPermittedAccounts(accountPermission?.id ?? '')) ?? [];
const fromOrigin = await getPermittedAccounts(
session.peer.metadata.url,
);
if (sessions) {
Object.keys(sessions).forEach(async (sessionKey) => {
try {
const session = sessions[sessionKey];

this.sessions[sessionKey] = new WalletConnect2Session({
web3Wallet,
channelId: sessionKey,
navigation: this.navigation,
deeplink:
typeof deeplinkSessions[session.pairingTopic] !== 'undefined',
session,
});

// Find approvedAccounts for current sessions
DevLogger.log(
`WC2::init getPermittedAccounts for ${sessionKey} origin=${session.peer.metadata.url}`,
JSON.stringify(permissionController.state, null, 2),
);
const accountPermission = permissionController.getPermission(
session.peer.metadata.url,
'eth_accounts',
);

DevLogger.log(
`WC2::init approvedAccounts id ${accountPermission?.id}`,
approvedAccounts,
);
DevLogger.log(
`WC2::init fromOrigin ${session.peer.metadata.url}`,
fromOrigin,
);
DevLogger.log(
`WC2::init accountPermission`,
JSON.stringify(accountPermission, null, 2),
);
let approvedAccounts =
(await getPermittedAccounts(accountPermission?.id ?? '')) ?? [];
const fromOrigin = await getPermittedAccounts(
session.peer.metadata.url,
);

// fallback to origin from metadata url
if (approvedAccounts.length === 0) {
DevLogger.log(
`WC2::init fallback to metadata url ${session.peer.metadata.url}`,
`WC2::init approvedAccounts id ${accountPermission?.id}`,
approvedAccounts,
);
DevLogger.log(
`WC2::init fromOrigin ${session.peer.metadata.url}`,
fromOrigin,
);
approvedAccounts =
(await getPermittedAccounts(session.peer.metadata.url)) ?? [];
}

if (approvedAccounts?.length === 0) {
// fallback to origin from metadata url
if (approvedAccounts.length === 0) {
DevLogger.log(
`WC2::init fallback to metadata url ${session.peer.metadata.url}`,
);
approvedAccounts =
(await getPermittedAccounts(session.peer.metadata.url)) ?? [];
}

if (approvedAccounts?.length === 0) {
DevLogger.log(
`WC2::init fallback to parsing accountPermission`,
accountPermission,
);
// FIXME: Why getPermitted accounts doesn't work???
approvedAccounts = extractApprovedAccounts(accountPermission);
DevLogger.log(`WC2::init approvedAccounts`, approvedAccounts);
}

const nChainId = parseInt(chainId, 16);
DevLogger.log(
`WC2::init fallback to parsing accountPermission`,
accountPermission,
`WC2::init updateSession session=${sessionKey} chainId=${chainId} nChainId=${nChainId} selectedAddress=${selectedAddress}`,
approvedAccounts,
);
// FIXME: Why getPermitted accounts doesn't work???
approvedAccounts = extractApprovedAccounts(accountPermission);
DevLogger.log(`WC2::init approvedAccounts`, approvedAccounts);
await this.sessions[sessionKey].updateSession({
chainId: nChainId,
accounts: approvedAccounts,
});
} catch (err) {
console.warn(`WC2::init can't update session ${sessionKey}`);
}

const nChainId = parseInt(chainId, 16);
DevLogger.log(
`WC2::init updateSession session=${sessionKey} chainId=${chainId} nChainId=${nChainId} selectedAddress=${selectedAddress}`,
approvedAccounts,
);
await this.sessions[sessionKey].updateSession({
chainId: nChainId,
accounts: approvedAccounts,
});
} catch (err) {
console.warn(`WC2::init can't update session ${sessionKey}`);
}
});
});
}
}

public static async init({
Expand Down Expand Up @@ -860,7 +924,7 @@ export class WC2Manager {

this.sessions[activeSession.topic] = session;
if (deeplink) {
session.redirect();
session.redirect('onSessionProposal');
}
} catch (err) {
console.error(`invalid wallet status`, err);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
"@walletconnect/core": "2.13.0",
"@walletconnect/jsonrpc-types": "^1.0.2",
"@walletconnect/react-native-compat": "2.13.0",
"@walletconnect/se-sdk": "1.8.0",
"@walletconnect/se-sdk": "1.8.1",
"@walletconnect/utils": "2.13.0",
"@xmldom/xmldom": "^0.8.10",
"appium-adb": "^9.11.4",
Expand Down
Loading

0 comments on commit a9522c9

Please sign in to comment.