From e2045b46aff7857128e672db7ed3ee5f36683424 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 16 Jan 2020 17:27:48 +0100 Subject: [PATCH 1/8] update README and deprecate individual connectors --- README.md | 314 +++++++++++++++++---------------------------------- src/index.ts | 2 - 2 files changed, 104 insertions(+), 212 deletions(-) diff --git a/README.md b/README.md index 3938edb658..5146e08b55 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ yarn add @walletconnect/web3-provider @portis/web3 fortmatic squarelink @torusla - [React Button](#React-Button) - [Core Module](#Core-Module) -- [Individual Connectors](#Individual-Connectors) ### React Button @@ -48,64 +47,12 @@ Add Web3Connect Button to your React App as follows ```js import Web3Connect from "web3connect"; -import WalletConnectProvider from "@walletconnect/web3-provider"; -import Portis from "@portis/web3"; -import Fortmatic from "fortmatic"; -import Squarelink from "squarelink"; -import Torus from "@toruslabs/torus-embed"; -import Arkane from "@arkane-network/web3-arkane-provider"; -import Authereum from "authereum"; + +const providerOptions = { /* See Provider Options Section */ } { const web3 = new Web3(provider); // add provider to web3 }} @@ -129,56 +76,13 @@ import Torus from "@toruslabs/torus-embed"; import Arkane from "@arkane-network/web3-arkane-provider"; import Authereum from "authereum"; +const providerOptions = { + /* See Provider Options Section */ +}; + const web3Connect = new Web3Connect.Core({ network: "mainnet", // optional - providerOptions: { - walletconnect: { - package: WalletConnectProvider, // required - options: { - infuraId: "INFURA_ID" // required - } - }, - portis: { - package: Portis, // required - options: { - id: "PORTIS_ID" // required - } - }, - fortmatic: { - package: Fortmatic, // required - options: { - key: "FORTMATIC_KEY" // required - } - }, - squarelink: { - package: Squarelink, // required - options: { - id: "SQUARELINK_ID" // required - } - }, - torus: { - package: Torus, // required - options: { - enableLogging: false, // optional - buttonPosition: "bottom-left", // optional - buildEnv: "production", // optional - showTorusButton: true, // optional - enabledVerifiers: { // optional - google: false // optional - } - } - }, - arkane: { - package: Arkane, // required - options: { - clientId: "ARKANE_CLIENT_ID" // required, replace - } - }, - authereum: { - package: Authereum, // required - options: {} - } - } + providerOptions: providerOptions }); // subscribe to connect @@ -194,68 +98,6 @@ web3Connect.on("close", () => { web3Connect.toggleModal(); // open modal on button click ``` -### Individual Connectors - -Add individual connectors for each provider to your own UI (no modal provided) - -```js -import Web3Connect from "web3connect"; -import WalletConnectProvider from "@walletconnect/web3-provider"; -import Portis from "@portis/web3"; -import Fortmatic from "fortmatic"; -import Squarelink from "squarelink"; -import Torus from "@toruslabs/torus-embed"; -import Authereum from "authereum"; - -// For inject providers in dapp browsers -const provider = await Web3Connect.ConnectToInjected(); - -// For WalletConnect -const provider = await Web3Connect.ConnectToWalletConnect( - WalletConnectProvider, - { - infuraId: "INFURA_ID", // required - bridge: "https://bridge.walletconnect.org" // optional - } -); - -// For Portis -const provider = await Web3Connect.ConnectToPortis(Portis, { - id: "PORTIS_ID", // required - network: "mainnet" // optional -}); - -// For Fortmatic -const provider = await Web3Connect.ConnectToFortmatic(Fortmatic, { - key: "FORTMATIC_KEY", // required - network: "mainnet" // optional -}); - -// For Squarelink -const provider = await Web3Connect.ConnectToSquarelink(Squarelink, { - id: "SQUARELINK_ID", // required - network: "mainnet" // optional -}); - -// For Torus -const provider = await Web3Connect.ConnectToTorus(Torus, { - enableLogging: false, // optional - buttonPosition: "bottom-left", // optional - buildEnv: "production", // optional - showTorusButton: true // optional -}); - -// For Arkane -const provider = await Web3Connect.ConnectToArkane(Arkane, { - key: "ARKANE_CLIENT_ID", // required - environment: "staging" // optional - -// For Authereum -const provider = await Web3Connect.ConnectToAuthereum(Authereum, { - network: "mainnet" // optional -}); -``` - ## Utils ```typescript @@ -298,67 +140,119 @@ interface IProviderCallback { } ``` -## Options +## Provider Options -- providerOptions (optional): An object mapping arbitrary string that adds the required configuration to multiple web3 providers. +These are all the providers available with Web3Connect and how to configure their provider options - - walletconnect: +### WalletConnect - - package: dependency injection to enable provider - - options: - - infuraId: the infura app ID registered (required) - - bridge: bridge url (optional) +```typescript +import WalletConnectProvider from "@walletconnect/web3-provider"; - - portis: +const providerOptions = { + walletconnect: { + package: WalletConnectProvider, // required + options: { + infuraId: "INFURA_ID" // required + } + } +}; +``` - - package: dependency injection to enable provider - - options: - - id: the app id registered (required) - - network: choose initial network name (optional) - - config: additional configuration, like support of Gas Station Network (optional) +### Portis - - fortmatic: +```typescript +import Portis from "@portis/web3"; - - package: dependency injection to enable provider - - options: - - key: the secret key (required) - - network: choose initial network name (optional) +const providerOptions = { + portis: { + package: Portis, // required + options: { + id: "PORTIS_ID" // required + } + } +}; +``` - - arkane: +### Fortmatic - - package: dependency injection to enable provider - - options: - - clientId: the client id used by the application (required) - - nodeUrl: choose initial network name (optional) - - environment: the environment to connect to (optional). Production by default, use 'staging' for testing +```typescript +import Fortmatic from "fortmatic"; - - squarelink: +const providerOptions = { + fortmatic: { + package: Fortmatic, // required + options: { + key: "FORTMATIC_KEY" // required + } + } +}; +``` - - package: dependency injection to enable provider - - options: - - id: the client ID registered (required) - - network: choose initial network name (optional) - - config: additional configuration, like `scope` to use supplemental methods (optional) +### Squarelink -- torus: +```typescript +import Squarelink from "squarelink"; - - package: dependency injection to enable provider - - options: - - enableLogging: enable logging for debugging (optional), - - buttonPosition: set button position (optional), - - buildEnv: set build environment (optional), - - showTorusButton: enable displaying torus button - - enabledVerifiers: disable certain verifiers by passing false against them +const providerOptions = { + squarelink: { + package: Squarelink, // required + options: { + id: "SQUARELINK_ID" // required + } + } +}; +``` + +### Torus + +```typescript +import Torus from "@toruslabs/torus-embed"; + +const providerOptions = { + torus: { + package: Torus, // required + options: { + enableLogging: false, // optional + buttonPosition: "bottom-left", // optional + buildEnv: "production", // optional + showTorusButton: true, // optional + enabledVerifiers: { + // optional + google: false // optional + } + } + } +}; +``` -- authereum: +### Arkane - - package: dependency injection to enable provider - - options: - - network: choose initial network name (optional) +```typescript +import Arkane from "@arkane-network/web3-arkane-provider"; + +const providerOptions = { + arkane: { + package: Arkane, // required + options: { + clientId: "ARKANE_CLIENT_ID" // required, replace + } + } +}; +``` -You can disable the injected provider by adding the following flag: +### Authereum -- disableInjectedProvider: true (optional) +```typescript +import Authereum from "authereum"; + +const providerOptions = { + authereum: { + package: Authereum, // required + options: {} + } +}; +``` ## Collaboration diff --git a/src/index.ts b/src/index.ts index 513250fd35..678ef157d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,11 @@ import ConnectButton from "./components/ConnectButton"; import Core from "./core"; -import connectors from "./core/connectors"; import * as utils from "./helpers/utils"; import * as types from "./helpers/types"; export default { Button: ConnectButton, Core, - ...connectors, ...utils, ...types }; From 5a0b4cbfa392746d0a3178b6648875d9d1b53c37 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 16 Jan 2020 17:29:15 +0100 Subject: [PATCH 2/8] update README --- README.md | 84 +++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5146e08b55..f370b323ac 100644 --- a/README.md +++ b/README.md @@ -98,48 +98,6 @@ web3Connect.on("close", () => { web3Connect.toggleModal(); // open modal on button click ``` -## Utils - -```typescript -function checkInjectedProviders(): IInjectedProvidersMap; -function getInjectedProviderName(): string | null; -function getProviderInfoByName(name: string | null): IProviderInfo; -function getProviderInfo(provider: any): IProviderInfo; -function isMobile(): boolean; -function formatProviderDescription(providerInfo: IProviderInfo); -``` - -## Types - -```typescript -interface IProviderInfo { - name: string; - type: string; - logo: string; - check: string; - styled: { - [prop: string]: any; - }; -} - -interface IProviderOptions { - [providerName: string]: { - package: any; - options: any; - }; -} - -interface IInjectedProvidersMap { - injectedAvailable: boolean; - [isProviderName: string]: boolean; -} - -interface IProviderCallback { - name: string | null; - onClick: () => Promise; -} -``` - ## Provider Options These are all the providers available with Web3Connect and how to configure their provider options @@ -254,6 +212,48 @@ const providerOptions = { }; ``` +## Utils + +```typescript +function checkInjectedProviders(): IInjectedProvidersMap; +function getInjectedProviderName(): string | null; +function getProviderInfoByName(name: string | null): IProviderInfo; +function getProviderInfo(provider: any): IProviderInfo; +function isMobile(): boolean; +function formatProviderDescription(providerInfo: IProviderInfo); +``` + +## Types + +```typescript +interface IProviderInfo { + name: string; + type: string; + logo: string; + check: string; + styled: { + [prop: string]: any; + }; +} + +interface IProviderOptions { + [providerName: string]: { + package: any; + options: any; + }; +} + +interface IInjectedProvidersMap { + injectedAvailable: boolean; + [isProviderName: string]: boolean; +} + +interface IProviderCallback { + name: string | null; + onClick: () => Promise; +} +``` + ## Collaboration ### Code contributions are welcome ❤️❤️❤️! From 6e0a535eb8f5d4585fd2dda44eff59f4b99caf41 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 16 Jan 2020 17:29:46 +0100 Subject: [PATCH 3/8] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35bc132dac..a794a6da4b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "web3connect", - "version": "1.0.0-beta.25", + "version": "1.0.0-beta.26", "description": "A single Web3 / Ethereum provider solution for all Wallets", "keywords": [ "web3", From 1ee50ebe34a69295af02f7d436c15613992dfb4f Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 16 Jan 2020 17:32:51 +0100 Subject: [PATCH 4/8] expose libraries inside providers --- src/core/connectors/authereum.ts | 1 + src/core/connectors/fortmatic.ts | 1 + src/core/connectors/squarelink.ts | 1 + src/core/connectors/torus.ts | 4 +++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/connectors/authereum.ts b/src/core/connectors/authereum.ts index ace08b2113..11fd4779ce 100644 --- a/src/core/connectors/authereum.ts +++ b/src/core/connectors/authereum.ts @@ -7,6 +7,7 @@ const ConnectToAuthereum = (Authereum: any, opts: IAuthereumConnectorOptions) => try { const authereum = new Authereum(opts.network); const provider = authereum.getProvider(); + provider.authereum = authereum await provider.enable(); resolve(provider); } catch (error) { diff --git a/src/core/connectors/fortmatic.ts b/src/core/connectors/fortmatic.ts index 8cbe10d81e..f967fd75ee 100644 --- a/src/core/connectors/fortmatic.ts +++ b/src/core/connectors/fortmatic.ts @@ -12,6 +12,7 @@ const ConnectToFortmatic = async ( const key = opts.key; const fm = new Fortmatic(key, opts.network); const provider = await fm.getProvider(); + provider.fm = fm await fm.user.login(); const isLoggedIn = await fm.user.isLoggedIn(); if (isLoggedIn) { diff --git a/src/core/connectors/squarelink.ts b/src/core/connectors/squarelink.ts index 041a479ce4..0c661e3764 100644 --- a/src/core/connectors/squarelink.ts +++ b/src/core/connectors/squarelink.ts @@ -25,6 +25,7 @@ const ConnectToSquarelink = ( const config = opts.config; const sqlk = new Squarelink(id, network, config); const provider = await sqlk.getProvider(); + provider.sqlk = sqlk await provider.enable(); return resolve(provider); } catch (error) { diff --git a/src/core/connectors/torus.ts b/src/core/connectors/torus.ts index 92f556fa6d..c19d6a56f5 100644 --- a/src/core/connectors/torus.ts +++ b/src/core/connectors/torus.ts @@ -82,7 +82,9 @@ const ConnectToTorus = async (Torus: any, opts: ITorusConnectorOptions) => { enabledVerifiers: enabledVerifiers }); await torus.login(); // await torus.ethereum.enable() - resolve(torus.provider); + const provider = torus.provider; + provider.torus = torus + resolve(provider); } catch (err) { reject(err) } From d273264bed44e290eb2a3a383dcff03beaeb4126 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 16 Jan 2020 17:36:01 +0100 Subject: [PATCH 5/8] updated authereum and arkana in example --- example/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/package.json b/example/package.json index 16158328b4..053c452986 100644 --- a/example/package.json +++ b/example/package.json @@ -3,11 +3,11 @@ "version": "0.1.0", "private": true, "dependencies": { - "@arkane-network/web3-arkane-provider": "0.2.5", + "@arkane-network/web3-arkane-provider": "^0.2.5", "@portis/web3": "^2.0.0-beta.40", "@toruslabs/torus-embed": "^0.2.6", "@walletconnect/web3-provider": "^1.0.0-beta.42", - "authereum": "0.0.4-beta.33", + "authereum": "^0.0.4-beta.68", "axios": "^0.18.0", "bignumber.js": "^8.1.1", "ethereumjs-util": "^6.1.0", From c857dee16c2484d5a0f049e566665afc92c57132 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 16 Jan 2020 18:20:13 +0100 Subject: [PATCH 6/8] localStorage helpers --- src/helpers/local.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/helpers/local.ts diff --git a/src/helpers/local.ts b/src/helpers/local.ts new file mode 100644 index 0000000000..52e45855b0 --- /dev/null +++ b/src/helpers/local.ts @@ -0,0 +1,44 @@ +export let local: Storage; + +if ( + typeof window !== "undefined" && + typeof window.localStorage !== "undefined" +) { + local = window.localStorage; +} + +export const setLocal = (key: string, data: any) => { + const jsonData = JSON.stringify(data); + if (local) { + local.setItem(key, jsonData); + } +}; + +export const getLocal = (key: string) => { + let data = null; + let raw = null; + if (local) { + raw = local.getItem(key); + } + if (raw && typeof raw === "string") { + try { + data = JSON.parse(raw); + } catch (error) { + return null; + } + } + return data; +}; + +export const removeLocal = (key: string) => { + if (local) { + local.removeItem(key); + } +}; + +export const updateLocal = (key: string, data: any) => { + const localData = getLocal(key) || {}; + const mergedData = { ...localData, ...data }; + setLocal(key, mergedData); +}; + From f93f9481a629bb7a42e3785d97d830ef072c37bb Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 16 Jan 2020 18:43:01 +0100 Subject: [PATCH 7/8] cache preferred provider --- README.md | 7 ++ src/components/ConnectButton.tsx | 1 + src/core/index.tsx | 109 +++++++++++++++++++++---------- src/helpers/constants.ts | 6 ++ 4 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 src/helpers/constants.ts diff --git a/README.md b/README.md index f370b323ac..3c6c7b707e 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,13 @@ interface IProviderCallback { } ``` +## Optional Flags + +You can enable the following optional flags: + +- disableInjectedProvider: disable displaying injected provider as an option +- disablePreferredProvider: disable caching preffered provider in localStorage + ## Collaboration ### Code contributions are welcome ❤️❤️❤️! diff --git a/src/components/ConnectButton.tsx b/src/components/ConnectButton.tsx index 733440ffb1..6730981ea5 100644 --- a/src/components/ConnectButton.tsx +++ b/src/components/ConnectButton.tsx @@ -8,6 +8,7 @@ class ConnectButton extends React.Component { constructor(props: any) { super(props); this.core = new Core({ + disablePreferredProvider: props.disablePreferredProvider, network: props.network, lightboxOpacity: props.lightboxOpacity, providerOptions: props.providerOptions diff --git a/src/core/index.tsx b/src/core/index.tsx index f05b1da117..92e401f7b0 100644 --- a/src/core/index.tsx +++ b/src/core/index.tsx @@ -6,11 +6,17 @@ import { isMobile, getInjectedProviderName } from "../helpers/utils"; import connectors from "./connectors"; import EventManager from "./events"; import { providerPackages } from "../providers"; - -const WEB3_CONNECT_MODAL_ID = "WEB3_CONNECT_MODAL_ID"; +import { setLocal, removeLocal, getLocal } from "../helpers/local"; +import { + WEB3_CONNECT_MODAL_ID, + PREFERRED_PROVIDER_KEY, + CONNECT_EVENT, + DISCONNECT_EVENT, + CLOSE_EVENT +} from "../helpers/constants"; interface ICoreOptions { - modal?: boolean; + disablePreferredProvider?: boolean; network?: string; lightboxOpacity?: number; providerOptions?: IProviderOptions; @@ -22,48 +28,65 @@ class Core { private show: boolean = INITIAL_STATE.show; private eventManager: EventManager = new EventManager(); private injectedProvider: string | null = null; + private disablePreferredProvider: boolean = false; private network: string = ""; private lightboxOpacity: number = 0.4; private providerOptions: IProviderOptions = {}; private providers: IProviderCallback[]; + private preferredProvider: string | undefined; constructor(opts?: ICoreOptions) { this.injectedProvider = getInjectedProviderName(); if (opts) { + this.disablePreferredProvider = opts.disablePreferredProvider || false; this.network = opts.network || ""; this.lightboxOpacity = opts.lightboxOpacity || 0.4; this.providerOptions = opts.providerOptions || {}; } + this.preferredProvider = getLocal(PREFERRED_PROVIDER_KEY) || undefined; this.providers = this.getProviders(); this.renderModal(); } + // --------------- PUBLIC METHODS --------------- // + public on(event: string, callback: (result: any) => void): () => void { this.eventManager.on({ event, callback }); - return () => this.eventManager.off({ - event, - callback - }) + return () => + this.eventManager.off({ + event, + callback + }); } public off(event: string, callback?: (result: any) => void): void { this.eventManager.off({ event, callback - }) + }); + } + + public setPreferredProvider(name: string) { + this.preferredProvider = name; + setLocal(PREFERRED_PROVIDER_KEY, name); + } + + public clearPreferredProvider() { + this.preferredProvider = undefined; + removeLocal(PREFERRED_PROVIDER_KEY); } public connectToInjected = async () => { try { const provider = await connectors.ConnectToInjected(); - await this.onConnect(provider); + await this.onConnect(provider, "injected"); } catch (error) { await this.onError(error); } @@ -92,17 +115,22 @@ class Core { const provider = await connector(providerPackage, opts); if (provider.isWalletConnect) { // Listen for Disconnect event - provider.wc.on("disconnect", async () => { - return this.onDisconnect() + provider.wc.on(DISCONNECT_EVENT, async () => { + return this.onDisconnect(); }); } - await this.onConnect(provider); + await this.onConnect(provider, name); } catch (error) { await this.onError(error); } }; public toggleModal = async () => { + if (this.preferredProvider) { + const provider = this.getProvider(this.preferredProvider); + await provider.onClick(); + return; + } if ( this.providers && this.providers.length === 1 && @@ -124,11 +152,25 @@ class Core { await this.updateState({ show: !this.show }); }; - private onDisconnect = async () => { - this.eventManager.trigger("disconnect") + public renderModal() { + const el = document.createElement("div"); + el.id = WEB3_CONNECT_MODAL_ID; + document.body.appendChild(el); + + ReactDOM.render( + , + document.getElementById(WEB3_CONNECT_MODAL_ID) + ); } - public shouldDisplayProvider(name: string) { + // --------------- PRIVATE METHODS --------------- // + + private shouldDisplayProvider(name: string) { const { providerOptions } = this; const providerPackage = providerPackages[name]; @@ -158,7 +200,7 @@ class Core { return false; } - public getProviders = () => { + private getProviders = () => { const mobile = isMobile(); let providers = [ @@ -275,6 +317,12 @@ class Core { return providersMap; }; + private getProvider = (name: string) => { + const providers = this.getProviders(); + const provider = providers.filter(x => x.name === name)[0]; + return provider; + }; + private onError = async (error: any) => { if (this.show) { await this.toggleModal(); @@ -282,18 +330,25 @@ class Core { this.eventManager.trigger("error", error); }; - private onConnect = async (provider: any) => { + private onConnect = async (provider: any, name: string) => { if (this.show) { await this.toggleModal(); } - this.eventManager.trigger("connect", provider); + if (this.disablePreferredProvider) { + this.setPreferredProvider(name); + } + this.eventManager.trigger(CONNECT_EVENT, provider); + }; + + private onDisconnect = async () => { + this.eventManager.trigger(DISCONNECT_EVENT); }; private onClose = async () => { if (this.show) { await this.toggleModal(); } - this.eventManager.trigger("close"); + this.eventManager.trigger(CLOSE_EVENT); }; private updateState = async (state: any) => { @@ -304,22 +359,6 @@ class Core { }; private resetState = () => this.updateState({ ...INITIAL_STATE }); - - public renderModal() { - const el = document.createElement("div"); - el.id = WEB3_CONNECT_MODAL_ID; - document.body.appendChild(el); - - ReactDOM.render( - , - document.getElementById(WEB3_CONNECT_MODAL_ID) - ); - } } export default Core; diff --git a/src/helpers/constants.ts b/src/helpers/constants.ts new file mode 100644 index 0000000000..6315f52adc --- /dev/null +++ b/src/helpers/constants.ts @@ -0,0 +1,6 @@ +export const WEB3_CONNECT_MODAL_ID = "WEB3_CONNECT_MODAL_ID"; +export const PREFERRED_PROVIDER_KEY = "WEB3_CONNECT_PREFERRED_PROVIDER"; + +export const CONNECT_EVENT = "connect"; +export const DISCONNECT_EVENT = "disconnect"; +export const CLOSE_EVENT = "close"; From b3c71375e8021094fe6b84ea801f9130c08d052c Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sat, 18 Jan 2020 14:10:02 +0100 Subject: [PATCH 8/8] export providers --- src/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 678ef157d2..21e11a69ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,13 @@ -import ConnectButton from "./components/ConnectButton"; +import Button from "./components/ConnectButton"; import Core from "./core"; +import providers from "./providers"; import * as utils from "./helpers/utils"; import * as types from "./helpers/types"; export default { - Button: ConnectButton, + Button, Core, + providers, ...utils, ...types };