Socket
Socket
Sign inDemoInstall

@metamask/ppom-validator

Package Overview
Dependencies
Maintainers
9
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@metamask/ppom-validator - npm Package Compare versions

Comparing version 0.0.1 to 0.1.1

10

CHANGELOG.md

@@ -9,2 +9,9 @@ # Changelog

## [0.1.1]
### Added
- Improvements in CDN data fetching ([#15](https://github.com/MetaMask/ppom-validator/pull/15))
- Mobile integration ([#14](https://github.com/MetaMask/ppom-validator/pull/14))
- Caching data for multiple networks ([#10](https://github.com/MetaMask/ppom-validator/pull/10))
- Adding periodic sync for ppom data ([#6](https://github.com/MetaMask/ppom-validator/pull/6))
## [0.0.1]

@@ -22,3 +29,4 @@ ### Added

[Unreleased]: https://github.com/MetaMask/ppom-validator/compare/v0.0.1...HEAD
[Unreleased]: https://github.com/MetaMask/ppom-validator/compare/v0.1.1...HEAD
[0.1.1]: https://github.com/MetaMask/ppom-validator/compare/v0.0.1...v0.1.1
[0.0.1]: https://github.com/MetaMask/ppom-validator/releases/tag/v0.0.1

1

dist/index.d.ts
export * from './ppom-controller';
export type { StorageBackend, StorageKey } from './ppom-storage';
export * from './ppom-middleware';

@@ -18,3 +18,2 @@ "use strict";

__exportStar(require("./ppom-controller"), exports);
__exportStar(require("./ppom-middleware"), exports);
//# sourceMappingURL=index.js.map

@@ -1,5 +0,5 @@

import * as PPOMModule from '@blockaid/ppom-mock';
import { BaseControllerV2, RestrictedControllerMessenger } from '@metamask/base-controller';
import { StorageBackend, FileMetadataList, FileMetadata } from './ppom-storage';
export declare const DAY_IN_MILLISECONDS: number;
export declare const REFRESH_TIME_INTERVAL: number;
export declare const NETWORK_CACHE_DURATION: number;
/**

@@ -18,8 +18,7 @@ * @type PPOMFileVersion

/**
* @type PPOMControllerState
* @type PPOMState
*
* Controller state
* @property lastFetched - Time when files were last updated.
* @property lastChainId - ChainId for which files were last updated.
* @property newChainId - ChainIf of currently selected network.
* @property chainId - ID of current chain.
* @property chainStatus - Array of chainId and time it was last visited.
* @property versionInfo - Version information fetched from CDN.

@@ -30,25 +29,23 @@ * @property storageMetadata - Metadata of files storaged in storage.

*/
export declare type PPOMControllerState = {
lastFetched: number;
lastChainId: string;
newChainId: string;
export declare type PPOMState = {
chainId: string;
chainStatus: Record<string, {
chainId: string;
lastVisited: number;
dataFetched: boolean;
}>;
versionInfo: PPOMVersionResponse;
storageMetadata: FileMetadataList;
refreshInterval: number;
fileScheduleInterval: number;
providerRequestLimit: number;
providerRequests: number[];
securityAlertsEnabled: boolean;
versionFileETag?: string;
};
declare const controllerName = "PPOMController";
export declare type Clear = {
type: `${typeof controllerName}:clear`;
handler: () => void;
};
export declare type UsePPOM = {
type: `${typeof controllerName}:usePPOM`;
handler: (callback: (ppom: PPOMModule.PPOM) => Promise<any>) => Promise<any>;
handler: (callback: (ppom: any) => Promise<any>) => Promise<any>;
};
export declare type SetRefreshInterval = {
type: `${typeof controllerName}:setRefreshInterval`;
handler: (interval: number) => void;
};
export declare type UpdatePPOM = {

@@ -58,4 +55,8 @@ type: `${typeof controllerName}:updatePPOM`;

};
export declare type PPOMControllerActions = Clear | UsePPOM | SetRefreshInterval | UpdatePPOM;
export declare type PPOMControllerActions = UsePPOM | UpdatePPOM;
export declare type PPOMControllerMessenger = RestrictedControllerMessenger<typeof controllerName, PPOMControllerActions, never, never, never>;
declare type PPOMProvider = {
ppomInit: () => Promise<void>;
PPOM: any;
};
/**

@@ -71,3 +72,3 @@ * PPOMController

*/
export declare class PPOMController extends BaseControllerV2<typeof controllerName, PPOMControllerState, PPOMControllerMessenger> {
export declare class PPOMController extends BaseControllerV2<typeof controllerName, PPOMState, PPOMControllerMessenger> {
#private;

@@ -78,36 +79,37 @@ /**

* @param options - Constructor options.
* @param options.chainId - Id of current chain.
* @param options.chainId - ChainId of the selected network.
* @param options.messenger - Controller messenger.
* @param options.onNetworkChange - Callback tobe invoked when network changes.
* @param options.provider - The provider used to create the PPOM instance.
* @param options.state - The controller state.
* @param options.storageBackend - The storage backend to use for storing PPOM data.
* @param options.securityAlertsEnabled - True if user has enabled preference for blockaid security check.
* @param options.onPreferencesChange - Callback invoked when user changes preferences.
* @param options.ppomProvider - Object wrapping PPOM.
* @param options.cdnBaseUrl - Base URL for the CDN.
* @param options.state - Initial state of the controller.
* @returns The PPOMController instance.
*/
constructor({ chainId, messenger, onNetworkChange, provider, state, storageBackend, }: {
constructor({ chainId, messenger, onNetworkChange, provider, storageBackend, securityAlertsEnabled, onPreferencesChange, ppomProvider, cdnBaseUrl, state, }: {
chainId: string;
onNetworkChange: (callback: (networkState: any) => void) => void;
messenger: PPOMControllerMessenger;
onNetworkChange: (callback: (chainId: string) => void) => void;
provider: any;
state?: PPOMControllerState;
storageBackend: StorageBackend;
securityAlertsEnabled: boolean;
onPreferencesChange: (callback: (perferenceState: any) => void) => void;
ppomProvider: PPOMProvider;
cdnBaseUrl: string;
state?: PPOMState;
});
/**
* Clear the controller state.
*/
clear(): void;
/**
* Set the interval at which the ppom version info will be fetched.
* Fetching will only occur on the next call to test/bypass.
* For immediate update to the ppom lists, call updatePPOM directly.
* Update the PPOM.
* This function will acquire mutex lock and invoke internal method #updatePPOM.
*
* @param interval - The new interval in ms.
* @param options - Options.
* @param options.updateForAllChains - True is update if required to be done for all chains in cache.
*/
setRefreshInterval(interval: number): void;
updatePPOM({ updateForAllChains }?: {
updateForAllChains: boolean;
}): Promise<void>;
/**
* Update the PPOM configuration.
* This function will fetch the latest version info when needed, and update the PPOM storage.
*/
updatePPOM(): Promise<void>;
/**
* Use the PPOM.

@@ -119,4 +121,4 @@ * This function receives a callback that will be called with the PPOM.

*/
usePPOM<T>(callback: (ppom: PPOMModule.PPOM) => Promise<T>): Promise<T>;
usePPOM<T>(callback: (ppom: any) => Promise<T>): Promise<T>;
}
export {};
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {

@@ -36,6 +13,5 @@ if (kind === "m") throw new TypeError("Private method is not writable");

};
var _PPOMController_instances, _PPOMController_ppom, _PPOMController_provider, _PPOMController_storage, _PPOMController_ppomMutex, _PPOMController_defaultState, _PPOMController_registerMessageHandlers, _PPOMController_shouldUpdate, _PPOMController_isOutOfDate, _PPOMController_getNewFiles, _PPOMController_updateVersionInfo, _PPOMController_maybeUpdatePPOM, _PPOMController_fetchBlob, _PPOMController_fetchVersionInfo, _PPOMController_jsonRpcRequest, _PPOMController_getPPOM;
var _PPOMController_instances, _PPOMController_ppom, _PPOMController_provider, _PPOMController_storage, _PPOMController_refreshDataInterval, _PPOMController_fileScheduleInterval, _PPOMController_ppomMutex, _PPOMController_ppomProvider, _PPOMController_cdnBaseUrl, _PPOMController_registerMessageHandlers, _PPOMController_maybeUpdatePPOM, _PPOMController_shouldUpdate, _PPOMController_updatePPOM, _PPOMController_updateVersionInfo, _PPOMController_checkFilePresentInStorage, _PPOMController_getFile, _PPOMController_setChainIdDataFetched, _PPOMController_getNewFilesForCurrentChain, _PPOMController_getListOfFilesToBeFetched, _PPOMController_deleteOldChainIds, _PPOMController_getNewFilesForAllChains, _PPOMController_getAPIResponse, _PPOMController_fetchVersionInfo, _PPOMController_fetchBlob, _PPOMController_jsonRpcRequest, _PPOMController_getPPOM, _PPOMController_onFileScheduledInterval, _PPOMController_scheduleFileDownloadForAllChains;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PPOMController = exports.DAY_IN_MILLISECONDS = void 0;
const PPOMModule = __importStar(require("@blockaid/ppom-mock"));
exports.PPOMController = exports.NETWORK_CACHE_DURATION = exports.REFRESH_TIME_INTERVAL = void 0;
const base_controller_1 = require("@metamask/base-controller");

@@ -45,5 +21,6 @@ const controller_utils_1 = require("@metamask/controller-utils");

const ppom_storage_1 = require("./ppom-storage");
exports.DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24;
exports.REFRESH_TIME_INTERVAL = 1000 * 60 * 60 * 2;
const PROVIDER_REQUEST_LIMIT = 500;
const MILLISECONDS_IN_FIVE_MINUTES = 300000;
const FILE_FETCH_SCHEDULE_INTERVAL = 1000 * 60 * 5;
exports.NETWORK_CACHE_DURATION = 1000 * 60 * 60 * 24 * 7;
// The following methods on provider are allowed to PPOM

@@ -69,16 +46,22 @@ const ALLOWED_PROVIDER_CALLS = [

const stateMetaData = {
lastFetched: { persist: false, anonymous: false },
lastChainId: { persist: false, anonymous: false },
newChainId: { persist: false, anonymous: false },
versionInfo: { persist: false, anonymous: false },
chainId: { persist: false, anonymous: false },
chainStatus: { persist: false, anonymous: false },
storageMetadata: { persist: false, anonymous: false },
refreshInterval: { persist: false, anonymous: false },
fileScheduleInterval: { persist: false, anonymous: false },
providerRequestLimit: { persist: false, anonymous: false },
providerRequests: { persist: false, anonymous: false },
securityAlertsEnabled: { persist: false, anonymous: false },
versionFileETag: { persist: false, anonymous: false },
};
// TODO: replace with metamask cdn
const PPOM_CDN_BASE_URL = 'https://storage.googleapis.com/ppom-cdn/';
const PPOM_VERSION = 'ppom_version.json';
const PPOM_VERSION_PATH = `${PPOM_CDN_BASE_URL}${PPOM_VERSION}`;
const PPOM_VERSION_FILE_NAME = 'ppom_version.json';
const URL_PREFIX = 'https://';
const controllerName = 'PPOMController';
const versionInfoFileHeaders = {
headers: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
},
};
/**

@@ -99,21 +82,31 @@ * PPOMController

* @param options - Constructor options.
* @param options.chainId - Id of current chain.
* @param options.chainId - ChainId of the selected network.
* @param options.messenger - Controller messenger.
* @param options.onNetworkChange - Callback tobe invoked when network changes.
* @param options.provider - The provider used to create the PPOM instance.
* @param options.state - The controller state.
* @param options.storageBackend - The storage backend to use for storing PPOM data.
* @param options.securityAlertsEnabled - True if user has enabled preference for blockaid security check.
* @param options.onPreferencesChange - Callback invoked when user changes preferences.
* @param options.ppomProvider - Object wrapping PPOM.
* @param options.cdnBaseUrl - Base URL for the CDN.
* @param options.state - Initial state of the controller.
* @returns The PPOMController instance.
*/
constructor({ chainId, messenger, onNetworkChange, provider, state, storageBackend, }) {
const defaultState = {
lastFetched: 0,
versionInfo: [],
storageMetadata: [],
lastChainId: '',
newChainId: chainId,
refreshInterval: exports.DAY_IN_MILLISECONDS,
providerRequestLimit: PROVIDER_REQUEST_LIMIT,
providerRequests: [],
...state,
constructor({ chainId, messenger, onNetworkChange, provider, storageBackend, securityAlertsEnabled, onPreferencesChange, ppomProvider, cdnBaseUrl, state, }) {
const initialState = {
versionInfo: state?.versionInfo ?? [],
storageMetadata: state?.storageMetadata ?? [],
chainId,
chainStatus: state?.chainStatus ?? {
[chainId]: {
chainId,
lastVisited: new Date().getTime(),
dataFetched: false,
},
},
refreshInterval: state?.refreshInterval ?? exports.REFRESH_TIME_INTERVAL,
fileScheduleInterval: state?.fileScheduleInterval ?? FILE_FETCH_SCHEDULE_INTERVAL,
providerRequestLimit: state?.providerRequestLimit ?? PROVIDER_REQUEST_LIMIT,
providerRequests: state?.providerRequests ?? [],
securityAlertsEnabled,
};

@@ -124,3 +117,3 @@ super({

messenger,
state: { ...defaultState, ...state },
state: initialState,
});

@@ -131,2 +124,4 @@ _PPOMController_instances.add(this);

_PPOMController_storage.set(this, void 0);
_PPOMController_refreshDataInterval.set(this, void 0);
_PPOMController_fileScheduleInterval.set(this, void 0);
/*

@@ -137,5 +132,6 @@ * This mutex is used to prevent concurrent usage of the PPOM instance

_PPOMController_ppomMutex.set(this, void 0);
_PPOMController_defaultState.set(this, void 0);
__classPrivateFieldSet(this, _PPOMController_defaultState, defaultState, "f");
_PPOMController_ppomProvider.set(this, void 0);
_PPOMController_cdnBaseUrl.set(this, void 0);
__classPrivateFieldSet(this, _PPOMController_provider, provider, "f");
__classPrivateFieldSet(this, _PPOMController_ppomProvider, ppomProvider, "f");
__classPrivateFieldSet(this, _PPOMController_storage, new ppom_storage_1.PPOMStorage({

@@ -153,47 +149,58 @@ storageBackend,

__classPrivateFieldSet(this, _PPOMController_ppomMutex, new await_semaphore_1.Mutex(), "f");
onNetworkChange((id) => {
__classPrivateFieldSet(this, _PPOMController_cdnBaseUrl, cdnBaseUrl, "f");
onNetworkChange((networkControllerState) => {
const id = networkControllerState.providerConfig.chainId;
if (id === this.state.chainId) {
return;
}
let { chainStatus } = this.state;
const existingNetworkObject = chainStatus[id];
chainStatus = {
...chainStatus,
[id]: {
chainId: id,
lastVisited: new Date().getTime(),
dataFetched: existingNetworkObject?.dataFetched ?? false,
},
};
this.update((draftState) => {
draftState.newChainId = id;
draftState.chainId = id;
draftState.chainStatus = chainStatus;
});
});
onPreferencesChange((preferenceControllerState) => {
const blockaidEnabled = preferenceControllerState.securityAlertsEnabled;
if (blockaidEnabled === this.state.securityAlertsEnabled) {
return;
}
if (blockaidEnabled) {
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_scheduleFileDownloadForAllChains).call(this);
}
else {
clearInterval(__classPrivateFieldGet(this, _PPOMController_refreshDataInterval, "f"));
clearInterval(__classPrivateFieldGet(this, _PPOMController_fileScheduleInterval, "f"));
}
this.update((draftState) => {
draftState.securityAlertsEnabled = blockaidEnabled;
});
});
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_registerMessageHandlers).call(this);
if (securityAlertsEnabled) {
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_scheduleFileDownloadForAllChains).call(this);
}
}
/**
* Clear the controller state.
*/
clear() {
this.update(() => __classPrivateFieldGet(this, _PPOMController_defaultState, "f"));
}
/**
* Set the interval at which the ppom version info will be fetched.
* Fetching will only occur on the next call to test/bypass.
* For immediate update to the ppom lists, call updatePPOM directly.
* Update the PPOM.
* This function will acquire mutex lock and invoke internal method #updatePPOM.
*
* @param interval - The new interval in ms.
* @param options - Options.
* @param options.updateForAllChains - True is update if required to be done for all chains in cache.
*/
setRefreshInterval(interval) {
this.update((draftState) => {
draftState.refreshInterval = interval;
});
}
/**
* Update the PPOM configuration.
* This function will fetch the latest version info when needed, and update the PPOM storage.
*/
async updatePPOM() {
if (__classPrivateFieldGet(this, _PPOMController_ppom, "f")) {
__classPrivateFieldGet(this, _PPOMController_ppom, "f").free();
__classPrivateFieldSet(this, _PPOMController_ppom, undefined, "f");
async updatePPOM({ updateForAllChains } = { updateForAllChains: true }) {
if (!this.state.securityAlertsEnabled) {
throw Error('User has not enabled blockaidSecurityCheck');
}
if (__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_isOutOfDate).call(this)) {
await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_updateVersionInfo).call(this);
}
this.update((draftState) => {
draftState.lastChainId = this.state.newChainId;
await __classPrivateFieldGet(this, _PPOMController_ppomMutex, "f").use(async () => {
await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_updatePPOM).call(this, updateForAllChains);
});
const storageMetadata = await __classPrivateFieldGet(this, _PPOMController_storage, "f").syncMetadata(this.state.versionInfo);
const newFiles = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getNewFiles).call(this, this.state.newChainId, storageMetadata);
for (const file of newFiles) {
await __classPrivateFieldGet(this, _PPOMController_storage, "f").writeFile(file);
}
}

@@ -208,2 +215,5 @@ /**

async usePPOM(callback) {
if (!this.state.securityAlertsEnabled) {
throw Error('User has not enabled blockaidSecurityCheck');
}
return await __classPrivateFieldGet(this, _PPOMController_ppomMutex, "f").use(async () => {

@@ -219,14 +229,24 @@ await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_maybeUpdatePPOM).call(this);

exports.PPOMController = PPOMController;
_PPOMController_ppom = new WeakMap(), _PPOMController_provider = new WeakMap(), _PPOMController_storage = new WeakMap(), _PPOMController_ppomMutex = new WeakMap(), _PPOMController_defaultState = new WeakMap(), _PPOMController_instances = new WeakSet(), _PPOMController_registerMessageHandlers = function _PPOMController_registerMessageHandlers() {
this.messagingSystem.registerActionHandler(`${controllerName}:clear`, this.clear.bind(this));
_PPOMController_ppom = new WeakMap(), _PPOMController_provider = new WeakMap(), _PPOMController_storage = new WeakMap(), _PPOMController_refreshDataInterval = new WeakMap(), _PPOMController_fileScheduleInterval = new WeakMap(), _PPOMController_ppomMutex = new WeakMap(), _PPOMController_ppomProvider = new WeakMap(), _PPOMController_cdnBaseUrl = new WeakMap(), _PPOMController_instances = new WeakSet(), _PPOMController_registerMessageHandlers = function _PPOMController_registerMessageHandlers() {
this.messagingSystem.registerActionHandler(`${controllerName}:usePPOM`, this.usePPOM.bind(this));
this.messagingSystem.registerActionHandler(`${controllerName}:setRefreshInterval`, this.setRefreshInterval.bind(this));
this.messagingSystem.registerActionHandler(`${controllerName}:updatePPOM`, this.updatePPOM.bind(this));
}, _PPOMController_maybeUpdatePPOM =
/**
* Conditionally update the ppom configuration.
*
* If the ppom configuration is out of date, this function will call `updatePPOM`
* to update the configuration.
*/
async function _PPOMController_maybeUpdatePPOM() {
if (__classPrivateFieldGet(this, _PPOMController_ppom, "f")) {
__classPrivateFieldGet(this, _PPOMController_ppom, "f").free();
__classPrivateFieldSet(this, _PPOMController_ppom, undefined, "f");
}
if (await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_shouldUpdate).call(this)) {
await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_updatePPOM).call(this, false);
}
}, _PPOMController_shouldUpdate =
/**
* Determine if an update to the ppom configuration is needed.
* The function will return true if
* - the chainId has changed
* - the ppom is out of date
* - the ppom is not initialized.
* The function will return true if data is not already fetched for the chain.
*

@@ -236,75 +256,189 @@ * @returns True if PPOM data requires update.

async function _PPOMController_shouldUpdate() {
if (this.state.newChainId !== this.state.lastChainId ||
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_isOutOfDate).call(this)) {
const { chainId, chainStatus } = this.state;
return !chainStatus[chainId]?.dataFetched;
}, _PPOMController_updatePPOM =
/**
* Update the PPOM configuration.
* This function will fetch the latest version info when needed, and update the PPOM storage.
*
* @param updateForAllChains - True if update is required to be done for all chains in chainStatus.
*/
async function _PPOMController_updatePPOM(updateForAllChains) {
const versionInfoUpdated = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_updateVersionInfo).call(this, updateForAllChains);
if (!versionInfoUpdated) {
return;
}
await __classPrivateFieldGet(this, _PPOMController_storage, "f").syncMetadata(this.state.versionInfo);
if (updateForAllChains) {
await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getNewFilesForAllChains).call(this);
}
else {
await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getNewFilesForCurrentChain).call(this);
}
}, _PPOMController_updateVersionInfo =
/*
* Fetch the version info from the CDN and update the version info in state.
*/
async function _PPOMController_updateVersionInfo(updateForAllChains) {
const versionInfo = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_fetchVersionInfo).call(this, updateForAllChains);
if (versionInfo) {
this.update((draftState) => {
draftState.versionInfo = versionInfo;
});
return true;
}
return __classPrivateFieldGet(this, _PPOMController_ppom, "f") === undefined;
}, _PPOMController_isOutOfDate = function _PPOMController_isOutOfDate() {
return Date.now() - this.state.lastFetched >= this.state.refreshInterval;
}, _PPOMController_getNewFiles =
return false;
}, _PPOMController_checkFilePresentInStorage = function _PPOMController_checkFilePresentInStorage(storageMetadata, fileVersionInfo) {
return storageMetadata.find((file) => file.name === fileVersionInfo.name &&
file.chainId === fileVersionInfo.chainId &&
file.version === fileVersionInfo.version &&
file.checksum === fileVersionInfo.checksum);
}, _PPOMController_getFile =
/**
* Returns an array of new files that should be downloaded and saved to storage.
* Gets a single file from CDN and write to the storage.
*
* @param chainId - The chain ID to check for files.
* @param storageMetadata - An array of file metadata objects already in storage.
* @returns A promise that resolves to an array of new files to download and save to storage.
* @param fileVersionInfo - Information about the file to be retrieved.
*/
async function _PPOMController_getNewFiles(chainId, storageMetadata) {
const newFiles = [];
for (const fileVersionInfo of this.state.versionInfo) {
// download all files for the current chain + generally required files.
if (fileVersionInfo.chainId && fileVersionInfo.chainId !== chainId) {
async function _PPOMController_getFile(fileVersionInfo) {
const { storageMetadata } = this.state;
if (__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_checkFilePresentInStorage).call(this, storageMetadata, fileVersionInfo)) {
return;
}
const fileUrl = `${URL_PREFIX}${__classPrivateFieldGet(this, _PPOMController_cdnBaseUrl, "f")}/${fileVersionInfo.filePath}`;
const fileData = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_fetchBlob).call(this, fileUrl);
await __classPrivateFieldGet(this, _PPOMController_storage, "f").writeFile({
data: fileData,
...fileVersionInfo,
});
}, _PPOMController_setChainIdDataFetched = function _PPOMController_setChainIdDataFetched(chainId) {
const { chainStatus } = this.state;
const chainIdObject = chainStatus[chainId];
if (chainIdObject && !chainIdObject.dataFetched) {
this.update((draftState) => {
draftState.chainStatus = {
...chainStatus,
[chainId]: { ...chainIdObject, dataFetched: true },
};
});
}
}, _PPOMController_getNewFilesForCurrentChain =
/**
* Fetches new files and save them to storage.
* The function is invoked if user if attempting transaction for a network,
* for which data is not previously fetched.
*
* @returns A promise that resolves to return void.
*/
async function _PPOMController_getNewFilesForCurrentChain() {
const { chainId, versionInfo } = this.state;
for (const fileVersionInfo of versionInfo) {
// download all files for the current chain.
if (fileVersionInfo.chainId !== chainId) {
continue;
}
// check if file is already in storage
if (storageMetadata.find((file) => file.name === fileVersionInfo.name &&
file.chainId === fileVersionInfo.chainId &&
file.version === fileVersionInfo.version &&
file.checksum === fileVersionInfo.checksum)) {
continue;
}
const fileUrl = `${PPOM_CDN_BASE_URL}${fileVersionInfo.filePath}`;
const fileData = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_fetchBlob).call(this, fileUrl);
newFiles.push({
data: fileData,
...fileVersionInfo,
await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getFile).call(this, fileVersionInfo).catch((exp) => {
console.error(`Error in getting file ${fileVersionInfo.filePath}: ${exp.message}`);
throw exp;
});
}
return newFiles;
}, _PPOMController_updateVersionInfo =
/*
* Fetch the version info from the PPOM cdn.
* update the version info in state.
*/
async function _PPOMController_updateVersionInfo() {
const versionInfo = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_fetchVersionInfo).call(this, PPOM_VERSION_PATH);
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_setChainIdDataFetched).call(this, chainId);
}, _PPOMController_getListOfFilesToBeFetched = function _PPOMController_getListOfFilesToBeFetched() {
const { chainStatus, storageMetadata, versionInfo: stateVersionInfo, } = this.state;
// create a map of chainId and files belonging to that chainId
const chainIdsFileInfoList = Object.keys(chainStatus).map((chainId) => ({
chainId,
versionInfo: stateVersionInfo.filter((versionInfo) => versionInfo.chainId === chainId &&
!__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_checkFilePresentInStorage).call(this, storageMetadata, versionInfo)),
}));
// build a list of files to be fetched for all networks
const fileToBeFetchedList = [];
chainIdsFileInfoList.forEach((chainIdFileInfo) => {
const { chainId, versionInfo } = chainIdFileInfo;
versionInfo.forEach((fileVersionInfo, index) => {
fileToBeFetchedList.push({
fileVersionInfo,
isLastFileOfNetwork: index === versionInfo.length - 1,
});
});
if (versionInfo.length === 0) {
// set dataFetched to true for chainId
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_setChainIdDataFetched).call(this, chainId);
}
});
return fileToBeFetchedList;
}, _PPOMController_deleteOldChainIds = function _PPOMController_deleteOldChainIds() {
const currentTimestamp = new Date().getTime();
const oldChaninIds = Object.keys(this.state.chainStatus).filter((chainId) => this.state.chainStatus[chainId].lastVisited <
currentTimestamp - exports.NETWORK_CACHE_DURATION &&
chainId !== this.state.chainId);
const chainStatus = { ...this.state.chainStatus };
oldChaninIds.forEach((chainId) => {
delete chainStatus[chainId];
});
this.update((draftState) => {
draftState.versionInfo = versionInfo;
draftState.lastFetched = Date.now();
draftState.chainStatus = chainStatus;
});
}, _PPOMController_maybeUpdatePPOM =
}, _PPOMController_getNewFilesForAllChains =
/**
* Conditionally update the ppom configuration.
* Function that fetched and saves to storage files for all networks.
* Files are not fetched parallely but at an interval.
*
* If the ppom configuration is out of date, this function will call `updatePPOM`
* to update the configuration.
* @returns A promise that resolves to return void.
*/
async function _PPOMController_maybeUpdatePPOM() {
if (await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_shouldUpdate).call(this)) {
await this.updatePPOM();
async function _PPOMController_getNewFilesForAllChains() {
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_deleteOldChainIds).call(this);
// clear already scheduled fetch if any
if (__classPrivateFieldGet(this, _PPOMController_fileScheduleInterval, "f")) {
clearInterval(__classPrivateFieldGet(this, _PPOMController_fileScheduleInterval, "f"));
}
}, _PPOMController_fetchBlob =
// build a list of files to be fetched for all networks
const fileToBeFetchedList = __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getListOfFilesToBeFetched).call(this);
// if schedule interval is large so that not all files can be fetched in
// refreshInterval, reduce schedule interval
let scheduleInterval = this.state.fileScheduleInterval;
if (this.state.refreshInterval / (fileToBeFetchedList.length + 1) <
scheduleInterval) {
scheduleInterval =
this.state.refreshInterval / (fileToBeFetchedList.length + 1);
}
// schedule files to be fetched in intervals
__classPrivateFieldSet(this, _PPOMController_fileScheduleInterval, setInterval(() => {
const fileToBeFetched = fileToBeFetchedList.pop();
if (!fileToBeFetched) {
return;
}
const { fileVersionInfo, isLastFileOfNetwork } = fileToBeFetched;
// get the file from CDN
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getFile).call(this, fileVersionInfo)
.then(() => {
if (isLastFileOfNetwork) {
// set dataFetched for chainId to true
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_setChainIdDataFetched).call(this, fileVersionInfo.chainId);
}
})
.catch((exp) => console.error(`Error in getting file ${fileVersionInfo.filePath}: ${exp.message}`));
// clear interval if all files are fetched
if (!fileToBeFetchedList.length) {
clearInterval(__classPrivateFieldGet(this, _PPOMController_fileScheduleInterval, "f"));
}
}, scheduleInterval), "f");
}, _PPOMController_getAPIResponse =
/*
* Fetch the blob from the PPOM cdn.
* getAPIResponse - Generic method to fetch file from CDN.
*/
async function _PPOMController_fetchBlob(fileUrl) {
const response = await (0, controller_utils_1.safelyExecute)(async () => fetch(fileUrl, { cache: 'no-cache' }), true);
switch (response?.status) {
case 200: {
return await response.arrayBuffer();
}
default: {
throw new Error(`Failed to fetch file with url ${fileUrl}`);
}
async function _PPOMController_getAPIResponse(url, options = {}, method = 'GET') {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
const response = await (0, controller_utils_1.safelyExecute)(async () => fetch(url, {
method,
cache: 'no-cache',
redirect: 'error',
signal: controller.signal,
...options,
}), true);
clearTimeout(timeoutId);
if (response?.status !== 200) {
throw new Error(`Failed to fetch file with url: ${url}`);
}
return response;
}, _PPOMController_fetchVersionInfo =

@@ -314,12 +448,27 @@ /*

*/
async function _PPOMController_fetchVersionInfo(url) {
const response = await (0, controller_utils_1.safelyExecute)(async () => fetch(url, { cache: 'no-cache' }), true);
switch (response?.status) {
case 200: {
return response.json();
async function _PPOMController_fetchVersionInfo(updateForAllChains) {
const url = `${URL_PREFIX}${__classPrivateFieldGet(this, _PPOMController_cdnBaseUrl, "f")}/${PPOM_VERSION_FILE_NAME}`;
if (updateForAllChains) {
const headResponse = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getAPIResponse).call(this, url, {
headers: versionInfoFileHeaders,
}, 'HEAD');
const { versionFileETag } = this.state;
if (headResponse.headers.get('ETag') === versionFileETag) {
return undefined;
}
default: {
throw new Error(`Failed to fetch version info url: ${url}`);
}
this.update((draftState) => {
draftState.versionFileETag = headResponse.headers.get('ETag');
});
}
const response = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getAPIResponse).call(this, url, {
headers: versionInfoFileHeaders,
});
return response.json();
}, _PPOMController_fetchBlob =
/*
* Fetch the blob from the PPOM cdn.
*/
async function _PPOMController_fetchBlob(url) {
const response = await __classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_getAPIResponse).call(this, url);
return await response.arrayBuffer();
}, _PPOMController_jsonRpcRequest =

@@ -333,3 +482,3 @@ /*

const currentTimestamp = new Date().getTime();
const requests = this.state.providerRequests.filter((requestTime) => requestTime - currentTimestamp < MILLISECONDS_IN_FIVE_MINUTES);
const requests = this.state.providerRequests.filter((requestTime) => requestTime - currentTimestamp < FILE_FETCH_SCHEDULE_INTERVAL);
if (requests.length >= 5) {

@@ -366,6 +515,7 @@ reject(new Error('Number of request to provider from PPOM exceed rate limit'));

async function _PPOMController_getPPOM() {
await PPOMModule.default();
const chainId = this.state.lastChainId;
const { ppomInit, PPOM } = __classPrivateFieldGet(this, _PPOMController_ppomProvider, "f");
await ppomInit();
const { chainId } = this.state;
const files = await Promise.all(this.state.versionInfo
.filter((file) => !file.chainId || file.chainId === chainId)
.filter((file) => file.chainId === chainId)
.map(async (file) => {

@@ -375,4 +525,12 @@ const data = await __classPrivateFieldGet(this, _PPOMController_storage, "f").readFile(file.name, file.chainId);

}));
return new PPOMModule.PPOM(__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_jsonRpcRequest).bind(this), files);
return new PPOM(__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_jsonRpcRequest).bind(this), files);
}, _PPOMController_onFileScheduledInterval = function _PPOMController_onFileScheduledInterval() {
this.updatePPOM().catch(() => {
// console.error(`Error while trying to update PPOM: ${exp.message}`);
});
}, _PPOMController_scheduleFileDownloadForAllChains = function _PPOMController_scheduleFileDownloadForAllChains() {
__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_onFileScheduledInterval).call(this);
__classPrivateFieldSet(this, _PPOMController_refreshDataInterval, setInterval(__classPrivateFieldGet(this, _PPOMController_instances, "m", _PPOMController_onFileScheduledInterval).bind(this), this.state.refreshInterval), "f");
};
// todo: handle empty version info file to hold on validations
//# sourceMappingURL=ppom-controller.js.map
{
"name": "@metamask/ppom-validator",
"version": "0.0.1",
"version": "0.1.1",
"description": "This module has code to integrate Blockaid PPOM with MetaMask",

@@ -19,3 +19,3 @@ "homepage": "https://github.com/MetaMask/ppom-validator#readme",

"scripts": {
"build": "tsc --project tsconfig.build.json && cp node_modules/@blockaid/ppom-mock/dist/ppom_bg.wasm dist/ppom_bg.wasm",
"build": "tsc --project tsconfig.build.json",
"build:clean": "rimraf dist && yarn build",

@@ -35,3 +35,2 @@ "build:docs": "typedoc",

"dependencies": {
"@blockaid/ppom-mock": "^1.0.0",
"@metamask/base-controller": "^3.0.0",

@@ -46,3 +45,3 @@ "@metamask/controller-utils": "^4.0.0",

"@metamask/eslint-config": "^11.0.1",
"@metamask/eslint-config-jest": "^11.0.0",
"@metamask/eslint-config-jest": "^12.0.0",
"@metamask/eslint-config-nodejs": "^11.0.1",

@@ -49,0 +48,0 @@ "@metamask/eslint-config-typescript": "^11.0.0",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc