Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@types/jest": "^22.2.3",
"@types/node": "^10.1.4",
"@types/sinon": "^4.3.3",
"@types/uuid": "^8.3.0",
"@types/web3": "^1.0.6",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
Expand All @@ -80,7 +81,7 @@
"sinon": "^7.4.1",
"ts-jest": "^26.3.0",
"typedoc": "^0.15.0",
"typescript": "^4.0.3"
"typescript": "^4.1.3"
},
"jest": {
"moduleFileExtensions": [
Expand Down
1 change: 1 addition & 0 deletions src/ComposableController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class ComposableController extends BaseController<any, any> {
const { name } = controller;
this.context[name] = controller;
controller.context = this.context;
controller.messagingSystem = this.messagingSystem;
this.cachedState && this.cachedState[name] && controller.update(this.cachedState[name]);
initialState[name] = controller.state;
controller.subscribe((state) => {
Expand Down
18 changes: 18 additions & 0 deletions src/MSG-SYSTEM.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { EventEmitter } from 'events';

interface AttenuatedMessageSystem {
subscriptionIds: string[];
call: () => void;
}

export function attenuatedMessageSystem(
eventEmitter: EventEmitter,
actionMap: Map<string, any>,
allowedActions: string[],
allowedSubscriptions: string[]
): AttenuatedMessageSystem {
return Object.freeze({
subscriptionIds: [],
call: () => {},
});
}
20 changes: 9 additions & 11 deletions src/assets/AccountTrackerController.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import BaseController, { BaseConfig, BaseState } from '../BaseController';
import PreferencesController from '../user/PreferencesController';
import { ControllerMessagingSystem } from '../controller-messaging-system';
import { GET_PREFERENCES_STATE, PREFERENCES_STATE_CHANGED } from '../user/PreferencesController';
import { BNToHex, safelyExecute } from '../util';

const EthjsQuery = require('ethjs-query');
Expand Down Expand Up @@ -44,12 +45,14 @@ export interface AccountTrackerState extends BaseState {
export class AccountTrackerController extends BaseController<AccountTrackerConfig, AccountTrackerState> {
private ethjsQuery: any;

private messagingSystem: ControllerMessagingSystem;

private handle?: NodeJS.Timer;

private syncAccounts() {
const {
state: { identities },
} = this.context.PreferencesController as PreferencesController;
identities,
} = this.messagingSystem.call(GET_PREFERENCES_STATE);
const { accounts } = this.state;
const addresses = Object.keys(identities);
const existing = Object.keys(accounts);
Expand All @@ -69,19 +72,15 @@ export class AccountTrackerController extends BaseController<AccountTrackerConfi
*/
name = 'AccountTrackerController';

/**
* List of required sibling controllers this controller needs to function
*/
requiredControllers = ['PreferencesController'];

/**
* Creates an AccountTracker instance
*
* @param config - Initial options used to configure this controller
* @param state - Initial state to set on this controller
*/
constructor(config?: Partial<AccountTrackerConfig>, state?: Partial<AccountTrackerState>) {
constructor(messagingSystem: ControllerMessagingSystem, config?: Partial<AccountTrackerConfig>, state?: Partial<AccountTrackerState>) {
super(config, state);
this.messagingSystem = messagingSystem;
this.defaultConfig = {
interval: 10000,
};
Expand All @@ -104,8 +103,7 @@ export class AccountTrackerController extends BaseController<AccountTrackerConfi
*/
onComposed() {
super.onComposed();
const preferences = this.context.PreferencesController as PreferencesController;
preferences.subscribe(this.refresh);
this.messagingSystem.subscribe(PREFERENCES_STATE_CHANGED, this.refresh);
this.poll();
}

Expand Down
21 changes: 16 additions & 5 deletions src/assets/AssetsController.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { EventEmitter } from 'events';
import { toChecksumAddress } from 'ethereumjs-util';
import BaseController, { BaseConfig, BaseState } from '../BaseController';
import PreferencesController from '../user/PreferencesController';
import { PreferencesState, PREFERENCES_STATE_CHANGED } from '../user/PreferencesController';
import NetworkController, { NetworkType } from '../network/NetworkController';
import { safelyExecute, handleFetch, validateTokenToWatch } from '../util';
import { ControllerMessagingSystem } from '../controller-messaging-system';
import { Token } from './TokenRatesController';
import { AssetsContractController } from './AssetsContractController';
import { ApiCollectibleResponse } from './AssetsDetectionController';
Expand Down Expand Up @@ -166,6 +167,10 @@ export interface AssetsState extends BaseState {
export class AssetsController extends BaseController<AssetsConfig, AssetsState> {
private mutex = new Mutex();

private messagingSystem: ControllerMessagingSystem;

private preferencesSubId?: string;

private getCollectibleApi(contractAddress: string, tokenId: number) {
return `https://api.opensea.io/api/v1/asset/${contractAddress}/${tokenId}`;
}
Expand Down Expand Up @@ -515,16 +520,17 @@ export class AssetsController extends BaseController<AssetsConfig, AssetsState>
/**
* List of required sibling controllers this controller needs to function
*/
requiredControllers = ['AssetsContractController', 'NetworkController', 'PreferencesController'];
requiredControllers = ['AssetsContractController', 'NetworkController'];

/**
* Creates a AssetsController instance
*
* @param config - Initial options used to configure this controller
* @param state - Initial state to set on this controller
*/
constructor(config?: Partial<BaseConfig>, state?: Partial<AssetsState>) {
constructor(messagingSystem: ControllerMessagingSystem, config?: Partial<BaseConfig>, state?: Partial<AssetsState>) {
super(config, state);
this.messagingSystem = messagingSystem;
this.defaultConfig = {
networkType: 'mainnet',
selectedAddress: '',
Expand Down Expand Up @@ -795,9 +801,8 @@ export class AssetsController extends BaseController<AssetsConfig, AssetsState>
*/
onComposed() {
super.onComposed();
const preferences = this.context.PreferencesController as PreferencesController;
const network = this.context.NetworkController as NetworkController;
preferences.subscribe(({ selectedAddress }) => {
this.preferencesSubId = this.messagingSystem.subscribe<PreferencesState>(PREFERENCES_STATE_CHANGED, ({ selectedAddress }) => {
const { allCollectibleContracts, allCollectibles, allTokens } = this.state;
const { networkType } = this.config;
this.configure({ selectedAddress });
Expand All @@ -821,6 +826,12 @@ export class AssetsController extends BaseController<AssetsConfig, AssetsState>
});
});
}

onDestroy() {
if (this.preferencesSubId) {
this.messagingSystem.unsubscribe(this.preferencesSubId);
}
}
}

export default AssetsController;
23 changes: 18 additions & 5 deletions src/assets/AssetsDetectionController.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { toChecksumAddress } from 'ethereumjs-util';
import BaseController, { BaseConfig, BaseState } from '../BaseController';
import NetworkController, { NetworkType } from '../network/NetworkController';
import PreferencesController from '../user/PreferencesController';
import { PreferencesState, PREFERENCES_STATE_CHANGED } from '../user/PreferencesController';
import { safelyExecute, timeoutFetch } from '../util';
import { ControllerMessagingSystem } from '../controller-messaging-system';
import AssetsContractController from './AssetsContractController';
import { Token } from './TokenRatesController';

Expand Down Expand Up @@ -55,6 +56,10 @@ export interface AssetsDetectionConfig extends BaseConfig {
export class AssetsDetectionController extends BaseController<AssetsDetectionConfig, BaseState> {
private handle?: NodeJS.Timer;

private messagingSystem: ControllerMessagingSystem;

private subId?: string;

private getOwnerCollectiblesApi(address: string) {
return `https://api.opensea.io/api/v1/assets?owner=${address}&limit=300`;
}
Expand Down Expand Up @@ -88,16 +93,17 @@ export class AssetsDetectionController extends BaseController<AssetsDetectionCon
/**
* List of required sibling controllers this controller needs to function
*/
requiredControllers = ['AssetsContractController', 'AssetsController', 'NetworkController', 'PreferencesController'];
requiredControllers = ['AssetsContractController', 'AssetsController', 'NetworkController'];

/**
* Creates a AssetsDetectionController instance
*
* @param config - Initial options used to configure this controller
* @param state - Initial state to set on this controller
*/
constructor(config?: Partial<AssetsDetectionConfig>, state?: Partial<BaseState>) {
constructor(messagingSystem: ControllerMessagingSystem, config?: Partial<AssetsDetectionConfig>, state?: Partial<BaseState>) {
super(config, state);
this.messagingSystem = messagingSystem;
this.defaultConfig = {
interval: DEFAULT_INTERVAL,
networkType: 'mainnet',
Expand Down Expand Up @@ -256,23 +262,30 @@ export class AssetsDetectionController extends BaseController<AssetsDetectionCon
*/
onComposed() {
super.onComposed();
const preferences = this.context.PreferencesController as PreferencesController;
const network = this.context.NetworkController as NetworkController;
const assets = this.context.AssetsController as AssetsController;
assets.subscribe(({ tokens }) => {
this.configure({ tokens });
});
preferences.subscribe(({ selectedAddress }) => {

this.subId = this.messagingSystem.subscribe<PreferencesState>(PREFERENCES_STATE_CHANGED, ({ selectedAddress }) => {
const actualSelectedAddress = this.config.selectedAddress;
if (selectedAddress !== actualSelectedAddress) {
this.configure({ selectedAddress });
this.detectAssets();
}
});

network.subscribe(({ provider }) => {
this.configure({ networkType: provider.type });
});
}

onDestroyed() {
if (this.subId) {
this.messagingSystem.unsubscribe(this.subId);
}
}
}

export default AssetsDetectionController;
29 changes: 26 additions & 3 deletions src/assets/CurrencyRateController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Mutex } from 'await-semaphore';
import BaseController from '../base-controller-v2';
import { safelyExecute } from '../util';
import { fetchExchangeRate as defaultFetchExchangeRate } from '../crypto-compare';
import { ControllerMessagingSystem } from '../controller-messaging-system';

/**
* @type CurrencyRateState
Expand Down Expand Up @@ -35,11 +36,28 @@ const schema = {
usdConversionRate: { persist: false, anonymous: true },
};

/** constants */
const GET_CURRENCY_RATE_STATE = 'CurrencyRateController.getState';
const SET_CURRENT_CURRENCY = 'CurrencyRateController.setCurrentCurrency';
const SET_NATIVE_CURRENCY = 'CurrencyRateController.setNativeCurrency';

export const CURRENCY_RATE_STATE_CHANGED = 'CurrencyRateController.state-changed';

export interface CurrencyRateActions {
[GET_CURRENCY_RATE_STATE]: () => CurrencyRateState;
[SET_CURRENT_CURRENCY]: (currentCurrency: string) => void;
[SET_NATIVE_CURRENCY]: (nativeCurrency: string) => void;
}

export interface CurrencyRateEvents {
[CURRENCY_RATE_STATE_CHANGED]: CurrencyRateState;
}

/**
* Controller that passively polls on a set interval for an exchange rate from the current base
* asset to the current currency
*/
export class CurrencyRateController extends BaseController<CurrencyRateState> {
export class CurrencyRateController extends BaseController<CurrencyRateState, CurrencyRateActions> {

private static defaultState = {
conversionDate: 0,
Expand All @@ -65,11 +83,16 @@ export class CurrencyRateController extends BaseController<CurrencyRateState> {
*
* @param state - Initial state to set on this controller
*/
constructor(state?: Partial<CurrencyRateState>, includeUsdRate = false, fetchExchangeRate = defaultFetchExchangeRate) {
super({ ...CurrencyRateController.defaultState, ...state }, schema);
constructor(messagingSystem: ControllerMessagingSystem, state?: Partial<CurrencyRateState>, includeUsdRate = false, fetchExchangeRate = defaultFetchExchangeRate) {
super(messagingSystem, { ...CurrencyRateController.defaultState, ...state }, CURRENCY_RATE_STATE_CHANGED, schema);
this.includeUsdRate = includeUsdRate;
this.fetchExchangeRate = fetchExchangeRate;
this.poll();
this.registerActions({
[GET_CURRENCY_RATE_STATE]: () => this.state,
[SET_CURRENT_CURRENCY]: this.setCurrentCurrency,
[SET_NATIVE_CURRENCY]: this.setNativeCurrency,
});
}

destroy() {
Expand Down
33 changes: 31 additions & 2 deletions src/assets/TokenRatesController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { toChecksumAddress } from 'ethereumjs-util';
import BaseController, { BaseConfig, BaseState } from '../BaseController';
import { ControllerMessagingSystem } from '../controller-messaging-system';
import { safelyExecute, handleFetch } from '../util';
import AssetsController from './AssetsController';
import { CURRENCY_RATE_STATE_CHANGED, CurrencyRateState } from './CurrencyRateController';

/**
* @type CoinGeckoResponse
Expand Down Expand Up @@ -63,6 +66,10 @@ export interface TokenRatesState extends BaseState {
export class TokenRatesController extends BaseController<TokenRatesConfig, TokenRatesState> {
private handle?: NodeJS.Timer;

private messagingSystem: ControllerMessagingSystem;

private subId?: string;

private tokenList: Token[] = [];

private getPricingURL(query: string) {
Expand All @@ -77,16 +84,17 @@ export class TokenRatesController extends BaseController<TokenRatesConfig, Token
/**
* List of required sibling controllers this controller needs to function
*/
requiredControllers = ['AssetsController', 'CurrencyRateController'];
requiredControllers = ['AssetsController'];

/**
* Creates a TokenRatesController instance
*
* @param config - Initial options used to configure this controller
* @param state - Initial state to set on this controller
*/
constructor(config?: Partial<TokenRatesConfig>, state?: Partial<TokenRatesState>) {
constructor(messagingSystem: ControllerMessagingSystem, config?: Partial<TokenRatesConfig>, state?: Partial<TokenRatesState>) {
super(config, state);
this.messagingSystem = messagingSystem;
this.defaultConfig = {
disabled: true,
interval: 180000,
Expand Down Expand Up @@ -133,6 +141,27 @@ export class TokenRatesController extends BaseController<TokenRatesConfig, Token
return handleFetch(this.getPricingURL(query));
}

/**
* Extension point called if and when this controller is composed
* with other controllers using a ComposableController
*/
onComposed() {
super.onComposed();
const assets = this.context.AssetsController as AssetsController;
assets.subscribe(() => {
this.configure({ tokens: assets.state.tokens });
});
this.subId = this.messagingSystem.subscribe<CurrencyRateState>(CURRENCY_RATE_STATE_CHANGED, (state) => {
this.configure({ nativeCurrency: state.nativeCurrency });
});
}

onDestroyed() {
if (this.subId) {
this.messagingSystem.unsubscribe(this.subId);
}
}

/**
* Updates exchange rates for all tokens
*
Expand Down
Loading