From 2e36fb59f913646c2c4dac0c9d53bd1d282fdc49 Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Tue, 25 Jan 2022 15:09:12 -0500 Subject: [PATCH] add tsdoc --- .eslintrc.js | 3 +- README.md | 44 ++++++++++---------- package.json | 3 +- packages/core/src/index.ts | 41 +++++++++++------- packages/eip1193/src/index.ts | 6 +++ packages/empty/src/index.ts | 12 ++++-- packages/example/chains.ts | 17 ++++---- packages/example/connectors/walletConnect.ts | 4 +- packages/metamask/src/index.ts | 13 ++++++ packages/network/src/index.ts | 10 +++++ packages/store/src/index.ts | 20 ++++++++- packages/types/src/index.ts | 40 +++++++++++++----- packages/url/src/index.ts | 6 +++ packages/walletconnect/src/index.ts | 11 ++++- packages/walletlink/src/index.ts | 13 +++++- yarn.lock | 42 +++++++++++++++++-- 16 files changed, 217 insertions(+), 68 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index c706bef56..e5d1552b7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { tsconfigRootDir: __dirname, project: ['./tsconfig.json', './packages/*/tsconfig.json'], }, - plugins: ['@typescript-eslint', 'react-hooks'], + plugins: ['@typescript-eslint', 'react-hooks', 'eslint-plugin-tsdoc'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', @@ -14,6 +14,7 @@ module.exports = { rules: { 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', + 'tsdoc/syntax': 'warn', }, env: { browser: true, diff --git a/README.md b/README.md index cec68e6e7..d66055259 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,29 @@ # web3-react (beta) -[![CI](https://github.com/NoahZinsmeister/web3-react/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/NoahZinsmeister/web3-react/actions/workflows/CI.yml) +[![CI](https://github.com/NoahZinsmeister/web3-react/actions/workflows/CI.yml/badge.svg)](https://github.com/NoahZinsmeister/web3-react/actions/workflows/CI.yml) ## [Example](https://web3-react-mu.vercel.app/) +This is a hosted version of [packages/example](packages/example). + ## Packages -| Package | Version | Size | Description | -|-------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------| -| [`@web3-react/types`](packages/types) | [![npm](https://img.shields.io/npm/v/@web3-react/types/beta.svg)](https://www.npmjs.com/package/@web3-react/types/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/types/beta.svg)](https://bundlephobia.com/result?p=@web3-react/types@beta) | | -| [`@web3-react/store`](packages/store) | [![npm](https://img.shields.io/npm/v/@web3-react/store/beta.svg)](https://www.npmjs.com/package/@web3-react/store/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/store/beta.svg)](https://bundlephobia.com/result?p=@web3-react/store@beta) | | -| [`@web3-react/core`](packages/core) | [![npm](https://img.shields.io/npm/v/@web3-react/core/beta.svg)](https://www.npmjs.com/package/@web3-react/core/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/core/beta.svg)](https://bundlephobia.com/result?p=@web3-react/core@beta) | | -| **Connectors** | | | | -| [`@web3-react/eip1193`](packages/eip1193) | [![npm](https://img.shields.io/npm/v/@web3-react/eip1193/beta.svg)](https://www.npmjs.com/package/@web3-react/eip1193/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/eip1193/beta.svg)](https://bundlephobia.com/result?p=@web3-react/eip1193@beta) | | -| [`@web3-react/empty`](packages/empty) | [![npm](https://img.shields.io/npm/v/@web3-react/empty/beta.svg)](https://www.npmjs.com/package/@web3-react/empty/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/empty/beta.svg)](https://bundlephobia.com/result?p=@web3-react/empty@beta) | | -| [`@web3-react/metamask`](packages/metamask) | [![npm](https://img.shields.io/npm/v/@web3-react/metamask/beta.svg)](https://www.npmjs.com/package/@web3-react/metamask/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/metamask/beta.svg)](https://bundlephobia.com/result?p=@web3-react/metamask@beta) | [MetaMask](https://metamask.io/) | -| [`@web3-react/network`](packages/network) | [![npm](https://img.shields.io/npm/v/@web3-react/network/beta.svg)](https://www.npmjs.com/package/@web3-react/network/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/network/beta.svg)](https://bundlephobia.com/result?p=@web3-react/network@beta) | | -| [`@web3-react/url`](packages/url) | [![npm](https://img.shields.io/npm/v/@web3-react/url/beta.svg)](https://www.npmjs.com/package/@web3-react/url/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/url/beta.svg)](https://bundlephobia.com/result?p=@web3-react/url@beta) | | -| [`@web3-react/walletconnect`](packages/walletconnect) | [![npm](https://img.shields.io/npm/v/@web3-react/walletconnect/beta.svg)](https://www.npmjs.com/package/@web3-react/walletconnect/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/walletconnect/beta.svg)](https://bundlephobia.com/result?p=@web3-react/walletconnect@beta) | [WalletConnect](https://walletconnect.org/) | -| [`@web3-react/walletlink`](packages/walletlink) | [![npm](https://img.shields.io/npm/v/@web3-react/walletlink/beta.svg)](https://www.npmjs.com/package/@web3-react/walletlink/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/walletlink/beta.svg)](https://bundlephobia.com/result?p=@web3-react/walletlink@beta) | [WalletLink](https://walletlink.org/#/) | -| **Experimental Connectors** | | | Not stable | -| [`@web3-react/frame`](packages/frame) | [![npm](https://img.shields.io/npm/v/@web3-react/frame/beta.svg)](https://www.npmjs.com/package/@web3-react/frame/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/frame/beta.svg)](https://bundlephobia.com/result?p=@web3-react/frame@beta) | [Frame](https://frame.sh/) | -| [`@web3-react/magic`](packages/magic) | [![npm](https://img.shields.io/npm/v/@web3-react/magic/beta.svg)](https://www.npmjs.com/package/@web3-react/magic/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/magic/beta.svg)](https://bundlephobia.com/result?p=@web3-react/magic@beta) | [Magic](https://magic.link/) | +| Package | Version | Size | Link | +|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------| +| [`@web3-react/types`](packages/types) | [![npm](https://img.shields.io/npm/v/@web3-react/types/beta.svg)](https://www.npmjs.com/package/@web3-react/types/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/types/beta.svg)](https://bundlephobia.com/result?p=@web3-react/types@beta) | | +| [`@web3-react/store`](packages/store) | [![npm](https://img.shields.io/npm/v/@web3-react/store/beta.svg)](https://www.npmjs.com/package/@web3-react/store/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/store/beta.svg)](https://bundlephobia.com/result?p=@web3-react/store@beta) | | +| [`@web3-react/core`](packages/core) | [![npm](https://img.shields.io/npm/v/@web3-react/core/beta.svg)](https://www.npmjs.com/package/@web3-react/core/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/core/beta.svg)](https://bundlephobia.com/result?p=@web3-react/core@beta) | | +| **Connectors** | | | | +| [`@web3-react/eip1193`](packages/eip1193) | [![npm](https://img.shields.io/npm/v/@web3-react/eip1193/beta.svg)](https://www.npmjs.com/package/@web3-react/eip1193/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/eip1193/beta.svg)](https://bundlephobia.com/result?p=@web3-react/eip1193@beta) | [EIP-1193](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md) | +| [`@web3-react/empty`](packages/empty) | [![npm](https://img.shields.io/npm/v/@web3-react/empty/beta.svg)](https://www.npmjs.com/package/@web3-react/empty/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/empty/beta.svg)](https://bundlephobia.com/result?p=@web3-react/empty@beta) | | +| [`@web3-react/metamask`](packages/metamask) | [![npm](https://img.shields.io/npm/v/@web3-react/metamask/beta.svg)](https://www.npmjs.com/package/@web3-react/metamask/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/metamask/beta.svg)](https://bundlephobia.com/result?p=@web3-react/metamask@beta) | [MetaMask](https://metamask.io/) | +| [`@web3-react/network`](packages/network) | [![npm](https://img.shields.io/npm/v/@web3-react/network/beta.svg)](https://www.npmjs.com/package/@web3-react/network/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/network/beta.svg)](https://bundlephobia.com/result?p=@web3-react/network@beta) | | +| [`@web3-react/url`](packages/url) | [![npm](https://img.shields.io/npm/v/@web3-react/url/beta.svg)](https://www.npmjs.com/package/@web3-react/url/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/url/beta.svg)](https://bundlephobia.com/result?p=@web3-react/url@beta) | | +| [`@web3-react/walletconnect`](packages/walletconnect) | [![npm](https://img.shields.io/npm/v/@web3-react/walletconnect/beta.svg)](https://www.npmjs.com/package/@web3-react/walletconnect/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/walletconnect/beta.svg)](https://bundlephobia.com/result?p=@web3-react/walletconnect@beta) | [WalletConnect](https://walletconnect.org/) | +| [`@web3-react/walletlink`](packages/walletlink) | [![npm](https://img.shields.io/npm/v/@web3-react/walletlink/beta.svg)](https://www.npmjs.com/package/@web3-react/walletlink/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/walletlink/beta.svg)](https://bundlephobia.com/result?p=@web3-react/walletlink@beta) | [WalletLink](https://walletlink.org/#/) | +| **Experimental Connectors** | | | Not stable | +| [`@web3-react/frame`](packages/frame) | [![npm](https://img.shields.io/npm/v/@web3-react/frame/beta.svg)](https://www.npmjs.com/package/@web3-react/frame/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/frame/beta.svg)](https://bundlephobia.com/result?p=@web3-react/frame@beta) | [Frame](https://frame.sh/) | +| [`@web3-react/magic`](packages/magic) | [![npm](https://img.shields.io/npm/v/@web3-react/magic/beta.svg)](https://www.npmjs.com/package/@web3-react/magic/v/beta) | [![minzip](https://img.shields.io/bundlephobia/minzip/@web3-react/magic/beta.svg)](https://bundlephobia.com/result?p=@web3-react/magic@beta) | [Magic](https://magic.link/) | ## Getting Started @@ -30,7 +32,7 @@ - `yarn bootstrap` - `yarn start` -In addition to compiling each package in watch mode, this will also spin up the example app on [http://localhost:3000/](http://localhost:3000/). +In addition to compiling each package in watch mode, this will also spin up [packages/example](packages/example) on [localhost:3000](http://localhost:3000/). ## Running Tests @@ -38,15 +40,15 @@ In addition to compiling each package in watch mode, this will also spin up the ## Documentation -This version of web3-react is still in beta, so unfortunately documentation is pretty sparse at the moment. The example repository and the source code itself are your best bets to get an idea of what's going on. More thorough documentation is a priority as development continues, however! +This version of web3-react is still in beta, so unfortunately documentation is pretty sparse at the moment. The [packages/example](packages/example), TSDoc comments, and the source itself are the best ways to get an idea of what's going on. More thorough documentation is a priority as development continues! ## Adding Connectors -If you're interested in using web3-react with a particular wallet solution that doesn't have an "official" connector package, you're in luck! This library was specifically written to be extremely modular, and you should be able to draw inspiration from the existing connectors to write your own! That code can live inside your codebase, or even be published as a standalone package. From time to time, if there's sufficient interest and desire, PRs adding new connectors may be accepted, but it's probably worth bringing up in an issue for discussion beforehand. +If you're interested in using web3-react with a wallet that doesn't have an "official" connector package, you're in luck! This library was designed to be highly modular, and you should be able to draw inspiration from the existing connectors to write your own! That code can live inside your codebase, or even be published as a standalone package. From time to time, if there's sufficient interest and desire, PRs adding new connectors may be accepted, but this should be brought up in an issue or discussion beforehand. ## Upgrading from v6 -While the internals of web3-react have changed fairly dramatically between v6 and v8, the hope is that usage don't have to change too much when upgrading. Once you've migrated to the new connectors and state management patterns, you should be able to use the hooks defined in @web3-react/core, in particular `useWeb3React`, as more-or-less drop-in replacements for the v6 hooks. The big benefit in v8 is that hooks are now per-connector, as opposed to global, so no more juggling between connectors/multiple roots! Instead, you should be able to use a higher-level hook that selects amongst all connectors based on whether they're active, have errors, etc. and return the appropriate provider/account/etc. for usage elsewhere in your application. +While the internals of web3-react have changed fairly dramatically between v6 and v8, the hope is that usage don't have to change too much when upgrading. Once you've migrated to the new connectors and state management patterns, you should be able to use the hooks defined in @web3-react/core, in particular `useWeb3React` (or `usePriorityWeb3React`), as more or less drop-in replacements for the v6 hooks. The big benefit in v8 is that hooks are now per-connector, as opposed to global, so no more juggling between connectors/multiple roots! ## Useful Commands diff --git a/package.json b/package.json index 5e819bfa6..4c38d4ed6 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "bootstrap": "lerna bootstrap", "clean": "lerna clean --yes", - "lint": "yarn run eslint . --ext .ts,.tsx", + "lint": "yarn run eslint --ext .ts,.tsx .", "test": "jest", "build": "lerna run build", "prestart": "yarn build", @@ -25,6 +25,7 @@ "@walletconnect/ethereum-provider": "^1.7.1", "eslint": "^8.4.1", "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-tsdoc": "^0.2.14", "eth-provider": "^0.9.4", "jest": "^27.2.4", "lerna": "^4.0.0", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ac9c8ab0e..40b1ee701 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -12,6 +12,17 @@ export type Web3ReactHooks = ReturnType & export type Web3ReactPriorityHooks = ReturnType +/** + * Wraps the initialization of a `connector`. Creates a zustand `store` with `actions` bound to it, and then passes + * these to the connector as specified in `f`. Also creates a variety of `hooks` bound to this `store`. + * + * @typeParam T - The type of the `connector` returned from `f`. + * @param f - A function which is called with `actions` bound to the returned `store`. + * @param allowedChainIds - An optional array of chainIds which the `connector` may connect to. If the `connector` is + * connected to a chainId which is not allowed, a ChainIdNotAllowedError error will be reported. + * If this argument is unspecified, the `connector` may connect to any chainId. + * @returns [connector, hooks, store] - The initialized connector, a variety of hooks, and a zustand store. + */ export function initializeConnector( f: (actions: Actions) => T, allowedChainIds?: number[] @@ -32,10 +43,16 @@ function computeIsActive({ chainId, accounts, activating, error }: Web3ReactStat return Boolean(chainId && accounts && !activating && !error) } +/** + * Creates a variety of convenience `hooks` that return data associated with the first of the `initializedConnectors` + * that is active. + * + * @param initializedConnectors - Two or more [connector, hooks] arrays, as returned from initializeConnector. + * @returns hooks - A variety of convenience hooks that wrap the hooks returned from initializeConnector. + */ export function getPriorityConnector(...initializedConnectors: [Connector, Web3ReactHooks][]) { - // the following code calls hooks in a map a lot, which technically violates the eslint rule. - // this is ok, though, because initializedConnectors never changes, so the same number of hooks - // are always called, and they're always the same + // the following code calls hooks in a map a lot, which violates the eslint rule. + // this is ok, though, because initializedConnectors never changes, so the same hooks are called each time function useActiveIndex() { // eslint-disable-next-line react-hooks/rules-of-hooks @@ -51,43 +68,37 @@ export function getPriorityConnector(...initializedConnectors: [Connector, Web3R function usePriorityChainId() { // eslint-disable-next-line react-hooks/rules-of-hooks const values = initializedConnectors.map(([, { useChainId }]) => useChainId()) - const index = useActiveIndex() - return values[index ?? 0] + return values[useActiveIndex() ?? 0] } function usePriorityAccounts() { // eslint-disable-next-line react-hooks/rules-of-hooks const values = initializedConnectors.map(([, { useAccounts }]) => useAccounts()) - const index = useActiveIndex() - return values[index ?? 0] + return values[useActiveIndex() ?? 0] } function usePriorityIsActivating() { // eslint-disable-next-line react-hooks/rules-of-hooks const values = initializedConnectors.map(([, { useIsActivating }]) => useIsActivating()) - const index = useActiveIndex() - return values[index ?? 0] + return values[useActiveIndex() ?? 0] } function usePriorityError() { // eslint-disable-next-line react-hooks/rules-of-hooks const values = initializedConnectors.map(([, { useError }]) => useError()) - const index = useActiveIndex() - return values[index ?? 0] + return values[useActiveIndex() ?? 0] } function usePriorityAccount() { // eslint-disable-next-line react-hooks/rules-of-hooks const values = initializedConnectors.map(([, { useAccount }]) => useAccount()) - const index = useActiveIndex() - return values[index ?? 0] + return values[useActiveIndex() ?? 0] } function usePriorityIsActive() { // eslint-disable-next-line react-hooks/rules-of-hooks const values = initializedConnectors.map(([, { useIsActive }]) => useIsActive()) - const index = useActiveIndex() - return values[index ?? 0] + return values[useActiveIndex() ?? 0] } function usePriorityProvider(network?: Networkish) { diff --git a/packages/eip1193/src/index.ts b/packages/eip1193/src/index.ts index 3555bc109..6be41687e 100644 --- a/packages/eip1193/src/index.ts +++ b/packages/eip1193/src/index.ts @@ -6,8 +6,13 @@ function parseChainId(chainId: string) { } export class EIP1193 extends Connector { + /** {@inheritdoc Connector.provider} */ provider: Provider + /** + * @param provider - An EIP-1193 ({@link https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md}) provider. + * @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed. + */ constructor(actions: Actions, provider: Provider, connectEagerly = true) { super(actions) @@ -43,6 +48,7 @@ export class EIP1193 extends Connector { } } + /** {@inheritdoc Connector.activate} */ public async activate(): Promise { this.actions.startActivation() diff --git a/packages/empty/src/index.ts b/packages/empty/src/index.ts index 687c97a0c..52dc80aee 100644 --- a/packages/empty/src/index.ts +++ b/packages/empty/src/index.ts @@ -1,12 +1,16 @@ import { Connector } from '@web3-react/types' class Empty extends Connector { + /** {@inheritdoc Connector.provider} */ provider: undefined - // eslint-disable-next-line @typescript-eslint/no-empty-function - public activate() {} + /** + * No-op. May be called if it simplifies application code. + */ + public activate() { + void 0 + } } -// @ts-expect-error this is okay because actions aren't ever validated, -// and they're only used to set a protected property +// @ts-expect-error actions aren't validated and are only used to set a protected property, so this is ok export const EMPTY = new Empty() diff --git a/packages/example/chains.ts b/packages/example/chains.ts index 5d03c4900..03cb3d476 100644 --- a/packages/example/chains.ts +++ b/packages/example/chains.ts @@ -134,12 +134,15 @@ export const CHAINS: { [chainId: number]: BasicChainInformation | ExtendedChainI }, } -export const URLS: { [chainId: number]: string[] } = Object.keys(CHAINS).reduce((accumulator, chainId) => { - const validURLs: string[] = CHAINS[Number(chainId)].urls +export const URLS: { [chainId: number]: string[] } = Object.keys(CHAINS).reduce<{ [chainId: number]: string[] }>( + (accumulator, chainId) => { + const validURLs: string[] = CHAINS[Number(chainId)].urls - if (validURLs.length) { - accumulator[chainId] = validURLs - } + if (validURLs.length) { + accumulator[Number(chainId)] = validURLs + } - return accumulator -}, {}) + return accumulator + }, + {} +) diff --git a/packages/example/connectors/walletConnect.ts b/packages/example/connectors/walletConnect.ts index 669b239e7..8a515b790 100644 --- a/packages/example/connectors/walletConnect.ts +++ b/packages/example/connectors/walletConnect.ts @@ -5,8 +5,8 @@ import { URLS } from '../chains' export const [walletConnect, hooks] = initializeConnector( (actions) => new WalletConnect(actions, { - rpc: Object.keys(URLS).reduce((accumulator, chainId) => { - accumulator[chainId] = URLS[Number(chainId)][0] + rpc: Object.keys(URLS).reduce<{ [chainId: number]: string }>((accumulator, chainId) => { + accumulator[Number(chainId)] = URLS[Number(chainId)][0] return accumulator }, {}), }), diff --git a/packages/metamask/src/index.ts b/packages/metamask/src/index.ts index ab3e129ee..27b227cd6 100644 --- a/packages/metamask/src/index.ts +++ b/packages/metamask/src/index.ts @@ -31,6 +31,10 @@ export class MetaMask extends Connector { private readonly options?: Parameters[0] private eagerConnection?: Promise + /** + * @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed. + * @param options - Options to pass to `@metamask/detect-provider` + */ constructor(actions: Actions, connectEagerly = true, options?: Parameters[0]) { super(actions) this.options = options @@ -93,6 +97,15 @@ export class MetaMask extends Connector { }) } + /** + * Initiates a connection. + * + * @param desiredChainIdOrChainParameters - If defined, indicates the desired chain to connect to. If the user is + * already connected to this chain, no additional steps will be taken. Otherwise, the user will be prompted to switch + * to the chain, if one of two conditions is met: either they already have it added in their extension, or the + * argument is of type AddEthereumChainParameter, in which case the user will be prompted to add the chain with the + * specified parameters first, before being prompted to switch. + */ public async activate(desiredChainIdOrChainParameters?: number | AddEthereumChainParameter): Promise { const desiredChainId = typeof desiredChainIdOrChainParameters === 'number' diff --git a/packages/network/src/index.ts b/packages/network/src/index.ts index 826a9f9d8..807bfad0d 100644 --- a/packages/network/src/index.ts +++ b/packages/network/src/index.ts @@ -6,12 +6,17 @@ import { Connector } from '@web3-react/types' type url = string | ConnectionInfo export class Network extends Connector { + /** {@inheritdoc Connector.provider} */ provider: Eip1193Bridge | undefined private urlMap: { [chainId: number]: url[] } private chainId: number private providerCache: { [chainId: number]: Eip1193Bridge } = {} + /** + * @param urlMap - A mapping from chainIds to RPC urls. + * @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed. + */ constructor(actions: Actions, urlMap: { [chainId: number]: url | url[] }, connectEagerly = true) { super(actions) this.urlMap = Object.keys(urlMap).reduce<{ [chainId: number]: url[] }>((accumulator, chainId) => { @@ -80,6 +85,11 @@ export class Network extends Connector { } } + /** + * Initiates a connection. + * + * @param desiredChainId - The desired chain to connect to. + */ public async activate(desiredChainId = Number(Object.keys(this.urlMap)[0])): Promise { if (this.urlMap[desiredChainId] === undefined) { throw new Error(`no url(s) provided for desiredChainId ${desiredChainId}`) diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index 8013a0e86..98f05dd78 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -46,6 +46,12 @@ export function createWeb3ReactStoreAndActions(allowedChainIds?: number[]): [Web // flag for tracking updates so we don't clobber data when cancelling activation let nullifier = 0 + /** + * Sets activating to true, indicating that an update is in progress. + * + * @returns cancelActivation - A function that cancels the activation by setting activating to false, + * as long as there haven't been any intervening updates. + */ function startActivation(): () => void { const nullifierCached = ++nullifier @@ -59,6 +65,13 @@ export function createWeb3ReactStoreAndActions(allowedChainIds?: number[]): [Web } } + /** + * Used to report a `stateUpdate` which is merged with existing state. The first `stateUpdate` that results in chainId + * and accounts being set will also set activating to false, indicating a successful connection. Similarly, if an + * error is set, the first `stateUpdate` that results in chainId and accounts being set will clear this error. + * + * @param stateUpdate - The state update to report. + */ function update(stateUpdate: Web3ReactStateUpdate): void { // validate chainId statically, independent of existing state if (stateUpdate.chainId !== undefined) { @@ -110,7 +123,12 @@ export function createWeb3ReactStoreAndActions(allowedChainIds?: number[]): [Web }) } - function reportError(error: Error | undefined) { + /** + * Used to report an `error`, which clears all existing state. + * + * @param error - The error to report. If undefined, the state will be reset to its default value. + */ + function reportError(error: Error | undefined): void { nullifier++ store.setState(() => ({ ...DEFAULT_STATE, error })) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index dc0733041..215316cb7 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -10,10 +10,19 @@ export interface Web3ReactState extends State { export type Web3ReactStore = StoreApi -export interface Web3ReactStateUpdate { - chainId?: number - accounts?: string[] -} +export type Web3ReactStateUpdate = + | { + chainId: number + accounts: string[] + } + | { + chainId: number + accounts?: never + } + | { + chainId?: never + accounts: string[] + } export interface Actions { startActivation: () => () => void @@ -44,22 +53,33 @@ export interface ProviderRpcError extends Error { data?: unknown } -// per EIP-1193 -export interface ProviderMessage { - readonly type: string - readonly data: unknown -} - export abstract class Connector { + /** + * An + * EIP-1193 ({@link https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md}) and + * EIP-1102 ({@link https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1102.md}) compliant provider. + * This property must be defined while the connector is active. + */ public provider: Provider | undefined protected readonly actions: Actions + /** + * @param actions - Methods bound to a zustand store that tracks the state of the connector. + * Actions are used by the connector to report changes in connection status. + */ constructor(actions: Actions) { this.actions = actions } + /** + * Initiate a connection. + */ public abstract activate(...args: unknown[]): Promise | void + + /** + * Initiate a disconnect. + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars public deactivate(...args: unknown[]): Promise | void { this.actions.reportError(undefined) diff --git a/packages/url/src/index.ts b/packages/url/src/index.ts index c9762550c..434be52eb 100644 --- a/packages/url/src/index.ts +++ b/packages/url/src/index.ts @@ -6,10 +6,15 @@ import { Connector } from '@web3-react/types' type url = string | ConnectionInfo export class Url extends Connector { + /** {@inheritdoc Connector.provider} */ provider: Eip1193Bridge | undefined private url: url + /** + * @param url - An RPC url. + * @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed. + */ constructor(actions: Actions, url: url, connectEagerly = true) { super(actions) this.url = url @@ -44,6 +49,7 @@ export class Url extends Connector { }) } + /** {@inheritdoc Connector.activate} */ public async activate(): Promise { return this.initialize() } diff --git a/packages/walletconnect/src/index.ts b/packages/walletconnect/src/index.ts index 61f4562bc..0dfc91238 100644 --- a/packages/walletconnect/src/index.ts +++ b/packages/walletconnect/src/index.ts @@ -9,11 +9,16 @@ interface MockWalletConnectProvider EventEmitter {} export class WalletConnect extends Connector { + /** {@inheritdoc Connector.provider} */ + provider: MockWalletConnectProvider | undefined + private readonly options?: IWCEthRpcConnectionOptions private eagerConnection?: Promise - provider: MockWalletConnectProvider | undefined - + /** + * @param options - Options to pass to `@walletconnect/ethereum-provider` + * @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed. + */ constructor(actions: Actions, options: IWCEthRpcConnectionOptions, connectEagerly = true) { super(actions) this.options = options @@ -74,6 +79,7 @@ export class WalletConnect extends Connector { }) } + /** {@inheritdoc Connector.activate} */ public async activate(): Promise { this.actions.startActivation() @@ -103,6 +109,7 @@ export class WalletConnect extends Connector { } } + /** {@inheritdoc Connector.deactivate} */ public async deactivate(): Promise { if (this.provider) { await this.provider.disconnect() diff --git a/packages/walletlink/src/index.ts b/packages/walletlink/src/index.ts index 6b673e9d7..3752d4f14 100644 --- a/packages/walletlink/src/index.ts +++ b/packages/walletlink/src/index.ts @@ -8,12 +8,21 @@ function parseChainId(chainId: string) { } export class WalletLink extends Connector { + /** {@inheritdoc Connector.provider} */ + public provider: ReturnType | undefined + private readonly options: WalletLinkOptions & { url: string } private eagerConnection?: Promise + /** + * A `walletlink` instance. + */ public walletLink: WalletLinkInstance | undefined - public provider: ReturnType | undefined + /** + * @param options - Options to pass to `walletlink` + * @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed. + */ constructor(actions: Actions, options: WalletLinkOptions & { url: string }, connectEagerly = true) { super(actions) this.options = options @@ -80,6 +89,7 @@ export class WalletLink extends Connector { }) } + /** {@inheritdoc Connector.activate} */ public async activate(): Promise { this.actions.startActivation() @@ -104,6 +114,7 @@ export class WalletLink extends Connector { }) } + /** {@inheritdoc Connector.deactivate} */ public deactivate(): void { if (this.provider) { this.provider.off('connect', this.disconnectListener) diff --git a/yarn.lock b/yarn.lock index 648cc8294..3dae8154d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1360,6 +1360,21 @@ resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== +"@microsoft/tsdoc-config@0.15.2": + version "0.15.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz#eb353c93f3b62ab74bdc9ab6f4a82bcf80140f14" + integrity sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA== + dependencies: + "@microsoft/tsdoc" "0.13.2" + ajv "~6.12.6" + jju "~1.4.0" + resolve "~1.19.0" + +"@microsoft/tsdoc@0.13.2": + version "0.13.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz#3b0efb6d3903bd49edb073696f60e90df08efb26" + integrity sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2133,7 +2148,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@~6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3481,6 +3496,14 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== +eslint-plugin-tsdoc@^0.2.14: + version "0.2.14" + resolved "https://registry.yarnpkg.com/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.14.tgz#e32e7c1df8af7b3009c252590bec07a1030afbf2" + integrity sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng== + dependencies: + "@microsoft/tsdoc" "0.13.2" + "@microsoft/tsdoc-config" "0.15.2" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -4595,7 +4618,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.0: +is-core-module@^2.1.0, is-core-module@^2.5.0, is-core-module@^2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== @@ -5254,6 +5277,11 @@ jest@^27.2.4: import-local "^3.0.2" jest-cli "^27.4.7" +jju@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo= + js-sha256@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" @@ -6514,7 +6542,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.7: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -7021,6 +7049,14 @@ resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@~1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"