Skip to content

Commit

Permalink
wallet-ext: move network switch handling to background service (Myste…
Browse files Browse the repository at this point in the history
…nLabs#7429)

* all UI instances now use the same network (changing network in one
instance will change all others as well)
* in case of any error while switching network show an error toast
* set growth book attributes in background service as well


https://user-images.githubusercontent.com/10210143/212878899-dc883200-58c7-4d19-ab2e-c94a2bb9ad33.mov



https://user-images.githubusercontent.com/10210143/212879044-1cc91183-97ee-4ce8-b1e8-e3aac489ccfa.mov

The only visible change (except the toast) is that when user clicks on
custom rpc url now the current selected network still is selected (shows
the checkmark green next to it) until saving the new custom rpc url.

Before:



https://user-images.githubusercontent.com/10210143/212880937-1950deb7-901b-428b-972b-f90a20dabbff.mov



After:



https://user-images.githubusercontent.com/10210143/212879832-2c5a3920-91a8-4fde-8971-a5c1adca28ef.mov

part of: APPS-308
  • Loading branch information
pchrysochoidis authored Jan 30, 2023
1 parent 62839fd commit 252217e
Show file tree
Hide file tree
Showing 18 changed files with 363 additions and 221 deletions.
3 changes: 3 additions & 0 deletions apps/wallet/src/background/FeatureGating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import { GrowthBook } from '@growthbook/growthbook';

import { setAttributes } from '_src/shared/experimentation/features';

const GROWTHBOOK_API_KEY =
process.env.NODE_ENV === 'production'
? 'key_prod_ac59fe325855eb5f'
Expand All @@ -22,6 +24,7 @@ class FeatureGating {
}

private async loadFeatures() {
setAttributes(this.#growthBook);
try {
const res = await fetch(
`https://cdn.growthbook.io/api/features/${GROWTHBOOK_API_KEY}`
Expand Down
62 changes: 62 additions & 0 deletions apps/wallet/src/background/NetworkEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import mitt from 'mitt';
import Browser from 'webextension-polyfill';

import FeatureGating from './FeatureGating';
import { API_ENV, API_ENV_TO_INFO, DEFAULT_API_ENV } from '_app/ApiProvider';
import { FEATURES } from '_src/shared/experimentation/features';
import { isValidUrl } from '_src/shared/utils';

export type NetworkEnvType =
| { env: Exclude<API_ENV, API_ENV.customRPC>; customRpcUrl: null }
| { env: API_ENV.customRPC; customRpcUrl: string };

class NetworkEnv {
#events = mitt<{ changed: NetworkEnvType }>();

async getActiveNetwork(): Promise<NetworkEnvType> {
const { sui_Env, sui_Env_RPC } = await Browser.storage.local.get({
sui_Env: DEFAULT_API_ENV,
sui_Env_RPC: null,
});
const adjEnv = (await this.#isNetworkAvailable(sui_Env))
? sui_Env
: DEFAULT_API_ENV;
const adjCustomUrl = adjEnv === API_ENV.customRPC ? sui_Env_RPC : null;
return { env: adjEnv, customRpcUrl: adjCustomUrl };
}

async setActiveNetwork(network: NetworkEnvType) {
const { env, customRpcUrl } = network;
if (!(await this.#isNetworkAvailable(env))) {
throw new Error(
`Error changing network, ${API_ENV_TO_INFO[env].name} is not available.`
);
}
if (env === API_ENV.customRPC && !isValidUrl(customRpcUrl)) {
throw new Error(`Invalid custom RPC url ${customRpcUrl}`);
}
await Browser.storage.local.set({
sui_Env: env,
sui_Env_RPC: customRpcUrl,
});
this.#events.emit('changed', network);
}

on = this.#events.on;

off = this.#events.off;

async #isNetworkAvailable(apiEnv: API_ENV) {
const growthBook = await FeatureGating.getGrowthBook();
return (
(apiEnv === API_ENV.testNet &&
growthBook.isOn(FEATURES.USE_TEST_NET_ENDPOINT)) ||
apiEnv !== API_ENV.testNet
);
}
}

export default new NetworkEnv();
32 changes: 29 additions & 3 deletions apps/wallet/src/background/connections/UiConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import { BehaviorSubject, filter, switchMap, takeUntil } from 'rxjs';

import FeatureGating from '../FeatureGating';
import NetworkEnv from '../NetworkEnv';
import { Connection } from './Connection';
import { createMessage } from '_messages';
import { isBasePayload } from '_payloads';
import { type ErrorPayload, isBasePayload } from '_payloads';
import { isSetNetworkPayload, type SetNetworkPayload } from '_payloads/network';
import {
isGetPermissionRequests,
isPermissionResponse,
Expand Down Expand Up @@ -118,10 +120,34 @@ export class UiConnection extends Connection {
id
)
);
} else if (
isBasePayload(payload) &&
payload.type === 'get-network'
) {
this.send(
createMessage<SetNetworkPayload>(
{
type: 'set-network',
network: await NetworkEnv.getActiveNetwork(),
},
id
)
);
} else if (isSetNetworkPayload(payload)) {
await NetworkEnv.setActiveNetwork(payload.network);
this.send(createMessage({ type: 'done' }, id));
}
} catch (e) {
// just in case
// we could log it also
this.send(
createMessage<ErrorPayload>(
{
error: true,
code: -1,
message: (e as Error).message,
},
id
)
);
}
}

Expand Down
74 changes: 47 additions & 27 deletions apps/wallet/src/background/connections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { UiConnection } from './UiConnection';
import { createMessage } from '_messages';
import { KEEP_ALIVE_BG_PORT_NAME } from '_src/content-script/keep-bg-alive';

import type { NetworkEnvType } from '../NetworkEnv';
import type { Connection } from './Connection';
import type { SetNetworkPayload } from '_payloads/network';
import type { Permission } from '_payloads/permissions';
import type {
WalletStatusChange,
Expand Down Expand Up @@ -52,40 +54,58 @@ export class Connections {
});
}

public notifyForPermissionReply(permission: Permission) {
for (const aConnection of this.#connections) {
if (
aConnection instanceof ContentScriptConnection &&
aConnection.origin === permission.origin
) {
aConnection.permissionReply(permission);
}
}
}

public notifyForLockedStatusUpdate(isLocked: boolean) {
public notifyContentScript(
notification:
| { event: 'permissionReply'; permission: Permission }
| {
event: 'walletStatusChange';
origin: string;
change: WalletStatusChange;
}
) {
for (const aConnection of this.#connections) {
if (aConnection instanceof UiConnection) {
aConnection.sendLockedStatusUpdate(isLocked);
if (aConnection instanceof ContentScriptConnection) {
switch (notification.event) {
case 'permissionReply':
aConnection.permissionReply(notification.permission);
break;
case 'walletStatusChange':
if (aConnection.origin === notification.origin) {
aConnection.send(
createMessage<WalletStatusChangePayload>({
type: 'wallet-status-changed',
...notification.change,
})
);
}
break;
}
}
}
}

public notifyWalletStatusChange(
origin: string,
change: WalletStatusChange
public notifyUI(
notification:
| { event: 'networkChanged'; network: NetworkEnvType }
| { event: 'lockStatusUpdate'; isLocked: boolean }
) {
for (const aConnection of this.#connections) {
if (
aConnection instanceof ContentScriptConnection &&
aConnection.origin === origin
) {
aConnection.send(
createMessage<WalletStatusChangePayload>({
type: 'wallet-status-changed',
...change,
})
);
if (aConnection instanceof UiConnection) {
switch (notification.event) {
case 'networkChanged':
aConnection.send(
createMessage<SetNetworkPayload>({
type: 'set-network',
network: notification.network,
})
);
break;
case 'lockStatusUpdate':
aConnection.sendLockedStatusUpdate(
notification.isLocked
);
break;
}
}
}
}
Expand Down
34 changes: 30 additions & 4 deletions apps/wallet/src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { lte, coerce } from 'semver';
import Browser from 'webextension-polyfill';

import { LOCK_ALARM_NAME } from './Alarms';
import FeatureGating from './FeatureGating';
import NetworkEnv from './NetworkEnv';
import Permissions from './Permissions';
import { Connections } from './connections';
import Keyring from './keyring';
import { isSessionStorageSupported } from './storage-utils';
import { openInNewTab } from '_shared/utils';
import { MSG_CONNECT } from '_src/content-script/keep-bg-alive';
import { setAttributes } from '_src/shared/experimentation/features';

Browser.runtime.onInstalled.addListener(async ({ reason, previousVersion }) => {
// Skip automatically opening the onboarding in end-to-end tests.
Expand Down Expand Up @@ -40,18 +43,27 @@ const connections = new Connections();

Permissions.permissionReply.subscribe((permission) => {
if (permission) {
connections.notifyForPermissionReply(permission);
connections.notifyContentScript({
event: 'permissionReply',
permission,
});
}
});

Permissions.on('connectedAccountsChanged', ({ origin, accounts }) => {
connections.notifyWalletStatusChange(origin, { accounts });
connections.notifyContentScript({
event: 'walletStatusChange',
origin,
change: { accounts },
});
});

const keyringStatusCallback = () => {
connections.notifyForLockedStatusUpdate(Keyring.isLocked);
connections.notifyUI({
event: 'lockStatusUpdate',
isLocked: Keyring.isLocked,
});
};

Keyring.on('lockedStatusUpdate', keyringStatusCallback);
Keyring.on('accountsChanged', keyringStatusCallback);
Keyring.on('activeAccountChanged', keyringStatusCallback);
Expand All @@ -78,3 +90,17 @@ if (!isSessionStorageSupported()) {
}
});
}
NetworkEnv.getActiveNetwork().then(async ({ env, customRpcUrl }) => {
setAttributes(await FeatureGating.getGrowthBook(), {
apiEnv: env,
customRPC: customRpcUrl,
});
});

NetworkEnv.on('changed', async (network) => {
setAttributes(await FeatureGating.getGrowthBook(), {
apiEnv: network.env,
customRPC: network.customRpcUrl,
});
connections.notifyUI({ event: 'networkChanged', network });
});
22 changes: 22 additions & 0 deletions apps/wallet/src/shared/experimentation/features.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import Browser from 'webextension-polyfill';

import { API_ENV } from '_src/ui/app/ApiProvider';

import type { GrowthBook } from '@growthbook/growthbook';

/**
* This is a list of feature keys that are used in wallet
* https://docs.growthbook.io/app/features#feature-keys
Expand All @@ -11,3 +17,19 @@ export enum FEATURES {
STAKING_ENABLED = 'wallet-staking-enabled',
WALLET_DAPPS = 'wallet-dapps',
}

export function setAttributes(
growthBook: GrowthBook,
network?: { apiEnv: API_ENV; customRPC?: string | null }
) {
const activeNetwork = network
? network.apiEnv === API_ENV.customRPC && network.customRPC
? network.customRPC
: network.apiEnv.toUpperCase()
: null;
growthBook.setAttributes({
network: activeNetwork,
version: Browser.runtime.getManifest().version,
beta: process.env.WALLET_BETA || false,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export type PayloadType =
| 'stake-request'
| 'wallet-status-changed'
| 'get-features'
| 'features-response';
| 'features-response'
| 'get-network'
| 'set-network';

export interface BasePayload {
type: PayloadType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { isBasePayload } from '_payloads';

import type { BasePayload, Payload } from '_payloads';
import type { NetworkEnvType } from '_src/background/NetworkEnv';

export interface SetNetworkPayload extends BasePayload {
type: 'set-network';
network: NetworkEnvType;
}

export function isSetNetworkPayload(
payload: Payload
): payload is SetNetworkPayload {
return isBasePayload(payload) && payload.type === 'set-network';
}
Loading

0 comments on commit 252217e

Please sign in to comment.