venom-connect
Version:
<p align="center"> <a href="https://github.com/venom-blockchain/developer-program"> <img src="https://raw.githubusercontent.com/venom-blockchain/developer-program/main/vf-dev-program.png" alt="Logo" width="366.8" height="146.4"> </a> </p>
492 lines (439 loc) • 14.8 kB
text/typescript
import {
CONNECT_EVENT,
ERROR_EVENT,
Events,
EXTENSION_AUTH_EVENT,
EXTENSION_WINDOW_CLOSED_EVENT,
SELECT_EVENT,
} from "../helpers/events";
import { checkIsCurrentBrowser } from "../helpers/utils";
import * as allProviders from "../providers";
import {
ConnectorType,
ExtensionConnector,
ProviderControllerOptions,
ProviderOptionsList,
ProviderOptionsListWithOnClick,
ProviderOptionsWithConnector,
ProviderOptionsWithConnectorOptional,
UserProvidersOptions,
} from "../types";
import { EventController } from "./EventController";
const sortingArr: ConnectorType[] = ["extension", "mobile", "ios", "android"];
export const getPromiseRaw = (
windowObject: any,
walletId: string,
type: string | undefined = "extension",
nTries: number = 0
) => {
const promises = {
venomwallet: {
extension: () => {
if (windowObject) {
return new Promise((resolve, reject) => {
if (windowObject.__venom && !windowObject.__venom.isOneArt) {
resolve(windowObject.__venom);
return;
}
// let nTries = 0; // число попыток, иначе он будет бесконечно, может это вынести в конфиг
let interval = setInterval(() => {
if (windowObject.__venom && !windowObject.__venom.isOneArt) {
clearInterval(interval);
resolve(windowObject.__venom);
} else if (nTries > 0) {
nTries--;
} else {
clearInterval(interval);
reject("Venom wallet is not found");
}
}, 100);
});
}
return Promise.reject();
},
},
everwallet: {
extension: () => {
if (windowObject) {
return new Promise((resolve, reject) => {
if (windowObject.__ever) {
resolve(windowObject.__ever);
return;
}
// let nTries = 0; // число попыток, иначе он будет бесконечно, может это вынести в конфиг
let interval = setInterval(() => {
if (windowObject.__ever) {
clearInterval(interval);
resolve(windowObject.__ever);
} else if (nTries > 0) {
nTries--;
} else {
clearInterval(interval);
reject("Ever wallet is not found");
}
}, 100);
});
}
return Promise.reject();
},
},
oxychatwallet: {
// todo
extension: () => {
if (windowObject) {
return new Promise((resolve, reject) => {
if (windowObject.__oxy) {
resolve(windowObject.__oxy);
return;
}
// let nTries = 0; // число попыток, иначе он будет бесконечно, может это вынести в конфиг
let interval = setInterval(() => {
if (windowObject.__oxy) {
clearInterval(interval);
resolve(windowObject.__oxy);
} else if (nTries > 0) {
nTries--;
} else {
clearInterval(interval);
reject("Ever wallet is not found");
}
}, 100);
});
}
return Promise.reject();
},
},
oneartwallet: {
extension: () => {
if (windowObject) {
return new Promise((resolve, reject) => {
if (windowObject.__venom && windowObject.__venom.isOneArt) {
resolve(windowObject.__venom);
return;
}
let interval = setInterval(() => {
if (windowObject.__venom && windowObject.__venom.isOneArt) {
clearInterval(interval);
resolve(windowObject.__venom);
} else if (nTries > 0) {
nTries--;
} else {
clearInterval(interval);
reject("OneArt wallet is not found");
}
}, 100);
});
}
return Promise.reject();
},
},
};
// @ts-ignore
return promises[walletId]?.[type];
};
export class ProviderController {
private eventController: EventController = new EventController();
private providers: ProviderOptionsList = [];
private providerOptions: UserProvidersOptions;
private checkNetworkId: number | number[];
private checkNetworkName: string;
private nTries: number;
private _currentProvider: any;
public getStandalone = async (walletId: string) => {
const wallet = this.providers?.find((provider) => provider.id === walletId);
if (wallet) {
const standaloneFromUser = wallet?.walletWaysToConnect.find(
(way) => way.type === "extension" && !!way.standalone
);
if (standaloneFromUser?.standalone) {
return standaloneFromUser.standalone(
standaloneFromUser.package,
standaloneFromUser.packageOptionsStandalone
);
}
}
return null;
};
public set currentProvider(cp) {
const updateVenomModal = () =>
window.updateVenomModal({
isFullProvider: !!cp,
});
(function tryUpdateVenomModal() {
try {
updateVenomModal();
} catch (error) {
setTimeout(tryUpdateVenomModal, 100);
}
})();
this._currentProvider = cp;
}
public get currentProvider() {
return this._currentProvider;
}
constructor(options: ProviderControllerOptions) {
this.nTries = options.nTries || 0;
const defaultPackageOptions: {
[key: string]: Record<string, Record<string, any>>;
} = {
venomwallet: window
? {
extension: {
forceUseFallback: true,
fallback:
getPromiseRaw(window, "venomwallet", undefined, this.nTries) ||
(() => Promise.reject("venomwallet fallback error")),
},
standalone: {}, // ?
}
: {},
everwallet: window
? {
extension: {
forceUseFallback: true,
fallback:
getPromiseRaw(window, "everwallet", undefined, this.nTries) ||
(() => Promise.reject("everwallet fallback error")),
},
standalone: {}, // ?
}
: {},
oxychatwallet: window
? {
extension: {
forceUseFallback: true,
fallback:
getPromiseRaw(
window,
"oxychatwallet",
undefined,
this.nTries
) || (() => Promise.reject("oxychatwallet fallback error")),
},
standalone: {}, // ?
}
: {},
};
this.checkNetworkId = options.checkNetworkId;
this.checkNetworkName = options.checkNetworkName;
this.providerOptions = options.providersOptions;
// TODO можно будет задать order для списка
this.providers = (Object.keys(allProviders.connectors).reverse() || [])
.filter((id) => this.providerOptions?.[id])
.map((id) => {
const providerInfo: ProviderOptionsWithConnector =
// @ts-ignore
allProviders.providers?.[id] || undefined;
const {
// wallet,
links,
walletWaysToConnect,
defaultWalletWaysToConnect,
} = this.providerOptions?.[id];
const types = walletWaysToConnect?.map(({ type }) => type);
const ids = walletWaysToConnect?.map(({ id }) => id);
return {
id,
// wallet,
links,
walletWaysToConnect: (
providerInfo.walletWaysToConnect as ProviderOptionsWithConnectorOptional["walletWaysToConnect"]
)
.filter((walletWayToConnect) => {
return (
!!defaultWalletWaysToConnect?.includes(
walletWayToConnect.type
) &&
(!types?.includes(walletWayToConnect.type) ||
!ids?.includes(walletWayToConnect.id))
);
})
.concat(walletWaysToConnect || [])
.map((walletWayToConnect) => {
const defaultWay =
// @ts-ignore
allProviders?.providers?.[id]?.walletWaysToConnect?.find(
// allProviders?.providers?.venomwallet?.walletWaysToConnect?.find(
// @ts-ignore
({ type }) => type === walletWayToConnect.type
);
const isCurrentDevise = checkIsCurrentBrowser(
defaultWay.options.isCurrentBrowser
);
const userOptions = walletWayToConnect.packageOptions;
const defaultOptions =
defaultPackageOptions[id]?.[walletWayToConnect.type];
const packageOptions = isCurrentDevise.isCurrentBrowser
? userOptions || defaultOptions || {}
: {};
const userOptionsStandalone =
walletWayToConnect.packageOptionsStandalone;
const defaultOptionsStandalone =
defaultPackageOptions[id]?.["standalone"];
const packageOptionsStandalone =
userOptionsStandalone || defaultOptionsStandalone || {};
// задаём 1000 как дефолт ид венома
packageOptions.checkNetworkId = this.checkNetworkId;
packageOptions.checkNetworkName = this.checkNetworkName;
return {
...defaultWay,
...walletWayToConnect,
connector:
walletWayToConnect.connector ||
// @ts-ignore
allProviders?.connectors?.[id]?.[walletWayToConnect.type]?.[
"connector"
],
authConnector:
walletWayToConnect.authConnector ||
// @ts-ignore
allProviders?.connectors?.[id]?.[walletWayToConnect.type]?.[
"authChecker"
],
standalone:
walletWayToConnect.standalone ||
// @ts-ignore
allProviders?.connectors?.[id]?.[walletWayToConnect.type]?.[
"standalone"
],
packageOptions,
packageOptionsStandalone,
options: {
...(typeof defaultWay?.options === "object"
? defaultWay?.options
: {}),
...walletWayToConnect?.options,
},
};
})
.sort(
(a, b) => sortingArr.indexOf(a.type) - sortingArr.indexOf(b.type)
),
};
});
}
public shouldDisplayProvider(id: string) {
const provider = this.getProvider(id);
return provider !== undefined;
}
public getOptions = () => {
const options = this.providers.reduce((r, provider) => {
const { id, walletWaysToConnect } = provider;
const obj = {
...provider,
walletWaysToConnect: walletWaysToConnect
.map((walletWayToConnect) => {
const { connector, id: connectorId } = walletWayToConnect;
const isShould = this.shouldDisplayProvider(id);
if (isShould && connector)
return {
...walletWayToConnect,
onClick: () => this.connectTo(id, connectorId, connector),
};
else return null;
})
.filter((walletWayToConnect) => !!walletWayToConnect),
};
if (obj.walletWaysToConnect.length) {
r.push(obj as ProviderOptionsListWithOnClick[0]);
}
return r;
}, [] as ProviderOptionsListWithOnClick);
return options;
};
public getProvider = (id: string) => {
return this.providers.find((provider) => provider.id === id);
};
public getProviderOption(id: string, connectorId: string, key: string) {
const walletWaysToConnect = this.providers?.find(
(provider) => provider.id === id
)?.walletWaysToConnect;
const walletWayToConnect = walletWaysToConnect?.find(
(walletWayToConnect) => walletWayToConnect.id === connectorId
);
// @ts-ignore
return walletWayToConnect?.[key] || {};
}
public getAuthTo = async (
id: string,
connectorId: string,
authConnector: (providerPackage: any, opts: any) => Promise<any>
) => {
try {
this.currentProvider = null;
const providerPackage = this.getProviderOption(
id,
connectorId,
"package"
);
const providerOptions = this.getProviderOption(
id,
connectorId,
"packageOptions"
);
const options = {
...providerOptions,
};
const provider = await authConnector(providerPackage, options);
this.currentProvider = provider?.auth || null;
return provider || null;
} catch (error) {
return null;
}
};
public connectTo = async (
id: string,
connectorId: string,
connector: ExtensionConnector
) => {
try {
this.currentProvider = null;
this.eventController.trigger(SELECT_EVENT, id);
const providerPackage = this.getProviderOption(
id,
connectorId,
"package"
);
const providerOptions = this.getProviderOption(
id,
connectorId,
"packageOptions"
);
const options = {
...providerOptions,
};
const provider = await connector(providerPackage, options, {
authorizationCompleted: (_provider) => {
this.eventController.trigger(EXTENSION_AUTH_EVENT, _provider);
},
extensionWindowClosed: () => {
this.eventController.trigger(EXTENSION_WINDOW_CLOSED_EVENT);
},
extensionWindowError: (error) => {
this.eventController.trigger(ERROR_EVENT, error);
},
});
this.currentProvider = provider;
this.eventController.trigger(CONNECT_EVENT, provider);
} catch (error) {
this.eventController.trigger(ERROR_EVENT, error);
}
};
public on(event: Events, callback: (result: any) => void): () => void {
this.eventController.on({
event,
callback,
});
return () =>
this.eventController.off({
event,
callback,
});
}
public off(event: Events, callback?: (result: any) => void): void {
this.eventController.off({
event,
callback,
});
}
}