Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ag/feature/create provider #39

Merged
merged 8 commits into from
Dec 5, 2024
Merged
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
12 changes: 11 additions & 1 deletion src/core/methods/initApp/initApp.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { safeWindow } from 'constants/index';
import { restoreProvider } from 'core/providers/helpers/restoreProvider';
import { ProviderFactory } from 'core/providers/ProviderFactory';
import { getDefaultNativeAuthConfig } from 'services/nativeAuth/methods/getDefaultNativeAuthConfig';
import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types';
import { initializeNetwork } from 'store/actions';
Expand Down Expand Up @@ -30,7 +32,8 @@ const defaultInitAppProps = {
* */
export async function initApp({
storage = defaultInitAppProps.storage,
dAppConfig
dAppConfig,
customProviders
}: InitAppType) {
initStore(storage.getStorageCallback);

Expand All @@ -57,6 +60,13 @@ export async function initApp({

const isLoggedIn = getIsLoggedIn();

const usedProviders = [
...((safeWindow as any)?.multiversx?.providers || []),
...(customProviders || [])
];

ProviderFactory.customProviders(usedProviders || []);

if (isLoggedIn) {
await restoreProvider();
await registerWebsocketListener();
Expand Down
2 changes: 2 additions & 0 deletions src/core/methods/initApp/initApp.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ICustomProvider } from 'core/providers/types/providerFactory.types';
import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types';
import { StorageCallback } from 'store/storage';
import { EnvironmentsEnum } from 'types/enums.types';
Expand Down Expand Up @@ -49,4 +50,5 @@ export type InitAppType = {
getStorageCallback: StorageCallback;
};
dAppConfig: DappConfigType;
customProviders?: ICustomProvider[];
};
4 changes: 1 addition & 3 deletions src/core/providers/DappProvider/helpers/logout/logout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getAddress } from 'core/methods/account/getAddress';
import { getProviderType } from 'core/providers/helpers/utils';
import {
IProvider,
ProviderTypeEnum
Expand Down Expand Up @@ -48,7 +47,6 @@ export async function logout({
}
}: IProviderLogout) {
let address = getAddress();
const providerType = getProviderType(provider);

if (options.shouldBroadcastLogoutAcrossTabs) {
broadcastLogoutAcrossTabs(address);
Expand All @@ -59,7 +57,7 @@ export async function logout({

if (
options.hasConsentPopup &&
providerType === ProviderTypeEnum.crossWindow
provider.getType() === ProviderTypeEnum.crossWindow
) {
(provider as unknown as CrossWindowProvider).setShouldShowConsentPopup(
true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Message, Address } from '@multiversx/sdk-core';
import { getAddress } from 'core/methods/account/getAddress';
import { getProviderType } from 'core/providers/helpers/utils';
import {
IProvider,
ProviderTypeEnum
Expand All @@ -22,7 +21,6 @@ export async function signMessage({
options
}: SignMessageType): Promise<Nullable<Message>> {
const address = getAddress();
const providerType = getProviderType(provider);

const messageToSign = new Message({
address: new Address(address),
Expand All @@ -31,7 +29,7 @@ export async function signMessage({

if (
options?.hasConsentPopup &&
providerType === ProviderTypeEnum.crossWindow
provider.getType() === ProviderTypeEnum.crossWindow
) {
(provider as unknown as CrossWindowProvider).setShouldShowConsentPopup(
true
Expand Down
37 changes: 20 additions & 17 deletions src/core/providers/ProviderFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,26 @@ import { getConfig } from './helpers/getConfig';
import { createIframeProvider } from './helpers/iframe/createIframeProvider';
import { createLedgerProvider } from './helpers/ledger/createLedgerProvider';
import {
ICustomProvider,
IProvider,
IProviderFactory,
ProviderTypeEnum
} from './types/providerFactory.types';

export class ProviderFactory {
public async create({
private static _customProviders: ICustomProvider[] = [];

public static customProviders(providers: ICustomProvider[]) {
this._customProviders = providers;
}

public static async create({
type,
config: userConfig,
customProvider
config: userConfig
}: IProviderFactory): Promise<DappProvider> {
let createdProvider: IProvider | null = null;
const { account, ui } = await getConfig(userConfig);
const config = await getConfig(userConfig);
const { account, UI } = config;

switch (type) {
case ProviderTypeEnum.extension: {
Expand All @@ -47,10 +54,7 @@ export class ProviderFactory {
}

case ProviderTypeEnum.ledger: {
const ledgerProvider = await createLedgerProvider(
ui.ledger.eventBus,
ui.ledger.mount
);
const ledgerProvider = await createLedgerProvider(UI.ledger.mount);

if (!ledgerProvider) {
throw new Error('Unable to create ledger provider');
Expand Down Expand Up @@ -106,16 +110,15 @@ export class ProviderFactory {
break;
}

case ProviderTypeEnum.custom: {
if (!customProvider) {
throw new Error('Unable to create custom provider provider');
}
createdProvider = customProvider;
default: {
this._customProviders.forEach(async (customProvider) => {
if (customProvider.type === type) {
createdProvider = await customProvider.constructor(config);
createdProvider.getType = () => type;
}
});
break;
}

default:
break;
}

if (!createdProvider) {
Expand All @@ -125,7 +128,7 @@ export class ProviderFactory {
const dappProvider = new DappProvider(createdProvider);

setAccountProvider(dappProvider);
setProviderType(type);
setProviderType(type as ProviderTypeEnum);

return dappProvider;
}
Expand Down
42 changes: 26 additions & 16 deletions src/core/providers/helpers/getConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,46 @@ import { defineCustomElements } from '@multiversx/sdk-dapp-core-ui/loader';
import { safeWindow } from 'constants/index';
import {
IProviderConfig,
IProviderConfigUI,
ProviderTypeEnum
} from '../types/providerFactory.types';

export const getConfig = async (config: IProviderConfig = {}) => {
if (!safeWindow.document) {
return config;
const UI: IProviderConfigUI = {
[ProviderTypeEnum.ledger]: {
mount: () => {
throw new Error('mount not implemented');
}
}
};

const defaultConfig = { UI };

defineCustomElements(safeWindow);
const ledgerModalElement = document.createElement(
'ledger-connect-modal'
) as LedgerConnectModal;
document.body.appendChild(ledgerModalElement);
await customElements.whenDefined('ledger-connect-modal');
const eventBus = await ledgerModalElement.getEventBus();
export const getConfig = async (config: IProviderConfig = defaultConfig) => {
if (!safeWindow.document) {
return { ...defaultConfig, ...config };
}

const ui = {
const UI = {
[ProviderTypeEnum.ledger]: {
eventBus,
mount: () => {
mount: async () => {
defineCustomElements(safeWindow);
const ledgerModalElement = document.createElement(
'ledger-connect-modal'
) as LedgerConnectModal;

document.body.appendChild(ledgerModalElement);

const eventBus = await ledgerModalElement.getEventBus();
return eventBus;
}
}
};

return {
...config,
ui: {
...ui,
...config.ui
UI: {
...defaultConfig.UI,
...UI
}
};
};
14 changes: 7 additions & 7 deletions src/core/providers/helpers/ledger/createLedgerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ import { ILedgerAccount } from './ledger.types';
const failInitializeErrorText = 'Check if the MultiversX App is open on Ledger';

export async function createLedgerProvider(
eventBus: IEventBus,
mount: () => void
mount: () => Promise<IEventBus>
): Promise<IProvider | null> {
if (!eventBus) {
throw new Error('Event bus not provided for Ledger provider');
}

const shouldInitiateLogin = !getIsLoggedIn();

let eventBus: IEventBus | undefined;
if (shouldInitiateLogin) {
mount?.();
eventBus = await mount?.();
}

if (!eventBus) {
throw new Error('Event bus not provided for Ledger provider');
}

const manager = LedgerConnectStateManager.getInstance(eventBus);
Expand Down
4 changes: 1 addition & 3 deletions src/core/providers/helpers/restoreProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ export async function restoreProvider() {
}
};

const factory = new ProviderFactory();

const provider = await factory.create({
const provider = await ProviderFactory.create({
type,
config
});
Expand Down
33 changes: 0 additions & 33 deletions src/core/providers/helpers/utils.ts

This file was deleted.

36 changes: 23 additions & 13 deletions src/core/providers/types/providerFactory.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { IDAppProviderBase } from '@multiversx/sdk-dapp-utils';

// @ts-ignore
export interface IProvider extends IDAppProviderBase {
export interface IProvider<T extends ProviderTypeEnum = ProviderTypeEnum>
extends IDAppProviderBase {
init: () => Promise<boolean>;
login: (options?: { callbackUrl?: string; token?: string }) => Promise<{
address: string;
Expand All @@ -12,7 +13,7 @@ export interface IProvider extends IDAppProviderBase {
}>;
logout: () => Promise<boolean>;
setShouldShowConsentPopup?: (shouldShow: boolean) => void;
getType: () => ProviderTypeEnum;
getType: () => T[keyof T] | string;
getAddress(): Promise<string | undefined>;
// TODO will be removed as soon as the new login method is implemented in the same way for all providers
getTokenLoginSignature(): string | undefined;
Expand All @@ -26,16 +27,17 @@ export interface IEventBus {
unsubscribe(event: string, callback: Function): void;
}

export interface IProviderConfigUI {
ledger: {
mount: () => Promise<IEventBus>;
};
}

export interface IProviderConfig {
account?: {
address: string;
};
ui?: {
ledger: {
eventBus: IEventBus;
mount: () => void;
};
};
UI?: IProviderConfigUI;
}

export enum ProviderTypeEnum {
Expand All @@ -47,13 +49,21 @@ export enum ProviderTypeEnum {
opera = 'opera',
metamask = 'metamask',
passkey = 'passkey',
webhook = 'webhook',
custom = 'custom',
none = ''
}

export interface IProviderFactory {
type: ProviderTypeEnum;
export interface IProviderFactory<
T extends ProviderTypeEnum = ProviderTypeEnum
> {
type: T[keyof T];
config?: IProviderConfig;
customProvider?: IProvider;
}

export interface ICustomProvider<
T extends ProviderTypeEnum = ProviderTypeEnum
> {
name: string;
type: T[keyof T];
icon: string;
constructor: (config: IProviderConfig) => Promise<IProvider>;
}
4 changes: 3 additions & 1 deletion src/store/actions/loginInfo/loginInfoActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import {
import { getStore } from 'store/store';
import { TokenLoginType } from 'types/login.types';

export const setProviderType = (providerType: ProviderTypeEnum) =>
export const setProviderType = <T extends ProviderTypeEnum = ProviderTypeEnum>(
providerType: T
) =>
getStore().setState(({ loginInfo: state }) => {
state.providerType = providerType;
});
Expand Down
6 changes: 4 additions & 2 deletions src/store/actions/sharedActions/sharedActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { resetStore } from 'store/middleware/logoutMiddleware';
import { getStore } from '../../store';

export const logoutAction = () => getStore().setState(resetStore);
export interface LoginActionPayloadType {
export interface LoginActionPayloadType<
T extends ProviderTypeEnum = ProviderTypeEnum
> {
address: string;
providerType: ProviderTypeEnum;
providerType: T[keyof T];
}

export const loginAction = ({
Expand Down
Loading
Loading