From 7db6670c759855913d46b051b879f4dba373e7c6 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 07:48:13 -0300 Subject: [PATCH 01/15] expose etherjs dependencies --- .npmignore | 12 ++++++++---- README.md | 2 ++ package.json | 6 +----- src/ethers.interface.ts | 8 ++++---- src/index.ts | 16 ++++++++++++++++ tsconfig.build.json | 2 +- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/.npmignore b/.npmignore index 41f3042..918a0d6 100644 --- a/.npmignore +++ b/.npmignore @@ -1,9 +1,13 @@ __tests__/ .github/ coverage/ -dist/*.tsbuildinfo -dist/*.js.map +*.tsbuildinfo +*.js.map src/ +.eslintrc.js +.gitignore +.prettierrc .travis.yml -CONTRIBUTING.md -README.md +nest-cli.json +tsconfig.build.json +tsconfig.json diff --git a/README.md b/README.md index 76525dc..af74b81 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Supported platforms: Express & Fastify

+

A complete Ethereum wallet implementation and utilities for NestJS based on [Ethers.js](https://github.com/ethers-io/ethers.js/)

+ ## Install ```sh diff --git a/package.json b/package.json index 7ef56ef..b5e9561 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,11 @@ { "name": "nestjs-ethers", - "version": "0.1.0-alpha.1", + "version": "0.1.0-rc.4", "description": "The ethers.js library for NestJS", "author": "Jose Ramirez ", "license": "Apache", "readmeFilename": "README.md", "main": "dist/index.js", - "files": [ - "dist/**/*", - "*.md" - ], "engineStrict": false, "engines": { "node": "^10.24 || >=12.22 || >=14.16 || >=15.14" diff --git a/src/ethers.interface.ts b/src/ethers.interface.ts index ae2b33e..8910fb5 100644 --- a/src/ethers.interface.ts +++ b/src/ethers.interface.ts @@ -2,13 +2,13 @@ import { ModuleMetadata } from '@nestjs/common/interfaces'; import { Network } from '@ethersproject/providers'; export interface InfuraProviderOptions { - projectId: string; - projectSecret: string; + projectId?: string; + projectSecret?: string; } export interface PocketProviderOptions { - applicationId: string; - applicationSecretKey: string; + applicationId?: string; + applicationSecretKey?: string; } export interface EthersModuleOptions extends Record { diff --git a/src/index.ts b/src/index.ts index c17b693..a76355a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,3 +11,19 @@ export { BaseProvider as EthersBaseProvider, Network, } from '@ethersproject/providers'; +export { BigNumber, BigNumberish } from '@ethersproject/bignumber'; +export { + Block, + BlockTag, + BlockWithTransactions, + TransactionReceipt, + TransactionRequest, + TransactionResponse, +} from '@ethersproject/abstract-provider'; +export { + commify, + formatUnits, + parseUnits, + formatEther, + parseEther, +} from '@ethersproject/units'; diff --git a/tsconfig.build.json b/tsconfig.build.json index 6ce8c6c..b90fc83 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,4 @@ { "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts", "**/*mock.ts"] + "include": ["src"] } From c078fdf757c42688ea8a810814e5047ae4a87c9f Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 07:49:37 -0300 Subject: [PATCH 02/15] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af74b81..22f085b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Supported platforms: Express & Fastify

-

A complete Ethereum wallet implementation and utilities for NestJS based on [Ethers.js](https://github.com/ethers-io/ethers.js/)

+

A complete Ethereum wallet implementation and utilities for NestJS based on

[Ethers.js](https://github.com/ethers-io/ethers.js/) ## Install From 752efec2b78990259c35bf08bd7547ff641c06d4 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 07:59:48 -0300 Subject: [PATCH 03/15] update readme --- README.md | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 22f085b..7555deb 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,16 @@ -

NestJS-Ethers

- -

- - npm - - - Travis - - - Coverage Status - - - Snyk Vulnerabilities for npm package - - David - Dependabot - Supported platforms: Express & Fastify -

- -

A complete Ethereum wallet implementation and utilities for NestJS based on

[Ethers.js](https://github.com/ethers-io/ethers.js/) +NestJS-Ethers +============= + +[![npm](https://img.shields.io/npm/v/nestjs-ethers)](https://www.npmjs.com/package/nestjs-ethers) +[![travis](https://api.travis-ci.com/jarcodallo/nestjs-ethers.svg?branch=main)](https://travis-ci.com/github/jarcodallo/nestjs-ethers) +[![coverage](https://coveralls.io/repos/github/jarcodallo/nestjs-ethers/badge.svg?branch=main)](https://coveralls.io/github/jarcodallo/nestjs-ethers?branch=main) +[![vulnerabilities](https://img.shields.io/snyk/vulnerabilities/npm/nestjs-ethers)](https://snyk.io/test/github/jarcodallo/nestjs-ethers) +[![dependencies](https://img.shields.io/david/jarcodallo/nestjs-ethers)](https://img.shields.io/david/jarcodallo/nestjs-ethers) +[![dependabot](https://badgen.net/dependabot/jarcodallo/nestjs-ethers/?icon=dependabot)](https://badgen.net/dependabot/jarcodallo/nestjs-ethers/?icon=dependabot) +[![supported platforms](https://img.shields.io/badge/platforms-Express%20%26%20Fastify-green)](https://img.shields.io/badge/platforms-Express%20%26%20Fastify-green) + + +Ethereum wallet implementation and utilities for NestJS based on [Ethers.js](https://github.com/ethers-io/ethers.js/) ## Install From 884ab3cb79733f037edd063fa13889b90946ed58 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 08:10:42 -0300 Subject: [PATCH 04/15] update readme --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 7555deb..c68042d 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,24 @@ Ethereum wallet implementation and utilities for NestJS based on [Ethers.js](htt npm i nestjs-ethers ``` +## Register module + +### Zero configuration + +Just import `EthersModule` to your module: + +```ts +import { EthersModule } from 'nestjs-ethers'; + +@Module({ + imports: [EthersModule.forRoot()], + ... +}) +class MyModule {} +``` + +**NOTE:** *By default `EthersModule` will try to connect using [getDefaultProvider](https://docs.ethers.io/v5/api/providers/#providers-getDefaultProvider). It's the safest, easiest way to begin developing on Ethereum, and it is also robust enough for use in production. It creates a [FallbackProvider](https://docs.ethers.io/v5/api/providers/other/#FallbackProvider) connected to as many backend services as possible.* + ## Change Log See [Changelog](CHANGELOG.md) for more information. From 8692ad1723e54cea3387ef4ded846eacafe72751 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 08:47:24 -0300 Subject: [PATCH 05/15] update readme --- README.md | 66 +++++++++++++++++++++++++++++++++++++++-- src/ethers.interface.ts | 2 +- src/ethers.providers.ts | 10 +++---- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c68042d..ccad687 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,69 @@ import { EthersModule } from 'nestjs-ethers'; class MyModule {} ``` -**NOTE:** *By default `EthersModule` will try to connect using [getDefaultProvider](https://docs.ethers.io/v5/api/providers/#providers-getDefaultProvider). It's the safest, easiest way to begin developing on Ethereum, and it is also robust enough for use in production. It creates a [FallbackProvider](https://docs.ethers.io/v5/api/providers/other/#FallbackProvider) connected to as many backend services as possible.* +**NOTE:** *By default `EthersModule` will try to connect using [getDefaultProvider](https://docs.ethers.io/v5/api/providers/#providers-getDefaultProvider). It's the safest, easiest way to begin developing on Ethereum. It creates a [FallbackProvider](https://docs.ethers.io/v5/api/providers/other/#FallbackProvider) connected to as many backend services as possible.* + +### Configuration params + +`nestjs-ethers` can be configured with this options: + +```ts +interface EthersModuleOptions { + /** + * Optional parameter that can be a Network object or the name of a common network as a string (e.g. "homestead") + * If no network is provided, homestead (i.e. mainnet) is used. + * The network may also be a URL to connect to, such as http://localhost:8545 or wss://example.com. + * @see {@link https://docs.ethers.io/v5/api/providers/types/#providers-Networkish} + */ + network?: Network | string; + + /** + * Optional parameter to name the module context, + */ + providerName?: string; + + /** + * Optional parameter for Alchemy API Token + * @see {@link https://alchemyapi.io} + */ + alchemy?: string; + + /** + * Optional parameter for Etherscan API Token + * @see {@link https://etherscan.io} + */ + etherscan?: string; + + /** + * Optional parameter for Cloudflare API Token + * @see {@link https://cloudflare-eth.com} + */ + cloudflare?: string; + + /** + * Optional parameter for Infura Project ID or InfuraProviderOptions(applicationId, applicationSecretKey) + * @see {@link https://infura.io} + */ + infura?: InfuraProviderOptions | string; + + /** + * Optional parameter for Pocket Network Application ID or PocketProviderOptions(projectId, projectSecret) + * @see {@link https://pokt.network} + */ + pocket?: PocketProviderOptions | string; + + /** + * Optional parameter the number of backends that must agree (default: 2 for mainnet, 1 for testnets) + */ + quorum?: number; + + /** + * Optional parameter if this option is false, EthersModule will try to connect with the credentials provided in options. + * If you define more than one provider, EthersModule will use the FallbackProvider to send multiple requests simultaneously. + */ + useDefaultProvider?: boolean; +} +``` ## Change Log @@ -50,4 +112,4 @@ Contributions welcome! See [Contributing](CONTRIBUTING.md). ## License -Licensed under the Apache 2.0 - see the [LICENSE](LICENSE) file for details. \ No newline at end of file +Licensed under the Apache 2.0 - see the [LICENSE](LICENSE) file for details. diff --git a/src/ethers.interface.ts b/src/ethers.interface.ts index 8910fb5..3db396a 100644 --- a/src/ethers.interface.ts +++ b/src/ethers.interface.ts @@ -20,9 +20,9 @@ export interface EthersModuleOptions extends Record { infura?: InfuraProviderOptions | string; pocket?: PocketProviderOptions | string; quorum?: number; - useCloudflareProvider?: boolean; useDefaultProvider?: boolean; } + export interface EthersModuleAsyncOptions extends Pick { providerName?: string; diff --git a/src/ethers.providers.ts b/src/ethers.providers.ts index 74b8aa0..8098af8 100644 --- a/src/ethers.providers.ts +++ b/src/ethers.providers.ts @@ -37,12 +37,6 @@ export async function createDefaultProvider( } = options; if (!useDefaultProvider) { - /** - * If you decided to not use the DefaultProvider, you can controll what Providers to enabled - * FallbackProvider uses a quorum and connects to multiple Providers as backends, - * each configured with a priority and a weight. - * @see {@link https://docs.ethers.io/v5/api/providers/other/#FallbackProvider} - */ const providers: Array = []; if (alchemy) { @@ -91,6 +85,10 @@ export async function createDefaultProvider( } if (providers.length > 1) { + /** + * FallbackProvider with selected providers. + * @see {@link https://docs.ethers.io/v5/api/providers/other/#FallbackProvider} + */ return new FallbackProvider(providers, quorum); } From 51d9b83c329bdc17b3a2a933091b8798c605ce9f Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 09:56:21 -0300 Subject: [PATCH 06/15] update readme --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ccad687..8a5eedc 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,11 @@ class MyModule {} ```ts interface EthersModuleOptions { /** - * Optional parameter that can be a Network object or the name of a common network as a string (e.g. "homestead") + * Optional parameter for connection, can be a Network object + * or the name of a common network as a string (e.g. "homestead") * If no network is provided, homestead (i.e. mainnet) is used. - * The network may also be a URL to connect to, such as http://localhost:8545 or wss://example.com. + * The network may also be a URL to connect to, + * such as http://localhost:8545 or wss://example.com. * @see {@link https://docs.ethers.io/v5/api/providers/types/#providers-Networkish} */ network?: Network | string; @@ -74,25 +76,29 @@ interface EthersModuleOptions { cloudflare?: string; /** - * Optional parameter for Infura Project ID or InfuraProviderOptions(applicationId, applicationSecretKey) + * Optional parameter for Infura Project ID + * or InfuraProviderOptions(applicationId, applicationSecretKey) * @see {@link https://infura.io} */ infura?: InfuraProviderOptions | string; /** - * Optional parameter for Pocket Network Application ID or PocketProviderOptions(projectId, projectSecret) + * Optional parameter for Pocket Network Application ID + * or PocketProviderOptions(projectId, projectSecret) * @see {@link https://pokt.network} */ pocket?: PocketProviderOptions | string; /** - * Optional parameter the number of backends that must agree (default: 2 for mainnet, 1 for testnets) + * Optional parameter the number of backends that must agree + * (default: 2 for mainnet, 1 for testnets) */ quorum?: number; /** - * Optional parameter if this option is false, EthersModule will try to connect with the credentials provided in options. - * If you define more than one provider, EthersModule will use the FallbackProvider to send multiple requests simultaneously. + * Optional parameter if this option is false, EthersModule will try to connect + * with the credentials provided in options. If you define more than one provider, + * EthersModule will use the FallbackProvider to send multiple requests simultaneously. */ useDefaultProvider?: boolean; } From 9b2a01a975b26f7464556e34179d993b3f246b15 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 10:45:04 -0300 Subject: [PATCH 07/15] update readme and change forRoot props --- README.md | 31 +++++++++++++++++++++++++++++ __tests__/ethers.decorators.spec.ts | 16 +++++++++++++++ __tests__/ethers.module.spec.ts | 6 ++++-- src/ethers-core.module.ts | 9 +++------ src/ethers.module.ts | 8 ++------ src/ethers.providers.ts | 14 ++++--------- 6 files changed, 60 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 8a5eedc..7ccf128 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,37 @@ interface EthersModuleOptions { } ``` +### Synchronous configuration + +Use `EthersModule.forRoot` method with argument of [Params interface](#configuration-params): + +```ts +import { EthersModule } from 'nestjs-ethers'; + +@Module({ + imports: [ + EthersModule.forRoot({ + network: 'rinkeby', + providerName: 'MyModule', + alchemy: '845ce4ed0120d68eb5740c9160f08f98', + etherscan: 'e8cce313c1cfbd085f68be509451f1bab8', + cloudflare: 'd9f3e29f461338bc70e79961', + infura: { + projectId: 'd71b3d93c2fcfa7cab4924e63298575a', + projectSecret: 'ed6baa9f7a09877998a24394a12bf3dc', + }, + pocket: { + applicationId: '9b0afc55221c429104d04ef9', + applicationSecretKey: 'b5e6d6a55426712a42a93f39555973fc', + }, + quorum: 1, + useDefaultProvider: true, + }) + ], +}) +class MyModule {} +``` + ## Change Log See [Changelog](CHANGELOG.md) for more information. diff --git a/__tests__/ethers.decorators.spec.ts b/__tests__/ethers.decorators.spec.ts index 024bb2e..4183389 100644 --- a/__tests__/ethers.decorators.spec.ts +++ b/__tests__/ethers.decorators.spec.ts @@ -1,6 +1,7 @@ import { NestFactory } from '@nestjs/core'; import { Module, Controller, Get, Injectable } from '@nestjs/common'; import * as request from 'supertest'; +import * as nock from 'nock'; import { EthersModule, InjectEthersProvider, @@ -11,6 +12,21 @@ import { platforms } from './utils/platforms'; import { extraWait } from './utils/extraWait'; describe('InjectEthersProvider', () => { + beforeEach(() => nock.cleanAll()); + + beforeAll(() => { + if (!nock.isActive()) { + nock.activate(); + } + + nock.disableNetConnect(); + nock.enableNetConnect('127.0.0.1'); + }); + + afterAll(() => { + nock.restore(); + }); + for (const PlatformAdapter of platforms) { describe(PlatformAdapter.name, () => { it('should inject ethers provider in a service successfully', async () => { diff --git a/__tests__/ethers.module.spec.ts b/__tests__/ethers.module.spec.ts index 0c41c68..50c76f5 100644 --- a/__tests__/ethers.module.spec.ts +++ b/__tests__/ethers.module.spec.ts @@ -108,7 +108,8 @@ describe('Ethers Module Initialization', () => { } @Module({ imports: [ - EthersModule.forRoot('rinkeby', { + EthersModule.forRoot({ + network: 'rinkeby', alchemy: RINKEBY_ALCHEMY_API_KEY, useDefaultProvider: false, }), @@ -158,7 +159,8 @@ describe('Ethers Module Initialization', () => { } @Module({ imports: [ - EthersModule.forRoot('rinkeby', { + EthersModule.forRoot({ + network: 'rinkeby', pocket: { applicationId: RINKEBY_POKT_API_KEY, applicationSecretKey: RINKEBY_POKT_SECRET_KEY, diff --git a/src/ethers-core.module.ts b/src/ethers-core.module.ts index 7ce7d04..4166c07 100644 --- a/src/ethers-core.module.ts +++ b/src/ethers-core.module.ts @@ -6,7 +6,7 @@ import { Inject, } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; -import { Network, BaseProvider } from '@ethersproject/providers'; +import { BaseProvider } from '@ethersproject/providers'; import { EthersModuleOptions, EthersModuleAsyncOptions, @@ -27,11 +27,8 @@ export class EthersCoreModule implements OnApplicationShutdown { private readonly moduleRef: ModuleRef, ) {} - static forRoot( - network: Network | string = 'homestead', - options: EthersModuleOptions = {}, - ): DynamicModule { - const ethersProvider = createEthersProvider(network, options); + static forRoot(options: EthersModuleOptions = {}): DynamicModule { + const ethersProvider = createEthersProvider(options); const providerName = options?.providerName ?? ''; return { diff --git a/src/ethers.module.ts b/src/ethers.module.ts index 8bdcbb6..3e2bd40 100644 --- a/src/ethers.module.ts +++ b/src/ethers.module.ts @@ -1,5 +1,4 @@ import { Module, DynamicModule } from '@nestjs/common'; -import { Network } from '@ethersproject/providers'; import { EthersCoreModule } from './ethers-core.module'; import { EthersModuleOptions, @@ -8,13 +7,10 @@ import { @Module({}) export class EthersModule { - static forRoot( - network: Network | string = 'homestead', - options: EthersModuleOptions = {}, - ): DynamicModule { + static forRoot(options: EthersModuleOptions = {}): DynamicModule { return { module: EthersModule, - imports: [EthersCoreModule.forRoot(network, options)], + imports: [EthersCoreModule.forRoot(options)], }; } diff --git a/src/ethers.providers.ts b/src/ethers.providers.ts index 8098af8..b016a97 100644 --- a/src/ethers.providers.ts +++ b/src/ethers.providers.ts @@ -3,7 +3,6 @@ import { Provider } from '@nestjs/common'; import { BaseProvider, getDefaultProvider, - Network, FallbackProvider, AlchemyProvider, CloudflareProvider, @@ -22,11 +21,11 @@ import { ETHERS_PROVIDER_NAME, } from './ethers.constants'; -export async function createDefaultProvider( - network: Network | string = 'homestead', +export async function createBaseProvider( options: EthersModuleOptions = {}, ): Promise { const { + network = 'homestead', alchemy, etherscan, infura, @@ -112,15 +111,12 @@ export async function createDefaultProvider( } export function createEthersProvider( - network: Network | string, options: EthersModuleOptions = {}, ): Provider { return { provide: getEthersToken(options?.providerName ?? ''), useFactory: async (): Promise => { - return await defer(() => - createDefaultProvider(network, options), - ).toPromise(); + return await defer(() => createBaseProvider(options)).toPromise(); }, }; } @@ -129,9 +125,7 @@ export function createEthersAsyncProvider(providerName = ''): Provider { return { provide: getEthersToken(providerName ?? ''), useFactory: async (options: EthersModuleOptions): Promise => { - return await defer(() => - createDefaultProvider(options?.network, options), - ).toPromise(); + return await defer(() => createBaseProvider(options)).toPromise(); }, inject: [ETHERS_MODULE_OPTIONS], }; From 0d19ea5983056774d9598e48a01b58112936f67b Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 11:01:26 -0300 Subject: [PATCH 08/15] update readme --- README.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ccf128..3813f76 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ interface EthersModuleOptions { ### Synchronous configuration -Use `EthersModule.forRoot` method with argument of [Params interface](#configuration-params): +Use `EthersModule.forRoot` method with [Options interface](#configuration-params): ```ts import { EthersModule } from 'nestjs-ethers'; @@ -131,10 +131,88 @@ import { EthersModule } from 'nestjs-ethers'; useDefaultProvider: true, }) ], + ... }) class MyModule {} ``` +### Asynchronous configuration + +With `EthersModule.forRootAsync` you can, for example, import your `ConfigModule` and inject `ConfigService` to use it in `useFactory` method. + +`useFactory` should return object with [Options interface](#configuration-params) or undefined + +Here's an example: + +```ts +import { EthersModule } from 'nestjs-ethers'; + +@Injectable() +class ConfigService { + public readonly infura = { + projectId: 'd71b3d93c2fcfa7cab4924e63298575a', + projectSecret: 'ed6baa9f7a09877998a24394a12bf3dc', + }; +} + +@Module({ + providers: [ConfigService], + exports: [ConfigService] +}) +class ConfigModule {} + +@Module({ + imports: [ + EthersModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (config: ConfigService) => { + await somePromise(); + return { + network: 'rinkeby', + infura: config.infura, + useDefaultProvider: false, + }; + } + }) + ], + ... +}) +class TestModule {} +``` + +Or you can just pass `ConfigService` to `providers`, if you don't have any `ConfigModule`: + +```ts +import { EthersModule } from 'nestjs-ethers'; + +@Injectable() +class ConfigService { + public readonly pocket: { + applicationId: '9b0afc55221c429104d04ef9', + applicationSecretKey: 'b5e6d6a55426712a42a93f39555973fc', + }; +} + +@Module({ + imports: [ + LoggerModule.forRootAsync({ + providers: [ConfigService], + inject: [ConfigService], + useFactory: (config: ConfigService) => { + return { + network: 'rinkeby', + pocket: config.pocket, + useDefaultProvider: false, + }; + } + }) + ], + controllers: [TestController] +}) +class TestModule {} +``` + ## Change Log See [Changelog](CHANGELOG.md) for more information. From 6523ad39b6d148cf9449130575d0b5740364d00a Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 11:03:00 -0300 Subject: [PATCH 09/15] update readme --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index 3813f76..dddba4e 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,50 @@ class ConfigService { class TestModule {} ``` +Or you can pass multiple `Providers`, if you want to use the FallbackProvider to send multiple requests simultaneously: + +```ts +import { EthersModule } from 'nestjs-ethers'; + +@Injectable() +class ConfigService { + public readonly infura = { + projectId: 'd71b3d93c2fcfa7cab4924e63298575a', + projectSecret: 'ed6baa9f7a09877998a24394a12bf3dc', + }; + public readonly pocket: { + applicationId: '9b0afc55221c429104d04ef9', + applicationSecretKey: 'b5e6d6a55426712a42a93f39555973fc', + }; +} + +@Module({ + providers: [ConfigService], + exports: [ConfigService] +}) +class ConfigModule {} + +@Module({ + imports: [ + EthersModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (config: ConfigService) => { + await somePromise(); + return { + network: 'rinkeby', + infura: config.infura, + pocket: config.pocket, + useDefaultProvider: false, + }; + } + }) + ], + ... +}) +class TestModule {} +``` + ## Change Log See [Changelog](CHANGELOG.md) for more information. From 1814ddb3f499590c12b9196c33ff60b35b4f56f1 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 11:03:49 -0300 Subject: [PATCH 10/15] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dddba4e..ac16ef0 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ class ConfigService { class TestModule {} ``` -Or you can pass multiple `Providers`, if you want to use the FallbackProvider to send multiple requests simultaneously: +Or you can pass multiple `Providers`, if you want to use the `FallbackProvider` to send multiple requests simultaneously: ```ts import { EthersModule } from 'nestjs-ethers'; From b5ddad648f81ba04a19479b6e88b1ff1e4138958 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 11:40:03 -0300 Subject: [PATCH 11/15] update readme --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ac16ef0..eca42da 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ class ConfigService { @Module({ imports: [ - LoggerModule.forRootAsync({ + EthersModule.forRootAsync({ providers: [ConfigService], inject: [ConfigService], useFactory: (config: ConfigService) => { @@ -257,6 +257,23 @@ class ConfigModule {} class TestModule {} ``` +#### Testing a class that uses @InjectEthersProvider + +This package exposes a getEthersToken() function that returns a prepared injection token based on the provided context. +Using this token, you can easily provide a mock implementation of the `EthersBaseProvider` using any of the standard custom provider techniques, including useClass, useValue, and useFactory. + +```ts + const module: TestingModule = await Test.createTestingModule({ + providers: [ + MyService, + { + provide: getEthersToken(MyService.name), + useValue: mockProvider, + }, + ], + }).compile(); +``` + ## Change Log See [Changelog](CHANGELOG.md) for more information. From 899eb72f5bbb2cb07a37e05e107769420169202b Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 12:56:09 -0300 Subject: [PATCH 12/15] github templates --- .github/ISSUE_TEMPLATE.md | 47 ++++++++++++++++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 43 +++++++++++++++++++++++++++++ package.json | 2 +- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..b11a57f --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,47 @@ + + +## I'm submitting a... + +

+[ ] Regression 
+[ ] Bug report
+[ ] Feature request
+[ ] Documentation issue or request
+[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
+
+ +## Current behavior + + + +## Expected behavior + + + +## Minimal reproduction of the problem with instructions + + +## What is the motivation / use case for changing the behavior? + + +## Environment + +

+Nest version: X.Y.Z
+Nest Ethers version: X.Y.Z
+
+ 
+For Tooling issues:
+- Node version: XX  
+- Platform:  
+- Server:  
+
+Others:
+
+
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..db5736d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,43 @@ +## PR Checklist +Please check if your PR fulfills the following requirements: + +- [ ] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md +- [ ] Tests for the changes have been added (for bug fixes / features) +- [ ] Docs have been added / updated (for bug fixes / features) + + +## PR Type +What kind of change does this PR introduce? + + +``` +[ ] Bugfix +[ ] Feature +[ ] Code style update (formatting, local variables) +[ ] Refactoring (no functional changes, no api changes) +[ ] Build related changes +[ ] CI related changes +[ ] Other... Please describe: +``` + +## What is the current behavior? + + +Issue Number: N/A + + +## What is the new behavior? + + + +## Does this PR introduce a breaking change? +``` +[ ] Yes +[ ] No +``` + + + + +## Other information + diff --git a/package.json b/package.json index b5e9561..5306493 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nestjs-ethers", - "version": "0.1.0-rc.4", + "version": "0.1.0-rc.5", "description": "The ethers.js library for NestJS", "author": "Jose Ramirez ", "license": "Apache", From 9dba561e7595c164b6242831435482f8a0c143a4 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 12:59:29 -0300 Subject: [PATCH 13/15] update CHANGELOG.md --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c478d2..e1d20bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,5 @@ -RELEASE 0.1.1 \ No newline at end of file +# Changelog + +## 0.1.0 +Published by **[jarcodallo](https://github.com/jarcodallo)** on **TODO** +- TODO \ No newline at end of file From 4a10b9822af484fe46a562bc555603f5544b65d7 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 16:08:27 -0300 Subject: [PATCH 14/15] unit test --- .github/ISSUE_TEMPLATE.md | 47 ---------- .github/PULL_REQUEST_TEMPLATE.md | 43 --------- README.md | 6 +- __tests__/ethers.decorators.spec.ts | 11 ++- __tests__/ethers.module.spec.ts | 132 ++++++++++++++++++++++++---- __tests__/utils/constants.ts | 21 ++++- src/ethers.constants.ts | 1 + src/ethers.interface.ts | 2 +- src/ethers.providers.ts | 9 +- src/index.ts | 1 + 10 files changed, 156 insertions(+), 117 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index b11a57f..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,47 +0,0 @@ - - -## I'm submitting a... - -

-[ ] Regression 
-[ ] Bug report
-[ ] Feature request
-[ ] Documentation issue or request
-[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
-
- -## Current behavior - - - -## Expected behavior - - - -## Minimal reproduction of the problem with instructions - - -## What is the motivation / use case for changing the behavior? - - -## Environment - -

-Nest version: X.Y.Z
-Nest Ethers version: X.Y.Z
-
- 
-For Tooling issues:
-- Node version: XX  
-- Platform:  
-- Server:  
-
-Others:
-
-
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index db5736d..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,43 +0,0 @@ -## PR Checklist -Please check if your PR fulfills the following requirements: - -- [ ] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md -- [ ] Tests for the changes have been added (for bug fixes / features) -- [ ] Docs have been added / updated (for bug fixes / features) - - -## PR Type -What kind of change does this PR introduce? - - -``` -[ ] Bugfix -[ ] Feature -[ ] Code style update (formatting, local variables) -[ ] Refactoring (no functional changes, no api changes) -[ ] Build related changes -[ ] CI related changes -[ ] Other... Please describe: -``` - -## What is the current behavior? - - -Issue Number: N/A - - -## What is the new behavior? - - - -## Does this PR introduce a breaking change? -``` -[ ] Yes -[ ] No -``` - - - - -## Other information - diff --git a/README.md b/README.md index eca42da..d792826 100644 --- a/README.md +++ b/README.md @@ -70,10 +70,10 @@ interface EthersModuleOptions { etherscan?: string; /** - * Optional parameter for Cloudflare API Token + * Optional parameter for use Cloudflare Provider * @see {@link https://cloudflare-eth.com} */ - cloudflare?: string; + cloudflare?: boolean; /** * Optional parameter for Infura Project ID @@ -118,7 +118,7 @@ import { EthersModule } from 'nestjs-ethers'; providerName: 'MyModule', alchemy: '845ce4ed0120d68eb5740c9160f08f98', etherscan: 'e8cce313c1cfbd085f68be509451f1bab8', - cloudflare: 'd9f3e29f461338bc70e79961', + cloudflare: true, infura: { projectId: 'd71b3d93c2fcfa7cab4924e63298575a', projectSecret: 'ed6baa9f7a09877998a24394a12bf3dc', diff --git a/__tests__/ethers.decorators.spec.ts b/__tests__/ethers.decorators.spec.ts index 4183389..0160d52 100644 --- a/__tests__/ethers.decorators.spec.ts +++ b/__tests__/ethers.decorators.spec.ts @@ -7,6 +7,7 @@ import { InjectEthersProvider, EthersBaseProvider, Network, + ETHERS_MAINNET_NAME, } from '../src'; import { platforms } from './utils/platforms'; import { extraWait } from './utils/extraWait'; @@ -73,7 +74,10 @@ describe('InjectEthersProvider', () => { .expect(200) .expect((res) => { expect(res.body.network).toBeDefined(); - expect(res.body.network).toHaveProperty('name', 'homestead'); + expect(res.body.network).toHaveProperty( + 'name', + ETHERS_MAINNET_NAME, + ); expect(res.body.network).toHaveProperty('chainId', 1); expect(res.body.network).toHaveProperty('ensAddress'); }); @@ -116,7 +120,10 @@ describe('InjectEthersProvider', () => { .expect(200) .expect((res) => { expect(res.body.network).toBeDefined(); - expect(res.body.network).toHaveProperty('name', 'homestead'); + expect(res.body.network).toHaveProperty( + 'name', + ETHERS_MAINNET_NAME, + ); expect(res.body.network).toHaveProperty('chainId', 1); expect(res.body.network).toHaveProperty('ensAddress'); }); diff --git a/__tests__/ethers.module.spec.ts b/__tests__/ethers.module.spec.ts index 50c76f5..680a1a3 100644 --- a/__tests__/ethers.module.spec.ts +++ b/__tests__/ethers.module.spec.ts @@ -3,7 +3,13 @@ import { NestFactory } from '@nestjs/core'; import { Module, Controller, Get, Injectable } from '@nestjs/common'; import * as request from 'supertest'; import * as nock from 'nock'; -import { EthersModule, InjectEthersProvider, EthersBaseProvider } from '../src'; +import { + EthersModule, + InjectEthersProvider, + EthersBaseProvider, + ETHERS_MAINNET_NAME, + Network, +} from '../src'; import { platforms } from './utils/platforms'; import { extraWait } from './utils/extraWait'; import { @@ -12,14 +18,18 @@ import { RINKEBY_ALCHEMY_POKT_URL, RINKEBY_POKT_API_KEY, RINKEBY_POKT_SECRET_KEY, - RINKEBY_ETHERSCAN_POKT_URL, + RINKEBY_ETHERSCAN_URL, RINKEBY_ETHERSCAN_API_KEY, - RINKEBY_INFURA_POKT_URL, + RINKEBY_INFURA_URL, + CLOUDFLARE_URL, RINKEBY_INFURA_PROJECT_ID, RINKEBY_INFURA_PROJECT_SECRET, ETHERSCAN_GET_GAS_PRICE_QUERY, PROVIDER_GET_GAS_PRICE_BODY, PROVIDER_GET_GAS_PRICE_RESPONSE, + ETHERSCAN_GET_BLOCK_NUMBER_QUERY, + PROVIDER_GET_BLOCK_NUMBER_BODY, + PROVIDER_GET_BLOCK_NUMBER_RESPONSE, } from './utils/constants'; import { BigNumber } from '@ethersproject/bignumber'; @@ -31,14 +41,11 @@ describe('Ethers Module Initialization', () => { nock.activate(); } - // nock.recorder.rec({ dont_print: true }); nock.disableNetConnect(); nock.enableNetConnect('127.0.0.1'); }); afterAll(() => { - // console.log(nock.recorder.play()); - // nock.recorder.clear(); nock.restore(); }); @@ -54,7 +61,7 @@ describe('Ethers Module Initialization', () => { ) {} @Get() async get() { - const network = await this.ethersProvider.getNetwork(); + const network: Network = await this.ethersProvider.getNetwork(); return { network }; } @@ -80,7 +87,10 @@ describe('Ethers Module Initialization', () => { .expect(200) .expect((res) => { expect(res.body.network).toBeDefined(); - expect(res.body.network).toHaveProperty('name', 'homestead'); + expect(res.body.network).toHaveProperty( + 'name', + ETHERS_MAINNET_NAME, + ); expect(res.body.network).toHaveProperty('chainId', 1); expect(res.body.network).toHaveProperty('ensAddress'); }); @@ -196,7 +206,7 @@ describe('Ethers Module Initialization', () => { describe('forRootAsync', () => { it('should compile properly with useFactory', async () => { - nock(RINKEBY_ETHERSCAN_POKT_URL) + nock(RINKEBY_ETHERSCAN_URL) .get('') .query(ETHERSCAN_GET_GAS_PRICE_QUERY) .reply(200, PROVIDER_GET_GAS_PRICE_RESPONSE); @@ -232,6 +242,7 @@ describe('Ethers Module Initialization', () => { inject: [ConfigService], useFactory: (config: ConfigService) => { return { + network: 'rinkeby', etherscan: config.etherscan, useDefaultProvider: false, }; @@ -263,8 +274,8 @@ describe('Ethers Module Initialization', () => { await app.close(); }); - it('should work properly when pass deps via providers', async () => { - nock(RINKEBY_INFURA_POKT_URL) + it('should work properly when pass dependencies via providers', async () => { + nock(RINKEBY_INFURA_URL) .post(`/${RINKEBY_INFURA_PROJECT_ID}`, PROVIDER_GET_GAS_PRICE_BODY) .reply(200, PROVIDER_GET_GAS_PRICE_RESPONSE); @@ -330,9 +341,8 @@ describe('Ethers Module Initialization', () => { }); it('should work properly when useFactory returns Promise', async () => { - nock(RINKEBY_ETHERSCAN_POKT_URL) - .get('') - .query(ETHERSCAN_GET_GAS_PRICE_QUERY) + nock(CLOUDFLARE_URL) + .post('/', PROVIDER_GET_GAS_PRICE_BODY) .reply(200, PROVIDER_GET_GAS_PRICE_RESPONSE); @Controller('/') @@ -351,7 +361,7 @@ describe('Ethers Module Initialization', () => { @Injectable() class ConfigService { - public readonly etherscan = RINKEBY_ETHERSCAN_API_KEY; + public readonly cloudflare = true; } @Module({ @@ -368,7 +378,99 @@ describe('Ethers Module Initialization', () => { await new Promise((r) => setTimeout(r, 20)); return { + cloudflare: config.cloudflare, + useDefaultProvider: false, + }; + }, + }), + ], + controllers: [TestController], + }) + class TestModule {} + + const app = await NestFactory.create( + TestModule, + new PlatformAdapter(), + { logger: false }, + ); + const server = app.getHttpServer(); + + await app.init(); + await extraWait(PlatformAdapter, app); + + await request(server) + .get('/') + .expect(200) + .expect((res) => { + expect(res.body).toBeDefined(); + expect(res.body).toHaveProperty('gasPrice', '1000000000'); + }); + + await app.close(); + }); + + it('should work properly when useFactory uses more than one Provider', async () => { + nock(RINKEBY_INFURA_URL) + .post(`/${RINKEBY_INFURA_PROJECT_ID}`, { + ...PROVIDER_GET_GAS_PRICE_BODY, + id: 43, + }) + .reply(200, PROVIDER_GET_GAS_PRICE_RESPONSE) + .post( + `/${RINKEBY_INFURA_PROJECT_ID}`, + PROVIDER_GET_BLOCK_NUMBER_BODY, + ) + .reply(200, PROVIDER_GET_BLOCK_NUMBER_RESPONSE); + + nock(RINKEBY_ETHERSCAN_URL) + .get('/') + .query({ + ...ETHERSCAN_GET_GAS_PRICE_QUERY, + id: 43, + }) + .reply(200, PROVIDER_GET_GAS_PRICE_RESPONSE) + .get('/') + .query(ETHERSCAN_GET_BLOCK_NUMBER_QUERY) + .reply(200, PROVIDER_GET_BLOCK_NUMBER_RESPONSE); + + @Controller('/') + class TestController { + constructor( + @InjectEthersProvider() + private readonly ethersProvider: EthersBaseProvider, + ) {} + @Get() + async get() { + const gasPrice: BigNumber = await this.ethersProvider.getGasPrice(); + + return { gasPrice: gasPrice.toString() }; + } + } + + @Injectable() + class ConfigService { + public readonly etherscan = RINKEBY_ETHERSCAN_API_KEY; + public readonly infura = { + projectId: RINKEBY_INFURA_PROJECT_ID, + projectSecret: RINKEBY_INFURA_PROJECT_SECRET, + }; + } + + @Module({ + providers: [ConfigService], + exports: [ConfigService], + }) + class ConfigModule {} + @Module({ + imports: [ + EthersModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => { + return { + network: 'rinkeby', etherscan: config.etherscan, + infura: config.infura, useDefaultProvider: false, }; }, diff --git a/__tests__/utils/constants.ts b/__tests__/utils/constants.ts index 48b8377..4979ff0 100644 --- a/__tests__/utils/constants.ts +++ b/__tests__/utils/constants.ts @@ -3,8 +3,9 @@ import { randomBytes } from 'crypto'; export const RINKEBY_ALCHEMY_BASE_URL = 'https://eth-rinkeby.alchemyapi.io/v2'; export const RINKEBY_ALCHEMY_POKT_URL = 'https://eth-rinkeby.gateway.pokt.network/v1'; -export const RINKEBY_ETHERSCAN_POKT_URL = 'https://api.etherscan.io/api'; -export const RINKEBY_INFURA_POKT_URL = 'https://rinkeby.infura.io/v3'; +export const RINKEBY_ETHERSCAN_URL = 'https://api-rinkeby.etherscan.io/api'; +export const RINKEBY_INFURA_URL = 'https://rinkeby.infura.io/v3'; +export const CLOUDFLARE_URL = 'https://cloudflare-eth.com'; export const RINKEBY_ETHERSCAN_API_KEY = randomBytes(17).toString('hex'); export const RINKEBY_ALCHEMY_API_KEY = randomBytes(16).toString('hex'); export const RINKEBY_POKT_API_KEY = randomBytes(12).toString('hex'); @@ -27,3 +28,19 @@ export const ETHERSCAN_GET_GAS_PRICE_QUERY = { action: 'eth_gasPrice', apikey: RINKEBY_ETHERSCAN_API_KEY, }; +export const ETHERSCAN_GET_BLOCK_NUMBER_QUERY = { + module: 'proxy', + action: 'eth_blockNumber', + apikey: RINKEBY_ETHERSCAN_API_KEY, +}; +export const PROVIDER_GET_BLOCK_NUMBER_BODY = { + method: 'eth_blockNumber', + params: [], + id: 42, + jsonrpc: '2.0', +}; +export const PROVIDER_GET_BLOCK_NUMBER_RESPONSE = { + jsonrpc: '2.0', + id: 42, + result: '0x802f1c', +}; diff --git a/src/ethers.constants.ts b/src/ethers.constants.ts index 25780e3..45a9eee 100644 --- a/src/ethers.constants.ts +++ b/src/ethers.constants.ts @@ -1,3 +1,4 @@ export const DECORATED_PREFIX = 'EthersJS'; export const ETHERS_PROVIDER_NAME = 'EthersProviderName'; export const ETHERS_MODULE_OPTIONS = 'EthersModuleOptions'; +export const ETHERS_MAINNET_NAME = 'homestead'; diff --git a/src/ethers.interface.ts b/src/ethers.interface.ts index 3db396a..cc4e71f 100644 --- a/src/ethers.interface.ts +++ b/src/ethers.interface.ts @@ -16,7 +16,7 @@ export interface EthersModuleOptions extends Record { providerName?: string; alchemy?: string; etherscan?: string; - cloudflare?: string; + cloudflare?: boolean; infura?: InfuraProviderOptions | string; pocket?: PocketProviderOptions | string; quorum?: number; diff --git a/src/ethers.providers.ts b/src/ethers.providers.ts index b016a97..124914f 100644 --- a/src/ethers.providers.ts +++ b/src/ethers.providers.ts @@ -19,18 +19,19 @@ import { getEthersToken } from './ethers.utils'; import { ETHERS_MODULE_OPTIONS, ETHERS_PROVIDER_NAME, + ETHERS_MAINNET_NAME, } from './ethers.constants'; export async function createBaseProvider( options: EthersModuleOptions = {}, ): Promise { const { - network = 'homestead', + network = ETHERS_MAINNET_NAME, alchemy, etherscan, infura, pocket, - cloudflare, + cloudflare = false, quorum = 1, useDefaultProvider = true, } = options; @@ -74,8 +75,8 @@ export async function createBaseProvider( providers.push(pocketProvider); } - if (cloudflare) { - const cloudflareProvider = new CloudflareProvider(network, cloudflare); + if (cloudflare && network === ETHERS_MAINNET_NAME) { + const cloudflareProvider = new CloudflareProvider(network); // wait until the node is up and running smoothly. await cloudflareProvider.ready; diff --git a/src/index.ts b/src/index.ts index a76355a..1e97c7e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ export { EthersModuleOptions, EthersModuleAsyncOptions, } from './ethers.interface'; +export { ETHERS_MAINNET_NAME } from './ethers.constants'; export { getEthersToken } from './ethers.utils'; export { BaseProvider as EthersBaseProvider, From 4c3a21846278d23569066a237ed71eee25ac6579 Mon Sep 17 00:00:00 2001 From: Jose Ramirez Date: Tue, 13 Apr 2021 17:18:53 -0300 Subject: [PATCH 15/15] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d792826..b22e1db 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ class ConfigService { class TestModule {} ``` -Or you can pass multiple `Providers`, if you want to use the `FallbackProvider` to send multiple requests simultaneously: +You can also pass multiple `ethersjs` configs, if you want to use the `FallbackProvider` to send multiple requests simultaneously: ```ts import { EthersModule } from 'nestjs-ethers';