Context
@kleros is improving the way we onboard users.
Instead of simply throwing a Metamask popup on the user's face as soon as (s)he visits our dapps, we will eventually move to an opt-in approach: the users will be asked for a wallet only when needed. Furthermore, the (s)he will also be able to choose a wallet provider.
Right now, beyond Metamask, we are trying to add support to Fortmatic.
To make things easier, we decided use web3-react
, because of its concept of connectors, which allow for easily switching different wallet providers, and the fact that it already has built-in connectorn for both Metamask (what they call InjectedConnector) and Fortmatic.
The Problem
As this project is setup today, @web3-react/fortmatic-connector
is broken. It fails to properly import the fortmatic
module which it depends upon.
The issue seems to happen here:
const { default: Fortmatic } = await import('fortmatic')
What they expect is the fortmatic
package to export an ES module, but I checked the tarball of [email protected]
and it exposes only a CJS module.
Apparently, when dynamic importing a CJS module with the current Linguo setup ([email protected]
+ [email protected]
), instead of wrapping it into an ES module that would look like export default module.exports
, it simply imports the CJS module as is.
Current Workaround
The solution for the FortmaticConnector
was not to use the provided @web3-react/fortmatic-connector
, instead, roll my own. In fact, it's basically a CTRL+C/CTRL+V of the original package code, the only difference was the way the import to fortmatic
is made.
// ...
-const { default: Fortmatic } = await import('fortmatic');
+const Fortmatic = await import('fortmatic');
// ...
Actually, to support both cases, I have:
let Fortmatic = await import('fortmatic');
Fortmatic = Fortmatic.default || Fortmatic;
Reproducing The Problem
-
Clone this repo: git clone https://github.com/kleros/linguo.git
-
Install dependencies: yarn install
-
Go to connectors.js
file.
-
Change the import of FortmaticConnector
:
@@ -1,5 +1,5 @@
import { InjectedConnector } from '@web3-react/injected-connector';
import { NetworkConnector } from '@web3-react/network-connector';
-import { FortmaticConnector } from '~/adapters/web3-react/FortmaticConnector';
+import { FortmaticConnector } from '@web3-react/fortmatic-connector';
-
Run the development server: yarn start
;
-
Go to http://localhost:1234
-
Look for the "gear" button to access the app settings, then click in "connect to wallet":
-
In the modal that will open, choose Fortmatic:
-
You will probably see the following error in the console:
TypeError: "Fortmatic is not a constructor"
_temp4 index.ts:34
web3React.js:17:12
getErrorMessage web3React.js:17
WalletConnectionModal WalletConnectionModal.jsx:87
React 8
unstable_runWithPriority scheduler.development.js:653
React 5
createHandleActivation WalletConnectionModal.jsx:56
IMPORTANT: I can't reproduce the problem outside the Linguo repo:
- I tried to reproduce that using a codesandbox, but here the original package does work.
- I tried to setup a new local repo with some of the configurations for Linguo (babel, parcel, etc.), but in this case I get only a blank screen, with no error.
Hypotheses
This seems like a problem of CJS/ES modules interoperability. I can think of some reasons:
- A bug within
parcel
dynamic module importing.
- Maybe something related to our
.browserslistrc
.
- A problem with our
.babelrc
, which is causing the code to not be properly transpiled.
- A bug in
babel
itself.
General Observations
- I did tried to clean
parcel
cache and start the dev server again. The results were the same.
Things I observed in the Linguo repo:
- Dynamic importing ES modules that are in the project, but outside
node_modules
works as expected.
- Dynamic importing CJS modules that are in the project, but outside
node_modules
works as as described here: the CJS module is not wrapped into an ES module; the promise returned by import()
is resolved with the value of module.exports
.
- I'm not sure this is the expected behavior.