Skip to content

Commit

Permalink
wallet-ext: set up experimentation framework (MystenLabs#4988)
Browse files Browse the repository at this point in the history
  • Loading branch information
666lcz authored Oct 6, 2022
1 parent 130f4f3 commit c895bc2
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 14 deletions.
1 change: 1 addition & 0 deletions apps/wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
},
"dependencies": {
"@floating-ui/react-dom-interactions": "^0.10.1",
"@growthbook/growthbook": "^0.18.1",
"@metamask/browser-passworder": "^3.0.0",
"@mysten/sui.js": "workspace:*",
"@reduxjs/toolkit": "^1.8.3",
Expand Down
29 changes: 17 additions & 12 deletions apps/wallet/src/ui/app/ApiProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { RawSigner, JsonRpcProvider } from '@mysten/sui.js';

import type FeatureGating from './experimentation/feature-gating';
import type { Ed25519Keypair } from '@mysten/sui.js';

export enum API_ENV {
Expand Down Expand Up @@ -62,36 +63,40 @@ function getDefaultAPI(env: API_ENV) {
}

export const DEFAULT_API_ENV = getDefaultApiEnv();
export const DEFAULT_API_ENDPOINT = getDefaultAPI(DEFAULT_API_ENV);

export default class ApiProvider {
private _apiProvider: JsonRpcProvider;
private _apiFullNodeProvider: JsonRpcProvider;
private _apiProvider?: JsonRpcProvider;
private _apiFullNodeProvider?: JsonRpcProvider;
private _signer: RawSigner | null = null;

constructor() {
this._apiProvider = new JsonRpcProvider(DEFAULT_API_ENDPOINT.gateway);
this._apiFullNodeProvider = new JsonRpcProvider(
DEFAULT_API_ENDPOINT.fullNode
);
}
constructor(private _featureGating: FeatureGating) {}

public setNewJsonRpcProvider(apiEnv: API_ENV) {
public setNewJsonRpcProvider(apiEnv: API_ENV = DEFAULT_API_ENV) {
this._apiProvider = new JsonRpcProvider(getDefaultAPI(apiEnv).gateway);
// TODO: based on _featureGating.isOn('deprecate-gateway') value,
// construct the transaction using local serializer or remote serializer
this._apiFullNodeProvider = new JsonRpcProvider(
getDefaultAPI(apiEnv).fullNode
);
this._signer = null;
}

public get instance() {
if (!this._apiFullNodeProvider) {
this.setNewJsonRpcProvider();
}
return {
gateway: this._apiProvider,
fullNode: this._apiFullNodeProvider,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
gateway: this._apiProvider!,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
fullNode: this._apiFullNodeProvider!,
};
}

public getSignerInstance(keypair: Ed25519Keypair): RawSigner {
if (!this._apiFullNodeProvider) {
this.setNewJsonRpcProvider();
}
if (!this._signer) {
this._signer = new RawSigner(keypair, this._apiProvider);
}
Expand Down
42 changes: 42 additions & 0 deletions apps/wallet/src/ui/app/experimentation/feature-gating.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

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

const GROWTHBOOK_API_KEY =
process.env.GROWTH_BOOK_API_KEY ?? 'key_dev_dc2872e15e0c5f95';

export default class FeatureGating {
#growthBook: GrowthBook;

constructor() {
// Create a GrowthBook context
this.#growthBook = new GrowthBook();
}

public async init() {
// Load feature definitions
await fetch(
`https://cdn.growthbook.io/api/features/${GROWTHBOOK_API_KEY}`
)
.then((res) => {
if (res.ok) {
return res.json();
}
throw new Error(res.statusText);
})
.then((parsed) => {
this.#growthBook.setFeatures(parsed.features);
})
.catch(() => {
// eslint-disable-next-line no-console
console.warn(
`Failed to fetch feature definitions from GrowthBook with API_KEY ${process.env.GROWTH_BOOK_API_KEY}`
);
});
}

public isOn(featureName: string): boolean {
return this.#growthBook.isOn(featureName);
}
}
6 changes: 5 additions & 1 deletion apps/wallet/src/ui/app/redux/store/thunk-extras.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
import ApiProvider from '_app/ApiProvider';
import KeypairVault from '_app/KeypairVault';
import { BackgroundClient } from '_app/background-client';
import FeatureGating from '_app/experimentation/feature-gating';

import type { RootState } from '_redux/RootReducer';
import type { AppDispatch } from '_store';

const featureGating = new FeatureGating();

export const thunkExtras = {
keypairVault: new KeypairVault(),
api: new ApiProvider(),
api: new ApiProvider(featureGating),
background: new BackgroundClient(),
featureGating: featureGating,
};

type ThunkExtras = typeof thunkExtras;
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ async function init() {
if (process.env.NODE_ENV === 'development') {
Object.defineProperty(window, 'store', { value: store });
}
await thunkExtras.featureGating.init();
store.dispatch(initAppType(getFromLocationSearch(window.location.search)));
await store.dispatch(initNetworkFromStorage()).unwrap();
await thunkExtras.background.init(store.dispatch);
Expand Down
9 changes: 8 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c895bc2

Please sign in to comment.