Skip to content

Commit

Permalink
feature: Allow enabling injected Cere Wallet without triggering other…
Browse files Browse the repository at this point in the history
… wallets (#207)
  • Loading branch information
skambalin authored Jul 29, 2024
1 parent 432ffa6 commit 15c8f70
Show file tree
Hide file tree
Showing 8 changed files with 425 additions and 309 deletions.
643 changes: 377 additions & 266 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/embed-wallet-inject/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cere/embed-wallet-inject",
"version": "0.19.0",
"version": "0.20.1",
"sideEffects": false,
"type": "module",
"types": "./dist/types/index.d.ts",
Expand Down
23 changes: 19 additions & 4 deletions packages/embed-wallet-inject/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import { EmbedWallet, PermissionRequest } from '@cere/embed-wallet';
import { EmbedWallet, WalletConnectOptions } from '@cere/embed-wallet';

import { PolkadotInjector } from './polkadot';

type InjectTarget = 'polkadot';

export type InjectOptions = {
export type EnableOptions = {
name?: string;
targets?: InjectTarget[];
target?: InjectTarget;
autoConnect?: boolean;
permissions?: PermissionRequest;
connectOptions?: WalletConnectOptions;
};

export type InjectOptions = Omit<EnableOptions, 'target'> & {
targets?: InjectTarget[];
};

/**
* Enables the injected wallet and returns the injected application
*/
export const enable = async (wallet: EmbedWallet, { target = 'polkadot', ...options }: EnableOptions = {}) => {
if (target !== 'polkadot') {
throw new Error(`Unsupported target: ${target}`);
}

return new PolkadotInjector(wallet, options).enable();
};

/**
Expand Down
53 changes: 19 additions & 34 deletions packages/embed-wallet-inject/src/polkadot/PolkadotInjector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EmbedWallet, PermissionRequest, WalletAccount } from '@cere/embed-wallet';
import { EmbedWallet, WalletConnectOptions, WalletAccount } from '@cere/embed-wallet';
import { injectExtension } from '@polkadot/extension-inject';
import type { Injected, InjectedAccount, InjectedAccounts } from '@polkadot/extension-inject/types';
import type { SignerPayloadRaw, SignerResult, Signer, SignerPayloadJSON } from '@polkadot/types/types';
Expand All @@ -8,7 +8,7 @@ export type PolkadotInjectorOptions = {
version?: string;
autoConnect?: boolean;
waitReady?: boolean;
permissions?: PermissionRequest;
connectOptions?: WalletConnectOptions;
};

export class PolkadotInjector {
Expand All @@ -17,58 +17,43 @@ export class PolkadotInjector {
private injected: boolean = false;
private shouldConnect: boolean;
private shouldWait: boolean;
private permissions?: PermissionRequest;
private connectOptions?: WalletConnectOptions;

constructor(
readonly wallet: EmbedWallet,
{ name, version, autoConnect = false, waitReady = true, permissions }: PolkadotInjectorOptions = {},
{ name, version, autoConnect = false, waitReady = true, connectOptions }: PolkadotInjectorOptions = {},
) {
this.name = name || 'Cere Wallet';
this.version = version || '0.0.0';
this.shouldConnect = autoConnect;
this.shouldWait = waitReady;
this.permissions = permissions;
this.connectOptions = connectOptions;
}

get isInjected() {
return this.injected;
}

private waitReady = () =>
new Promise((resolve) => {
if (this.wallet.status !== 'not-ready') {
return resolve(true);
}

const unsubscribe = this.wallet.subscribe('status-update', () => {
if (this.wallet.status !== 'not-ready') {
resolve(true);
unsubscribe();
}
});
});

private waitReady = () => this.wallet.isReady.then(() => true);
private filterAccounts = (accounts: WalletAccount[]) => {
return accounts.filter((account: WalletAccount) => account.type === 'ed25519') as InjectedAccount[];
};

private getAccounts = async () => {
readonly getAccounts = async () => {
const allAccounts = await this.wallet.provider.request({
method: 'wallet_accounts',
});

return this.filterAccounts(allAccounts);
};

private subscribeAccounts = (onReceive: (accounts: InjectedAccount[]) => void) => {
const listener = (accounts: WalletAccount[]) => onReceive(this.filterAccounts(accounts));

this.wallet.provider.on('wallet_accountsChanged', listener);

return () => this.wallet.provider.off('wallet_accountsChanged', listener);
readonly subscribeAccounts = (onReceive: (accounts: InjectedAccount[]) => void) => {
return this.wallet.subscribe('accounts-update', (accounts: WalletAccount[]) =>
onReceive(this.filterAccounts(accounts)),
);
};

private signRaw = async (raw: SignerPayloadRaw): Promise<SignerResult> => {
readonly signRaw = async (raw: SignerPayloadRaw): Promise<SignerResult> => {
const signature = await this.wallet.provider.request({
method: 'ed25519_signRaw',
params: [raw.address, raw.data],
Expand All @@ -77,7 +62,7 @@ export class PolkadotInjector {
return { id: 0, signature };
};

private signPayload = async (payload: SignerPayloadJSON): Promise<SignerResult> => {
readonly signPayload = async (payload: SignerPayloadJSON): Promise<SignerResult> => {
const signature = await this.wallet.provider.request({
method: 'ed25519_signPayload',
params: [payload],
Expand All @@ -86,17 +71,17 @@ export class PolkadotInjector {
return { id: 0, signature };
};

private enable = async (): Promise<Injected> => {
readonly enable = async (): Promise<Injected> => {
if (this.shouldWait) {
await this.waitReady();
}

const { permissions } = this.connectOptions || {};

if (this.shouldConnect && this.wallet.status === 'ready') {
await this.wallet.connect({
permissions: this.permissions,
});
} else if (this.wallet.status === 'connected' && this.permissions) {
await this.wallet.requestPermissions(this.permissions).catch(console.warn);
await this.wallet.connect(this.connectOptions);
} else if (this.wallet.status === 'connected' && permissions) {
await this.wallet.requestPermissions(permissions).catch(console.warn);
}

const accounts: InjectedAccounts = {
Expand Down
2 changes: 1 addition & 1 deletion packages/embed-wallet/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cere/embed-wallet",
"version": "0.19.0",
"version": "0.20.1",
"description": "Cere Wallet SDK to integrate the wallet into a web application.",
"sideEffects": false,
"main": "dist/bundle.umd.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/embed-wallet/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const WALLET_CLIENT_VERSION = '1.39.0';
export const WALLET_CLIENT_VERSION = '1.40.0';
2 changes: 1 addition & 1 deletion playground/Wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ export const Wallet = () => {
variant="contained"
color="primary"
disabled={status === 'not-ready' || status === 'connecting' || status === 'initializing'}
onClick={() => handleConnect({ email: 'sergey.kambalin@cere.io' })}
onClick={() => handleConnect({ email: 'wallet-playground@cere.io' })}
>
Connect wallet (email)
</Button>
Expand Down
7 changes: 6 additions & 1 deletion src/stores/AuthenticationStore/AuthenticationStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { makeAutoObservable, reaction, when } from 'mobx';
import { LoginOptions } from '@cere-wallet/communication';

import { reportError } from '~/reporting';
import { Wallet } from '../types';
import { PopupManagerStore } from '../PopupManagerStore';
import { AccountStore, AccountLoginData } from '../AccountStore';
Expand Down Expand Up @@ -209,7 +210,11 @@ export class AuthenticationStore {

this.syncLoginData();
await when(() => !!this.accountStore.account); // Wait for accounts to be created from the privateKey
await this.applicationsStore.saveApplication({ permissions });

/**
* Save application permissions in background to avoid blocking the UI
*/
this.applicationsStore.saveApplication({ permissions }).catch(reportError);

return this.accountStore.account!.address;
}
Expand Down

0 comments on commit 15c8f70

Please sign in to comment.