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

Provider injection #88

Closed
wants to merge 15 commits into from
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# ![polkadot{.js} extension](docs/logo.jpg)
[![Greenkeeper badge](https://badges.greenkeeper.io/polkadot-js/extension.svg)](https://greenkeeper.io/)

A very simple scaffolding browser extension that injects a [@polkadot/api](https://github.com/polkadot-js/api) Signer into a page, along with any associated accounts, allowing for use by any dapp. This is an extensible POC implementation of a Polkadot/Substrate browser signer.

As it stands, it does one thing: it _only_ manages accounts and allows the signing of transactions with those accounts. It does not inject providers for use by dapps at this early point, nor does it perform wallet functions where it constructs and submits txs to the network.
A very simple scaffolding browser extension that injects a [@polkadot/api](https://github.com/polkadot-js/api) Signer into a page, along with any associated accounts, allowing for use by any dapp. A provider is also injected so that dapps can connect to your node. This is an extensible POC implementation of a Polkadot/Substrate browser signer.

## Installation

Expand Down Expand Up @@ -61,12 +59,12 @@ When there is more than one extension, each will populate an entry above, so fro
```js
interface Injected {
// the interface for Accounts, as detailed below
read-only accounts: Accounts;
readonly accounts: Accounts;
// the standard Signer interface for the API, as detailed below
read-only signer: Signer;
// not injected as of yet, subscribable provider for polkadot-js API injection,
readonly signer: Signer;
// subscribable provider for polkadot-js API injection,
// this can be passed to the API itself upon construction in the dapp
// read-only provider: Provider
readonly provider: Provider
}

interface Account = {
Expand Down Expand Up @@ -120,7 +118,7 @@ import { ApiPromise } from '@polkadot/api';

const pjsx = await window.injectedWeb3['polkadot-js'].enable('my dapp');
const accounts = await pjsx.accounts.get();
const api = await Api.create({ signer: pjsx.signer });
const api = await Api.create({ signer: pjsx.signer, provider: pjsx.provider });
```

Generally, you would probably want to have access to all extensions available and have a slightly higher-level interface to work with. For these cases, [extension-dapp](packages/extension-dapp/) provides a cleaner interface around the injected object, making it simpler to work with from a dapp perspective.
16 changes: 12 additions & 4 deletions packages/extension-dapp/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
# @polkadot/extension-dapp

A basic extractor that manipulates the `window.injectedWeb3` to retrieve all the providers added to the page. It has a number of utilities -
A basic extractor that manipulates the `window.injectedWeb3` to retrieve and use all the extensions added to the page. It has a number of utilities -

- `web3Enable(dappName: string): Promise<InjectedExtension[]>` - to be called before anything else, retrieves the list of all injected extensions/providers
- `web3Enable(dappName: string): Promise<InjectedExtension[]>` - to be called before anything else, retrieves the list of all injected extensions
- `web3Accounts(): Promise<InjectedAccountWithMeta[]>` - returns a list of all the injected accounts, accross all extensions (source in meta)
- `web3AccountsSubscribe(cb: (accounts: InjectedAccountWithMeta[]) => any): Promise<Unsubcall>` - subscribes to the accounts accross all extensions, returning a full list as changes are made
- `web3FromAddress(address: string): Promise<InjectedExtension>` - Retrieves a provider for a specific address
- `web3FromSource(name: string): Promise<InjectedExtension>` - Retrieves a provider identified by the name
- `web3FromAddress(address: string): Promise<InjectedExtension>` - retrieves an extension for a specific address
- `web3FromSource(name: string): Promise<InjectedExtension>` - retrieves an extension identified by the name
- `web3Providers(): Promise<ProvidersWithMeta[]>` - returns a list of all providers (endpoints to be used by the api) across all extensions (source in meta)
- `isWeb3Injected: boolean` - boolean to indicate if `injectedWeb3` was found on the page
- `web3EnablePromise: Promise<InjectedExtension[]> | null` - `null` or the promise as a result of the last call to `web3Enable`

## Usage

```js
import Api from '@polkadot/api/promise';
import { web3Accounts, web3Enable, web3FromAddress } from '@polkadot/extension-dapp';

// returns an array of all the injected sources
// (this needs to be called first, before other requests)
const allInjected = await web3Enable('my cool dapp');

// returns an array of all the providers (api endpoints) available
const allProviders = await web3Providers();

// initialize the api with a provider
const api = new Api(allProviders.length ? allProviders[0].provider : defaultProvider);

// returns an array of { address, meta: { name, source } }
// meta.source contains the name of the extension that provides this account
const allAccounts = await web3Accounts();
Expand Down
36 changes: 30 additions & 6 deletions packages/extension-dapp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { Injected, InjectedAccount, InjectedAccountWithMeta, InjectedExtension, InjectedExtensionInfo, InjectedWindow, Unsubcall } from '@polkadot/extension-inject/types';
import { Injected, InjectedAccount, InjectedAccountWithMeta, InjectedExtension, InjectedExtensionInfo, InjectedWindow, Unsubcall, InjectedProviderWithMeta } from '@polkadot/extension-inject/types';

// our extension adaptor for other kinds of extensions
import compatInjector from './compat';
Expand Down Expand Up @@ -39,7 +39,7 @@ let web3EnablePromise: Promise<InjectedExtension[]> | null = null;

export { isWeb3Injected, web3EnablePromise };

// enables all the providers found on the injected window interface
// enables all the extensions found on the injected window interface
export function web3Enable (originName: string): Promise<InjectedExtension[]> {
web3EnablePromise = compatInjector()
.then((): Promise<InjectedExtension[]> =>
Expand All @@ -65,7 +65,7 @@ export function web3Enable (originName: string): Promise<InjectedExtension[]> {
ext.accounts.get().then(cb).catch(console.error);

return (): void => {
// no ubsubscribe needed, this is a single-shot
// no unsubscribe needed, this is a single-shot
};
};
}
Expand All @@ -89,7 +89,31 @@ export function web3Enable (originName: string): Promise<InjectedExtension[]> {
return web3EnablePromise;
}

// retrieve all the accounts accross all providers
// retrieve all the providers accross all extensions
export async function web3Providers (): Promise<InjectedProviderWithMeta[]> {
if (!web3EnablePromise) {
return throwError('web3Providers');
}

const injected = await web3EnablePromise;
const providers: InjectedProviderWithMeta[] = injected
.map(({ provider, name: source }): InjectedProviderWithMeta | null =>
provider
? { provider, meta: { source } }
: null
)
.filter((x): x is InjectedProviderWithMeta =>
x !== null
);

const sources = providers.map(({ meta: { source } }): string => source);

console.log(`web3Providers: Found ${providers.length} provider${providers.length !== 1 ? 's' : ''} from extension${sources.length !== 1 ? 's' : ''} ${sources.join(', ')}`);

return Promise.resolve(providers);
}

// retrieve all the accounts accross all extensions
export async function web3Accounts (): Promise<InjectedAccountWithMeta[]> {
if (!web3EnablePromise) {
return throwError('web3Accounts');
Expand Down Expand Up @@ -150,7 +174,7 @@ export async function web3AccountsSubscribe (cb: (accounts: InjectedAccountWithM
};
}

// find a specific provider based on the name
// find a specific extension based on the name
export async function web3FromSource (source: string): Promise<InjectedExtension> {
if (!web3EnablePromise) {
return throwError('web3FromSource');
Expand All @@ -166,7 +190,7 @@ export async function web3FromSource (source: string): Promise<InjectedExtension
return found;
}

// find a specific provider based on an address
// find a specific extension based on an address
export async function web3FromAddress (address: string): Promise<InjectedExtension> {
if (!web3EnablePromise) {
return throwError('web3FromAddress');
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-inject/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @polkadot/extension-inject

This is a basic extension injector that manages access to the global objects available. As an extension developer, you don't need to manage access to the windo object manually, by just calling enable here, the global object is setup and managed properly. From here any dapp can access it with the `@polkadot/extension-dapp` package;
This basic extension injector lets extension developers manage the global objects available to dapps, without the need to access the window object manually. By just calling enable on this package, the global object is setup and managed properly. From that point on, any dapp can access it with the [`@polkadot/extension-dapp`](../extension-dapp) package.

## Usage

Expand Down
3 changes: 2 additions & 1 deletion packages/extension-inject/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"license": "Apache-2",
"peerDependencies": {
"@babel/runtime": "^7.5.4",
"@polkadot/api": "*"
"@polkadot/api": "*",
"@polkadot/rpc-provider": "*"
}
}
11 changes: 11 additions & 0 deletions packages/extension-inject/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.

import { Signer } from '@polkadot/api/types';
import { ProviderInterface } from '@polkadot/rpc-provider/types';

export type Unsubcall = () => void;

Expand All @@ -27,6 +28,15 @@ export interface InjectedAccounts {

export type InjectedSigner = Signer;

export type InjectedProvider = ProviderInterface;

export interface InjectedProviderWithMeta {
provider: InjectedProvider;
meta: {
source: string;
};
}

export interface InjectedExtensionInfo {
name: string;
version: string;
Expand All @@ -35,6 +45,7 @@ export interface InjectedExtensionInfo {
export interface Injected {
accounts: InjectedAccounts;
signer: InjectedSigner;
provider?: InjectedProvider;
}

export interface InjectedWindowProvider {
Expand Down