MetaMask / metamask-sdk

The simplest yet most secure way to connect your blockchain-based applications to millions of MetaMask Wallet users.
https://metamask.io/sdk/
Other
189 stars 119 forks source link

[Bug]: Expo Sample App not work after updating the SDK version, send Transaction Not work with error shown #1094

Open ram4444 opened 1 month ago

ram4444 commented 1 month ago

SDK

React-Native

Provide environment information

OS: Arch Linux Android Simulator SDK 34

MetaMask SDK Version

"@metamask/sdk-react": "^0.18.6", "^0.30.0",

MetaMask Mobile app Version

N/A

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

Expo

Describe the Bug

Here is my sample app which is working with dependencies in package,json below

"expo": "~49.0.9",
"@metamask/sdk-react": "^0.18.6",
"eciesjs": "0.3.20", <-not shown but I can find it in node module

As there is some dependency requires me to upgrade my expo to 51.0.0, so finally I start from zero and make a new project with package.json:

"expo": "^51.0.0",
"@metamask/sdk-react": "^0.30.0",
"eciesjs": "0.4.8", <-not shown but I can find it in node module

However the app no longer work with error message of

SDKConfigProvider not found or unable to resolve @ecies/ciphers/aes from node_modules/eciesjs/dist/utils/symmetric.js

even I downgrade the eciesjs as

"expo": "^51.0.0",
"@metamask/sdk-react": "^0.30.0",
"eciesjs": "0.3.20", <-which I think it is OK with the previous setting

it is still not working

I got the same error message when I try it on the exposample app (with an updated sdk version) from https://github.com/MetaMask/metamask-sdk/tree/main/packages/examples/expo-demo

Everything is working fine with package.json below so I don't think it is am expo issue

"expo": "^51.0.0",
"@metamask/sdk-react": "^0.18.6",
"eciesjs": "0.3.20", 

Expected Behavior

Expo can show up my app without error

Link to reproduction - Issues with a link to complete (but minimal) reproduction code will be addressed faster

No response

To Reproduce

npx expo run:android

ram4444 commented 4 weeks ago

Please refer to my discussion with the ecies author @kigawas Unable to resolve "@ecies/ciphers/aes" from "node_modules/eciesjs/dist/utils/symmetric.js" #808

phabiulla commented 3 weeks ago

Any news about this?! I'm facing the same issue..

ram4444 commented 3 weeks ago

Author of ecies/cipher has a fix but the issue remains the same after I have applied it to my project package.json

kigawas commented 3 weeks ago

Any news about this?! I'm facing the same issue..

@phabiulla check this react native demo

https://github.com/ecies/js-rn-demo

ram4444 commented 3 weeks ago

@kigawas sorry for any misleading to everyone. cause I am using expo example for my project and I will try to remove it using react native alone. But I think there should be some update to prove those examples from MetaMaskSDK repo are working perfectly.

kigawas commented 3 weeks ago

For expo example config.resolver.unstable_enablePackageExports=true is needed in metro.config.js

ram4444 commented 3 weeks ago

@kigawas I have traced from the error log when launching the app. As the error log is showing something about ecies therefore I contacted you for a luck try. The issue may not be on your side.

My current issue is that with the old version of MetaMaskSDK, it is possible to launch the app but not able to deep link to the transaction screen using the transaction button provided (only switching to the MetaMask app) So I hope an update for MetaMaskSDK could make it works. Therefore I update the MetaMaskSDK but the result is what I have shown not able to launch the app

ram4444 commented 3 weeks ago

with config.resolver.unstable_enablePackageExports=true I got the error of

 ERROR  TypeError: Cannot read property 'createHash' of undefined, js engine: hermes [Component Stack]
 ERROR  TypeError: Cannot read property 'createHash' of undefined, js engine: hermes [Component Stack]
 ERROR  TypeError: Cannot read property 'MetaMaskProvider' of undefined

This error is located at:
    in App (created by Index)
    in Index
    in Unknown (created by Route(index))
    in Suspense (created by Route(index))
    in Route (created by Route(index))
    in Route(index) (created by SceneView)
    in StaticContainer
    in EnsureSingleNavigator (created by SceneView)
    in SceneView (created by SceneView)
    in RCTView (created by View)
    in View (created by DebugContainer)
    in DebugContainer (created by MaybeNestedStack)
    in MaybeNestedStack (created by SceneView)
    in RCTView (created by View)
    in View (created by SceneView)
    in RNSScreen (created by Animated(Anonymous))
    in Animated(Anonymous) (created by InnerScreen)
    in Suspender (created by Freeze)
    in Suspense (created by Freeze)
    in Freeze (created by DelayedFreeze)
    in DelayedFreeze (created by InnerScreen)
    in InnerScreen (created by Screen)
    in Screen (created by SceneView)
    in SceneView (created by NativeStackViewInner)
    in Suspender (created by Freeze)
    in Suspense (created by Freeze)
    in Freeze (created by DelayedFreeze)
    in DelayedFreeze (created by ScreenStack)
    in RNSScreenStack (created by ScreenStack)
    in ScreenStack (created by NativeStackViewInner)
    in NativeStackViewInner (created by NativeStackView)
    in RCTView (created by View)
    in View (created by SafeAreaProviderCompat)
    in SafeAreaProviderCompat (created by NativeStackView)
    in NativeStackView (created by NativeStackNavigator)
    in PreventRemoveProvider (created by NavigationContent)
    in NavigationContent
    in Unknown (created by NativeStackNavigator)
    in NativeStackNavigator
    in Unknown (created by RootLayout)
    in RootLayout
    in Unknown (created by Route())
    in Suspense (created by Route())
    in Route (created by Route())
    in Route() (created by ContextNavigator)
    in RNCSafeAreaProvider (created by SafeAreaProvider)
    in SafeAreaProvider (created by wrapper)
    in wrapper (created by ContextNavigator)
    in EnsureSingleNavigator
    in BaseNavigationContainer
    in ThemeProvider
    in NavigationContainerInner (created by ContextNavigator)
    in ContextNavigator (created by ExpoRoot)
    in ExpoRoot (created by App)
    in App (created by ErrorOverlay)
    in ErrorToastContainer (created by ErrorOverlay)
    in ErrorOverlay (created by withDevTools(ErrorOverlay))
    in withDevTools(ErrorOverlay)
    in RCTView (created by View)
    in View (created by AppContainer)
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer
    in main(RootComponent), js engine: hermes
 ERROR  TypeError: Cannot read property 'MetaMaskProvider' of undefined

This error is located at:
    in App (created by Index)
    in Index
    in Unknown (created by Route(index))
    in Suspense (created by Route(index))
    in Route (created by Route(index))
    in Route(index) (created by SceneView)
    in StaticContainer
    in EnsureSingleNavigator (created by SceneView)
    in SceneView (created by SceneView)
    in RCTView (created by View)
    in View (created by DebugContainer)
    in DebugContainer (created by MaybeNestedStack)
    in MaybeNestedStack (created by SceneView)
    in RCTView (created by View)
    in View (created by SceneView)
    in RNSScreen (created by Animated(Anonymous))
    in Animated(Anonymous) (created by InnerScreen)
    in Suspender (created by Freeze)
    in Suspense (created by Freeze)
    in Freeze (created by DelayedFreeze)
    in DelayedFreeze (created by InnerScreen)
    in InnerScreen (created by Screen)
    in Screen (created by SceneView)
    in SceneView (created by NativeStackViewInner)
    in Suspender (created by Freeze)
    in Suspense (created by Freeze)
    in Freeze (created by DelayedFreeze)
    in DelayedFreeze (created by ScreenStack)
    in RNSScreenStack (created by ScreenStack)
    in ScreenStack (created by NativeStackViewInner)
    in NativeStackViewInner (created by NativeStackView)
    in RCTView (created by View)
    in View (created by SafeAreaProviderCompat)
    in SafeAreaProviderCompat (created by NativeStackView)
    in NativeStackView (created by NativeStackNavigator)
    in PreventRemoveProvider (created by NavigationContent)
    in NavigationContent
    in Unknown (created by NativeStackNavigator)
    in NativeStackNavigator
    in Unknown (created by RootLayout)
    in RootLayout
    in Unknown (created by Route())
    in Suspense (created by Route())
    in Route (created by Route())
    in Route() (created by ContextNavigator)
    in RNCSafeAreaProvider (created by SafeAreaProvider)
    in SafeAreaProvider (created by wrapper)
    in wrapper (created by ContextNavigator)
    in EnsureSingleNavigator
    in BaseNavigationContainer
    in ThemeProvider
    in NavigationContainerInner (created by ContextNavigator)
    in ContextNavigator (created by ExpoRoot)
    in ExpoRoot (created by App)
    in App (created by ErrorOverlay)
    in ErrorToastContainer (created by ErrorOverlay)
    in ErrorOverlay (created by withDevTools(ErrorOverlay))
    in withDevTools(ErrorOverlay)
    in RCTView (created by View)
    in View (created by AppContainer)
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer
    in main(RootComponent), js engine: hermes
Apcozar commented 3 weeks ago

I´m working on a project and faced the same issue, I struggled trying multiple things, but this was the one working. Idk if i´m using best practices here but this solved my problems: Here for app/_layout.tsx:

import 'node-libs-expo/globals';
import 'react-native-url-polyfill/auto';
import 'react-native-get-random-values';

import { MetaMaskProvider } from "@metamask/sdk-react";
import { Stack } from "expo-router";
import { Linking } from "react-native";

export default function RootLayout() {
  return (
    <MetaMaskProvider
      sdkOptions={{
        dappMetadata: {
          name: 'yourapp',
          url: 'yourappurl',
        },
        openDeeplink: (link) => {
          Linking.openURL(link);
        }
      }}
    >
      <Stack>
        <Stack.Screen name="index" />
        <Stack.Screen name="home" />
      </Stack>
    </MetaMaskProvider>
  );
}

This for the login button index.tsx:

import React from 'react';
import { View, Button } from 'react-native';
import { useRouter } from 'expo-router';
import { useSDK } from '@metamask/sdk-react';

export default function LoginScreen() {
  const router = useRouter();
  const { provider } = useSDK();

  const handleLogin = async () => {
    try {

      if (!provider) {
        console.error('Provider not available');
        return;
      }

      const accounts = await provider.request({ method: 'eth_requestAccounts' }) as string[];
      console.log(accounts);
      if (accounts && accounts.length > 0) {
        router.push({
          pathname: './home',
          params: { account: accounts[0] },
        });
      }
    } catch (error) {
      console.error('Error connecting with metamask:', error);
    }
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button title="Conectar con MetaMask" onPress={handleLogin} />
    </View>
  );
}

And my metro.config.js:

// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

config.resolver.extraNodeModules = {
    ...require('node-libs-expo'),
    crypto: require.resolve('crypto-browserify'),
};

module.exports = config;

Using: "@metamask/sdk-react": "^0.30.0", "expo": "~51.0.28",

If you have any suggestion to improve my code, I will appreciate. Hope I´m helping someone!

ram4444 commented 3 weeks ago

@Apcozar @kigawas

Using the package.json dependency from @Apcozar I still got the message of Unable to resolve "@ecies/ciphers/aes" from "node_modules/eciesjs/dist/utils/symmetric.js"

However after npm uninstall eciesjs I can confirm everythings works fine.

Anyway thanks for your help but I think the example code should have a update since I was starting from v0.18.6 and those demo apps RNonly/expo both have come differences in code and also config. Quite frustrated by trying everything out by guess & luck for days and days

Last question May I know I should use yarn add / npm install / expo add for adding new dependencies to React Native Project?

ram4444 commented 2 weeks ago

After I have a try on updating @metamask/sdk to 0.30.2 as I notice there is an dependency update on eciesjs under @metamask/sdk-communication-layer, the app cannot be launched again any more

image

what I can do is

  1. turn it back to the backed-up package-lock.json
  2. yarn install
  3. npm uninstall eciesjs

to make it works again. I can just launched the app,deeplink to the transaction screen but not sure there will be any issue for further steps
npm is a nightmare at all

kigawas commented 2 weeks ago

I recommend metamask/sdk to bump its dependency to use eciesjs v0.4, which is more multi platform friendly. You only need to polyfill Buffer (for React Native crypto.getRandomValues is also needed)

ram4444 commented 2 weeks ago

@kigawas image image the recent update has the latest eciesjs but it will show up error "@ecies/ciphers/aes" from "node_modules/eciesjs/dist/utils/symmetric.js"

and after adding config.resolver.unstable_enablePackageExports=true the error comes to

image

I think the unstable_enablePackageExports alter the other common dependencies behaviors like axios which is very common image

so i dont think it is a good way to add that line in metro config + the previous trick no longer works

kigawas commented 2 weeks ago

axios is very problematic, can you use undici instead?

ram4444 commented 2 weeks ago

axios is very problematic, can you use undici instead?

After removing axios with config.resolver.unstable_enablePackageExports=true it is ok.

But how come it will make you a conclusion that axios is problematic I wanna know cause It is a common tools for http request. How about the other choices?

update: with @metamask/sdk v0.30.2 pressing sendTransaction in the sample app shows error (which did not happened in 0.30.0)
[TypeError: v.persistAccounts is not a function (it is undefined)]

kigawas commented 2 weeks ago

Many common npm libraries are already outdated and full of glitches like axios, webpack, jest etc.

axios is common because it's old, now you should just use fetch api. If you need performance or other functionality like proxy agent, use undici

ram4444 commented 2 weeks ago

seems undici is not working on react native proj and I am now using the build-in fetch instead but once everythings works fine last night, it turns to back to error today. Extremely frustrated by the dependency issue

kigawas commented 2 weeks ago

You can pin metamask sdk version to 0.30.0

ram4444 commented 2 weeks ago

After deleting of node_modules, yarn install, and changed back to index.tsx recommend by @Apcozar I can make it works in metamask-sdk@v0.30.2 app is launch-able in android simulator sendTransaction in the demo screen is able to switch to metamask app and trigger a transaction.

but I don't recommend to close this issue cause error messages ERROR RemoteCommunication::on "wallet_init" -- error [TypeError: r.persistAccounts is not a function (it is undefined)] [TypeError: v.persistAccounts is not a function (it is undefined)] will be shown in some cases randomly.

I am afraid to tell this js lib metamask-sdk is still very buggy in react-native environment when some major library like react-native, expo is being updated in the future as well as the metamask app itself.

Thanks @kigawas for assist

Apcozar commented 2 weeks ago

Hi everyone, I just found this way of connecting wallets to my app. Really helpful and smooth: https://docs.reown.com/appkit/overview It´s from walletconnetc. @ram4444