diff --git a/packages/hdwallet-provider/package.json b/packages/hdwallet-provider/package.json index 03052dc6662..d1585ccec82 100644 --- a/packages/hdwallet-provider/package.json +++ b/packages/hdwallet-provider/package.json @@ -39,7 +39,8 @@ "ganache-core": "2.13.0", "mocha": "8.1.2", "ts-node": "^9.0.0", - "typescript": "^4.1.4" + "typescript": "^4.1.4", + "web3": "1.5.3" }, "keywords": [ "etheruem", diff --git a/packages/hdwallet-provider/src/constructor/Constructor.ts b/packages/hdwallet-provider/src/constructor/Constructor.ts index 9647757a0f3..29284e4004c 100644 --- a/packages/hdwallet-provider/src/constructor/Constructor.ts +++ b/packages/hdwallet-provider/src/constructor/Constructor.ts @@ -2,6 +2,8 @@ import type { Mnemonic, MnemonicPhrase, PrivateKey, + Provider, + ProviderUrl, ProviderOrUrl, AddressIndex, NumberOfAddresses, @@ -38,7 +40,9 @@ export type InputSigningAuthority = | PrivateKeysSigningAuthority; export interface CommonOptions { - providerOrUrl: ProviderOrUrl; + providerOrUrl?: ProviderOrUrl; + provider?: Provider; + url?: ProviderUrl; addressIndex?: AddressIndex; numberOfAddresses?: NumberOfAddresses; shareNonce?: ShareNonce; diff --git a/packages/hdwallet-provider/src/constructor/getOptions.ts b/packages/hdwallet-provider/src/constructor/getOptions.ts index 25fa77270dd..52b4c2a77af 100644 --- a/packages/hdwallet-provider/src/constructor/getOptions.ts +++ b/packages/hdwallet-provider/src/constructor/getOptions.ts @@ -111,7 +111,7 @@ const matchesNewInputOptions = ( // beyond that, determine based on property inclusion check for required keys return ( - "providerOrUrl" in options && + ("providerOrUrl" in options || "provider" in options || "url" in options) && ("privateKeys" in options || "mnemonic" in options) ); }; diff --git a/packages/hdwallet-provider/src/constructor/types.ts b/packages/hdwallet-provider/src/constructor/types.ts index 791f2b0d05f..9f470433eff 100644 --- a/packages/hdwallet-provider/src/constructor/types.ts +++ b/packages/hdwallet-provider/src/constructor/types.ts @@ -7,8 +7,15 @@ export interface Mnemonic { phrase: MnemonicPhrase; password?: MnemonicPassword; } +import type { Provider as LegacyProvider } from "web3/providers"; +type Eip1193Provider = { + request: (options: { + method: string; + params?: unknown[] | object; + }) => Promise; +}; export type PrivateKey = string; -export type Provider = any; +export type Provider = LegacyProvider | Eip1193Provider; export type ProviderUrl = string; export type ProviderOrUrl = Provider | ProviderUrl; export type AddressIndex = number; diff --git a/packages/hdwallet-provider/src/index.ts b/packages/hdwallet-provider/src/index.ts index 12bf49fcbba..d4ea45af35b 100644 --- a/packages/hdwallet-provider/src/index.ts +++ b/packages/hdwallet-provider/src/index.ts @@ -54,7 +54,9 @@ class HDWalletProvider { constructor(...args: ConstructorArguments) { const { - providerOrUrl, // required + provider, + url, + providerOrUrl, addressIndex = 0, numberOfAddresses = 10, shareNonce = true, @@ -78,11 +80,21 @@ class HDWalletProvider { pollingInterval }); - if (!HDWalletProvider.isValidProvider(providerOrUrl)) { + let providerToUse; + if (HDWalletProvider.isValidProvider(provider)) { + providerToUse = provider; + } else if (HDWalletProvider.isValidProvider(url)) { + providerToUse = url; + } else { + providerToUse = providerOrUrl; + } + + if (!HDWalletProvider.isValidProvider(providerToUse)) { throw new Error( [ - `Malformed provider URL: '${providerOrUrl}'`, - "Please specify a correct URL, using the http, https, ws, or wss protocol.", + `No provider or an invalid provider was specified: '${providerToUse}'`, + "Please specify a valid provider or URL, using the http, https, " + + "ws, or wss protocol.", "" ].join("\n") ); @@ -219,8 +231,8 @@ class HDWalletProvider { : this.engine.addProvider(singletonNonceSubProvider); this.engine.addProvider(new FiltersSubprovider()); - if (typeof providerOrUrl === "string") { - const url = providerOrUrl; + if (typeof providerToUse === "string") { + const url = providerToUse; const providerProtocol = ( Url.parse(url).protocol || "http:" @@ -235,8 +247,7 @@ class HDWalletProvider { this.engine.addProvider(new RpcProvider({ rpcUrl: url })); } } else { - const provider = providerOrUrl; - this.engine.addProvider(new ProviderSubprovider(provider)); + this.engine.addProvider(new ProviderSubprovider(providerToUse)); } // Required by the provider engine. @@ -356,15 +367,20 @@ class HDWalletProvider { return this.addresses; } - public static isValidProvider(provider: string | any): boolean { - const validProtocols = ["http:", "https:", "ws:", "wss:"]; - + public static isValidProvider(provider: any): boolean { + if (!provider) return false; if (typeof provider === "string") { + const validProtocols = ["http:", "https:", "ws:", "wss:"]; const url = Url.parse(provider.toLowerCase()); return !!(validProtocols.includes(url.protocol || "") && url.slashes); + } else if ("request" in provider) { + // provider is an 1193 provider + return true; + } else if ("send" in provider) { + // provider is a "legacy" provider + return true; } - - return true; + return false; } } diff --git a/packages/hdwallet-provider/test/provider.test.ts b/packages/hdwallet-provider/test/provider.test.ts index 773eccd5908..9366901fd93 100644 --- a/packages/hdwallet-provider/test/provider.test.ts +++ b/packages/hdwallet-provider/test/provider.test.ts @@ -140,7 +140,7 @@ describe("HD Wallet Provider", function () { mnemonic: { phrase: mnemonicPhrase }, - providerOrUrl: `http://localhost:${port}` + url: `http://localhost:${port}` }); assert.deepEqual(provider.getAddresses(), truffleDevAccounts); @@ -168,7 +168,7 @@ describe("HD Wallet Provider", function () { "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"; provider = new HDWalletProvider({ mnemonic: mnemonicPhrase, - providerOrUrl: `http://localhost:${port}` + url: `http://localhost:${port}` }); assert.deepEqual(provider.getAddresses(), truffleDevAccounts); @@ -198,7 +198,7 @@ describe("HD Wallet Provider", function () { phrase: mnemonicPhrase, password: "yummy" }, - providerOrUrl: `http://localhost:${port}` + url: `http://localhost:${port}` }); assert.deepEqual(provider.getAddresses(), accounts); @@ -208,20 +208,6 @@ describe("HD Wallet Provider", function () { assert(number === 0); }); - it("throws on invalid mnemonic", () => { - try { - provider = new HDWalletProvider({ - mnemonic: { - phrase: "I am not a crook" - }, - providerOrUrl: "http://localhost:8545" - }); - assert.fail("Should throw on invalid mnemonic"); - } catch (e) { - assert(e.message.includes("Mnemonic invalid or undefined")); - } - }); - it("provides for a default polling interval", () => { const mnemonicPhrase = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"; @@ -229,7 +215,7 @@ describe("HD Wallet Provider", function () { mnemonic: { phrase: mnemonicPhrase }, - providerOrUrl: `http://localhost:${port}`, + url: `http://localhost:${port}`, // polling interval is unspecified }); assert.ok(provider.engine, @@ -249,7 +235,7 @@ describe("HD Wallet Provider", function () { mnemonic: { phrase: mnemonicPhrase }, - providerOrUrl: `http://localhost:${port}`, + url: `http://localhost:${port}`, // double the default value, for less chatty JSON-RPC pollingInterval: 8000, }); @@ -278,7 +264,7 @@ describe("HD Wallet Provider", function () { provider = new HDWalletProvider({ privateKeys, - providerOrUrl: `http://localhost:${port}` + url: `http://localhost:${port}` }); web3.setProvider(provider); @@ -304,5 +290,50 @@ describe("HD Wallet Provider", function () { const number = await web3.eth.getBlockNumber(); assert(number === 0); }); + + describe("instantiation errors", () => { + it("throws on invalid providers", () => { + try { + provider = new HDWalletProvider({ + mnemonic: { + phrase: "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat" + }, + // @ts-ignore we gotta do the bad thing here to get the test right + provider: { junk: "in", an: "object" } + }); + assert.fail("Should throw on invalid provider"); + } catch (e) { + assert(e.message.includes("invalid provider was specified")); + } + }); + + it("throws on invalid urls", () => { + try { + provider = new HDWalletProvider({ + mnemonic: { + phrase: "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat" + }, + url: "justABunchOfJunk" + }); + assert.fail("Should throw on invalid url"); + } catch (e) { + assert(e.message.includes("invalid provider was specified")); + } + }); + + it("throws on invalid mnemonic", () => { + try { + provider = new HDWalletProvider({ + mnemonic: { + phrase: "I am not a crook" + }, + url: "http://localhost:8545" + }); + assert.fail("Should throw on invalid mnemonic"); + } catch (e) { + assert(e.message.includes("Mnemonic invalid or undefined")); + } + }); + }); }); }); diff --git a/packages/hdwallet-provider/test/urlValidation.test.ts b/packages/hdwallet-provider/test/urlValidation.test.ts index d1f9a637bac..71396a13794 100644 --- a/packages/hdwallet-provider/test/urlValidation.test.ts +++ b/packages/hdwallet-provider/test/urlValidation.test.ts @@ -1,5 +1,6 @@ import assert from "assert"; import WalletProvider from "../dist"; +import Ganache from "ganache-core"; import { describe, it } from "mocha"; const { isValidProvider } = WalletProvider; @@ -20,8 +21,8 @@ describe("HD Wallet Provider Validator", () => { assert.fail("did not throw!"); } catch (e) { const expectedMessage = [ - `Malformed provider URL: '${badUrl}'`, - "Please specify a correct URL, using the http, https, ws, or wss protocol.", + `No provider or an invalid provider was specified: '${badUrl}'`, + "Please specify a valid provider or URL, using the http, https, ws, or wss protocol.", "" ].join("\n"); assert.equal(e.message, expectedMessage); @@ -35,8 +36,8 @@ describe("HD Wallet Provider Validator", () => { assert.fail("did not throw!"); } catch (e) { const expectedMessage = [ - `Malformed provider URL: '${badUrl}'`, - "Please specify a correct URL, using the http, https, ws, or wss protocol.", + `No provider or an invalid provider was specified: '${badUrl}'`, + "Please specify a valid provider or URL, using the http, https, ws, or wss protocol.", "" ].join("\n"); assert.equal(e.message, expectedMessage); @@ -80,5 +81,13 @@ describe("HD Wallet Provider Validator", () => { "Good WSS Url should pass validation" ); }); + + it("a provider", () => { + const provider = Ganache.provider(); + assert.ok( + isValidProvider(provider), + "Good provider should pass validation." + ); + }); }); });