Skip to content

Commit

Permalink
feat: base implementation for reactive controller of wallet manager
Browse files Browse the repository at this point in the history
  • Loading branch information
wainola committed Oct 4, 2023
1 parent 43666c3 commit 08e7d70
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 31 deletions.
3 changes: 2 additions & 1 deletion packages/wallet-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"dependencies": {
"@polkadot/api": "10.7.2",
"@polkadot/extension-dapp": "^0.46.5",
"ethers": "5.7.2"
"ethers": "5.7.2",
"lit": "^2.8.0"
}
}
116 changes: 116 additions & 0 deletions packages/wallet-manager/src/WalletManagerController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { ReactiveController, ReactiveControllerHost } from 'lit';
import { EvmWallet, SubstrateWallet } from '.';
import { Web3Provider } from '@ethersproject/providers';
import { ApiPromise } from '@polkadot/api';
import { AddChain } from './types';
import { IWalletManagerController } from './wallets/interfaces';

export class WalletManagerController implements IWalletManagerController {
host: ReactiveControllerHost;
evmWallet?: EvmWallet;
substrateWallet?: SubstrateWallet;
account?: string;
substrateAccount?: string;

constructor(host: ReactiveControllerHost) {
(this.host = host).addController(this as ReactiveController);
}

/**
* @name initFromWeb3Provider
* @param web3Provider Web3Provider
* @description Initializes the EvmWallet from a Web3Provider
*/
public initFromWeb3Provider(web3Provider: Web3Provider): void {
this.evmWallet = EvmWallet.initFromWeb3Provider(web3Provider);
this.appendProviderEvents(this.evmWallet);
}

/**
* @name initFromWindow
* @description Initializes the EvmWallet from a valid EIP-1193 provider
*/
public initFromWindow(): void {
this.evmWallet = EvmWallet.initFromWindow();
}

/**
* @name connectFromApiPromise
* @param apiPromise
* @description Initializes the SubstrateWallet from an ApiPromise
*/
public connectFromApiPromise(apiPromise: ApiPromise): void {
this.substrateWallet = SubstrateWallet.connectFromApiPromise(apiPromise);
}

/**
* @name connectFromWssProvider
* @param wssProvider
* @description Initializes the SubstrateWallet from a wssProvider
*/
public async connectFromWssProvider(wssProvider: string): Promise<void> {
this.substrateWallet =
await SubstrateWallet.connectFromWssProvider(wssProvider);
}

/**
* @name addChain
* @param { AddChain }
* @description Adds a chain to the EvmWallet
* @returns void
*/
public async addChain({
chainId,
chainName,
rpcUrl,
nativeCurrency
}: AddChain): Promise<void> {
try {
await this.evmWallet?.addChain({
chainId,
chainName,
rpcUrl,
nativeCurrency
});
} catch (error) {
throw error;
}
}

/**
* @name connect
* @returns void
* @description Connects the Substrate extension key manager
*/
public async connectoToSubstrate(): Promise<void> {
await this.substrateWallet?.connect();
this.substrateAccount = this.substrateWallet?.substrateAccount;
this.host.requestUpdate();
}

/**
* @name connectEvmWallet
* @returns void
* @description Connects the EvmWallet
*/
public async connectEvmWallet(): Promise<void> {
await this.evmWallet?.connect();
this.account = this.evmWallet?.account;
this.host.requestUpdate();
}

get accountData(): string | undefined {
return this.account;
}

get substrateAccountAddress(): string | undefined {
return this.substrateAccount;
}

private appendProviderEvents(evmWallet: EvmWallet): void {
evmWallet.addListener('walletAccountChanged', (account) => {
this.account = account;
this.host.requestUpdate();
});
}
}
1 change: 1 addition & 0 deletions packages/wallet-manager/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { EvmWallet, SubstrateWallet } from './wallets';
export { WalletManagerController } from './WalletManagerController';
11 changes: 11 additions & 0 deletions packages/wallet-manager/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,14 @@ declare global {
ethereum: ExternalProvider;
}
}

export type AddChain = {
chainId: number;
rpcUrl: string;
chainName: string;
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
};
72 changes: 46 additions & 26 deletions packages/wallet-manager/src/wallets/Evm/Evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ import type {
Provider
} from '@ethersproject/providers';
import { IEvmWallet } from '../interfaces';
import { AddChain } from '../../types';

class EvmWallet extends events.EventEmitter implements IEvmWallet {
public account: string | undefined;
public web3Provider!: Web3Provider;
public windowConnector: Provider;
public windowConnector: ExternalProvider;

constructor(provider?: Web3Provider) {
super();

if (!window.ethereum) {
throw new Error('window.ethereum is not defined.');
} else {
this.windowConnector = window.ethereum as Provider;
this.windowConnector = window.ethereum as ExternalProvider;
}
if (!provider) {
this.web3Provider = new ethers.providers.Web3Provider(
Expand All @@ -30,15 +31,31 @@ class EvmWallet extends events.EventEmitter implements IEvmWallet {
this.appendProviderEvents();
}

static initFromWeb3Provider(web3Provider: Web3Provider) {
/**
* @name initFromWeb3Provider
* @param web3Provider
* @returns EvmWallet
* @description Initializes the EvmWallet from a Web3Provider
*/
static initFromWeb3Provider(web3Provider: Web3Provider): EvmWallet {
return new EvmWallet(web3Provider);
}

static initFromWindow() {
/**
* @name initFromWindow
* @returns EvmWallet
* @description Initializes the EvmWallet from a valid EIP-1193 provider
*/
static initFromWindow(): EvmWallet {
return new EvmWallet();
}

private async calculateAccountData(accounts?: string[]) {
/**
* @name calculateAccountData
* @param accounts
* @returns void
*/
private async calculateAccountData(accounts?: string[]): Promise<void> {
if (accounts?.length) {
this.account = accounts[0];
}
Expand All @@ -52,24 +69,33 @@ class EvmWallet extends events.EventEmitter implements IEvmWallet {
this.account = _accounts![0];
}

private reConnectToProvider() {
/**
* @name reConnectToProvider
* @returns void
*/
private reConnectToProvider(): void {
this.web3Provider = new ethers.providers.Web3Provider(
this.windowConnector as ExternalProvider
);
}

/**
* @name appendProviderEvents
* @returns void
* @description Appends the provider events to the windowConnector
*/
private appendProviderEvents(): void {
try {
this.checkWindow();
} catch (e) {
throw e;
}
this.windowConnector.on('connect', async () => {
(this.windowConnector as Provider).on('connect', async () => {
this.reConnectToProvider();
await this.calculateAccountData();
});

this.windowConnector.on(
(this.windowConnector as Provider).on(
'disconnect',
async (error: Error & { code: number; data?: unknown }) => {
console.log(error);
Expand All @@ -78,19 +104,22 @@ class EvmWallet extends events.EventEmitter implements IEvmWallet {
}
);

this.windowConnector.on('chainChanged', async () => {
(this.windowConnector as Provider).on('chainChanged', async () => {
this.reConnectToProvider();
await this.calculateAccountData();

this.emit('walletChainChanged', this.web3Provider);
});

this.windowConnector.on('accountsChanged', async (accounts: string[]) => {
this.reConnectToProvider();
await this.calculateAccountData(accounts);
(this.windowConnector as Provider).on(
'accountsChanged',
async (accounts: string[]) => {
this.reConnectToProvider();
await this.calculateAccountData(accounts);

this.emit('walletAccountChanged', this.account);
});
this.emit('walletAccountChanged', this.account);
}
);
}

// eslint-disable-next-line class-methods-use-this
Expand Down Expand Up @@ -123,27 +152,18 @@ class EvmWallet extends events.EventEmitter implements IEvmWallet {
chainName,
rpcUrl,
nativeCurrency
}: {
chainId: number;
rpcUrl: string;
chainName: string;
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
}): Promise<void> {
}: AddChain): Promise<void> {
this.checkWindow();

try {
await (this.windowConnector as ExternalProvider).request!({
await this.windowConnector.request!({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${chainId.toString(16)}` }]
});
} catch (switchError: unknown) {
if ((switchError as { code: number }).code === 4902) {
try {
await (this.windowConnector as ExternalProvider).request!({
await this.windowConnector.request!({
method: 'wallet_addEthereumChain',
params: [
{
Expand Down
3 changes: 2 additions & 1 deletion packages/wallet-manager/src/wallets/Substrate/Substrate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ApiPromise, WsProvider } from '@polkadot/api';
import { web3Accounts, web3Enable } from '@polkadot/extension-dapp';
import { ISusbtrateWallet } from '../interfaces';

class SubstrateWallet {
class SubstrateWallet implements ISusbtrateWallet {
substrateAccount?: string;
apiPromise?: ApiPromise;
wssProvider?: WsProvider;
Expand Down
26 changes: 23 additions & 3 deletions packages/wallet-manager/src/wallets/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Provider, Web3Provider } from '@ethersproject/providers';
import { Web3Provider, ExternalProvider } from '@ethersproject/providers';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { ReactiveController } from 'lit';
import { AddChain } from '../../types';

export interface SupportedWallet {
id: string;
Expand All @@ -10,7 +12,7 @@ export interface SupportedWallet {

export interface IEvmWallet {
web3Provider: Web3Provider;
windowConnector: Provider;
windowConnector: ExternalProvider;
account?: string;
connect(): Promise<void>;
addChain({
Expand All @@ -29,8 +31,26 @@ export interface IEvmWallet {
}): Promise<void>;
}

export interface SusbtrateWallet {
export interface ISusbtrateWallet {
wssProvider?: WsProvider;
apiPromise?: ApiPromise;
substrateAccount?: string;
}

export interface IWalletManagerController extends ReactiveController {
web3Provider?: Web3Provider;
apiPromise?: ApiPromise;
wsProviderUrl?: string;
evmWallet?: IEvmWallet;
substrateWallet?: ISusbtrateWallet;
account?: string;
substrateAccount?: string;

initFromWeb3Provider(web3Provider: Web3Provider): void;
initFromWindow(): void;
connectFromWssProvider(wssProvider: string): Promise<void>;
connectFromApiPromise(apiPromise: ApiPromise): void;
addChain(addChainParameters: AddChain): Promise<void>;
connectoToSubstrate(): Promise<void>;
connectEvmWallet(): Promise<void>;
}

0 comments on commit 08e7d70

Please sign in to comment.