ethers.js: React Native: Solutions for slow crypto-operations (`Wallet.createRandom()`)
A lot of issues have been opened about React Native being slow. For example, creating a wallet takes 33 seconds on an iPhone 11 Pro - I don’t even want to find out how slow that is on an older Android device.
This code:
import 'react-native-get-random-values'
import '@ethersproject/shims'
import { ethers } from 'ethers'
const now = performance.now()
Logger.log('💰 Creating new Wallet...')
const wallet = ethers.Wallet.createRandom()
const end = performance.now()
Logger.log(
`💰 New wallet created! Took ${end - now}ms, Phrase: ${wallet.mnemonic.phrase}`,
)
Takes half a minute:
[16:49:19.253] 💰 Creating new Wallet...
[16:49:52.812] 💰 New wallet created! Took 33559.440958321095ms, Phrase: ...
I’m wondering if there is any workaround to make this code faster, as it’s definitely not a solution to let the user wait for 5 minutes until he can use the app.
I can create a library for the crypto polyfills that uses a pure C/C++ implementation accessed synchronously through JSI for maximum performance, but I’d need help to find out what algorithms are slow and where I can swap them.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 2
- Comments: 49 (5 by maintainers)
I did it.
@zemse @mirceanis @ricmoo how do I swap the pbkdf2 function?
The one from this library takes 14 seconds:
and my pure C++ JSI implementation takes 0.001 second:
…so it’s almost 10.000x faster.
I’d love to make that open source if there’s a way to swap the implementations.
Step 1: create a patch js file with the following content:
Step 2: update the
module-resolver
config inbabel.config.js
:Step 3:
npm start --reset-cache
Hey - we’ve just published the fully native C++ library for Crypto. 🎉🥳
As I said before, doing crypto in JS simply won’t work for mobile apps (React Native) since JS is too slow in such cases. That’s why libraries like BN.js or crypto-browserify are a huge bottleneck in React Native applications. Instead, we at Margelo implemented those functions fully natively in C++ to offer huge speedups (in BN.js we measured up to 300x improvements and in QuickCrypto up to 58x improvements!)
As of today, I recommend to install both react-native-quick-crypto and react-native-bignumber in your projects and use them as drop-in replacements for free app speedups!
Install:
Then add them to your babel.config.js (see the READMEs for details).
This makes ethers.js run smoothly on React Native! Would be cool if we could add that to the README or docs of ethers.js? cc @ricmoo
Do you have an ETA for v6?
@ricmoo ethers.js currently doesn’t use
crypto
(norreact-native-quick-crypto
) if it is available, not sure where the try/import/catch is happening (or if you guys are doing that), but just wanted to make sure that y’all are aware of this.I had to manually patch
node_modules/@ethersproject/pbkdf2/...
to use ourcrypto.pbkdf2Sync
function (which is implemented natively in react-native-quick-crypto) to improve the performance for wallet creation.Before patching
@ethersproject/pbkdf2
(with your JS-based implementation):After patching
@ethersproject/pbkdf2
(with our native C++/JSI react-native-quick-crypto implementation):Here’s the patch I used:
@ethersproject+pbkdf2+5.6.1.patch
You can DM me on Twitter if you have any questions or want to collab on getting this integrated with ethers.js. 😃
Just as an FYI, I’ve been adding an option to v6 to register custom implementations for certain crypto methods, so this should be easier to swap out in v6.
So I patched
@ethersproject/pbkdf2/lib/browser-pbkdf2.js
to use my custom C++ implementation instead of the JS one, and here’s the results:Before
After
…so I brought it down from 45 seconds to 1 second, a 45x performance improvement.
My patch
I still wonder why it takes a full second to import the wallet from the passphrase, I am even using the native C++
atob
,btoa
andBuffer
implementations, will debug further - but at least now the app is useable.Thank you to everyone in this thread and particularly @ricmoo and @mrousavy for their work with these libraries.
Using the thread above I was able to get Ethers v6.6 and react-native-quick-crypto (RNQC) working with RN. Without using RNQC Ethers v6.6 was able to create a wallet in around 1300ms which was a big improvement from Ethers v5.7 (around 5500ms). However when using RNQC, Ethers V6.6 creates a new wallet in 300ms dropping to 50ms for any additional requests.
I will include my setup for anyone wishing to work with Ethers v6:
Then imported ‘ethers-setup’ into the App.tsx file.
I now import this into my project instead of the usual import { ethers } from ‘ethers’;
This is available in v6, using the
.register
functions on each of the hash, hmac, etc. functions.For example:
See the
src.ts/crypto/
files for more details. I’ll be adding more docs for RN, Expo, etc. to help too.Thanks! 😃
Yes, I did.
I’m working on releasing the package as open-source today or tomorrow, stay tuned. Follow me on Twitter for the announcement
He already said it’s closed source
Yeah great point @mrousavy. I am intrigued to see whether the encrypt wallet method could be improved as it takes 30 seconds+ during testing. It seems this process relies on scrypt but I struggled to get the RNQC library to work with ethers, using the ethers.scrypt.register approach.
I did have a question @mrousavy if you could confirm the security of the native implementations? Ethers uses noble-hashes which has been audited. Can you think of any security issues that could arise by replacing these functions with native implementations you have written?
@mrousavy amazing work! Improvement was from
4108ms
down to210ms
replacing@ethersproject/pbkdf2/lib/browser-pbkdf2
with your implementation ofpbkdf2Sync
🥳@anarkrypto I was using an expo managed workflow and apply the
@ethersproject/project/pbkdf2
patch applied above. However I found that it won’t work in the managed workflow, and you’ll have to eject to bare workflow if you want it to work due to the native code modification that will happen under the hood. It’s not enough to apply the changes in thebabel.config.js
you have to patch the pbkdf2 in order to ensure that ethers works performantly when creating new wallets. Hope this helps. I can confirm that the patch does work and lowers wait times from ~6-10 seconds [depending on the device, beforehand it was 6 seconds on new iPhones like the iPhone 13 pro, and 10-12 seconds on iPhone 7s, all the way to sub to single second waits for all phone’s on all builds for new wallet creation in production builds for iOS, I haven’t built it yet for android but I assume that it works as well, but maybe somebuild.gradle
changes will have to be made.Note: I got the error that it doesn’t work in Expo Go and that’s exactly the point remember this is drop in native code, you’re going to have to use less convenient local building tools like the react-native cli or expo dev-client builds from now on to build the app for emulators locally, and the nice things that come with testing expo go on a real phone on the same network also goes away, hope this helps 😄