metamask-mobile icon indicating copy to clipboard operation
metamask-mobile copied to clipboard

Connect Metamask with a native mobile app built with Flutter

Open 0xCaso opened this issue 3 years ago • 54 comments

Hi everyone. I've been researching a lot online but I can't figure out how to perform a connection between a Mobile app (which we're building with Flutter) and Metamask.

Connecting with a blockchain isn't a problem, I just have to figure out how to take private keys from a Metamask account in order to interact with the chain (for know I'm using a private key imported manually, but would be great to have a "connect wallet" button which opens the Metamask Mobile app, asking for permissions).

I found about "deeplinking" and interacting with WalletConnect, but still can't find anything for Flutter Mobile, just Web versions (which is useless).

0xCaso avatar Feb 12 '22 14:02 0xCaso

Hi, I have the same problem. Stuck with researching more data for opening Matamusk app and auto sync wallets. Which packages did you use for blockchain connection inside Flutter Mobile? Have you thought about something like external_app_luncher https://pub.dev/packages/external_app_launcher?

pozniejzmienie avatar Feb 15 '22 09:02 pozniejzmienie

Hi! Yesterday I managed to make the connection using https://pub.dev/documentation/walletconnect_dart/latest/. As they suggest, you should also use https://pub.dev/packages/url_launcher.

Basically you should launch the uri (in the format wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=91303dedf64285cbbaf9120f6e9d160a5c8aa3deb67017a3874cd272323f48ae) when the user presses the button "Connect Wallet".

Check also this https://docs.walletconnect.com/mobile-linking

For blockchain connection I used https://pub.dev/packages/dart_web3

0xCaso avatar Feb 15 '22 09:02 0xCaso

Hi again Matteo, could you show me your logical steps you did, when implementing connections? I read what you have showed me and more, but can't put it together in a logical whole... :/

EDIT: should be like this?

  1. By deep linking create wallet connections beeten Fluter app and Metamask app
  2. Then connect to proper chain
  3. By using private keys I'll be able to do transactions directly from Flutter app as Metamask account

Is it correct way of thinking?

pozniejzmienie avatar Feb 16 '22 07:02 pozniejzmienie

Basically you should merge these two examples:

  • https://github.com/pay-2-bit-human/dart_web3/tree/main/example (ignore metamask folder as it's for web and not mobile)
  • https://github.com/RootSoft/walletconnect-dart-sdk/tree/master/example/mobile
  1. Create a file with your contract's ABI, contract.abi.json.
  2. From this you can generate contract.g.dart, with the command flutter pub run build_runner build.
  3. Then you'll need the class WalletConnectEthereumCredentials from https://github.com/RootSoft/walletconnect-dart-sdk/blob/master/example/mobile/lib/ethereum_transaction_tester.dart, in order to send transactions.
  4. Now you can write this function, maybe the code is not reliable but this is what I've written:
_walletConnect() async {
    final connector = WalletConnect(
      bridge: 'https://bridge.walletconnect.org',
      clientMeta: const PeerMeta(
        name: 'WalletConnect',
        description: 'WalletConnect Developer App',
        url: 'https://walletconnect.org',
        icons: [
          'https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media'
        ],
      ),
    );
    // Subscribe to events
    connector.on('connect', (session) => print(session));
    connector.on('session_update', (payload) => print(payload));
    connector.on('disconnect', (session) => print(session));

    // Create a new session
    if (!connector.connected) {
      session = await connector.createSession(
          chainId: 43113,
          onDisplayUri: (uri) async => {print(uri), await launch(uri)});
    }

    setState(() {
      account = session.accounts[0];
    });

    if (account != null) {
      final client = Web3Client(rpcUrl, Client());
      EthereumWalletConnectProvider provider =
          EthereumWalletConnectProvider(connector);
      credentials = WalletConnectEthereumCredentials(provider: provider);
      yourContract = YourContract(address: contractAddr, client: client);
    }
  }

This function has to be called when user presses your "Connect Wallet" button, something like this:

Row(
      children: [
        ElevatedButton(
          style: ElevatedButton.styleFrom(
            primary: Colors.white24,
            padding: const EdgeInsets.all(16.0),
            textStyle: const TextStyle(fontSize: 22, fontFamily: 'Poppins'),
          ),
          onPressed: () async => _walletConnect(),
          child: const Text('Connect Wallet'),
        ),
      ],
      mainAxisAlignment: MainAxisAlignment.center,
) 

Note you can use every EVM compatible chain: here I'm using Avalanche Fuji Testnet (chainId: 43113); Also I put session, account, credentials and yourContract as global variables cause you could need them in other functions. rpcUrl in my case is https://api.avax-test.network/ext/bc/C/rpc. await launch(uri) will let the user chose the wallet to connect, then it will open the wallet app, for example Metamask. Then user will allow (or not) the connection between app and Metamask.

  1. You can now interact with your smart contract. For getter methods (view-functions in Solidity), you won't need to build the object transaction, but if you have to "write" in the blockchain then you'll need it (here escrow is my smart contract variable). For example I wrote this:
Future<void> _confirmOrder(String orderID) async {
    final transaction = Transaction(
      to: contractAddr,
      from: EthereumAddress.fromHex(account),
      value: EtherAmount.fromUnitAndValue(EtherUnit.finney, 0),
    );

    launch("https://metamask.app.link/");

    String returned = await escrow.confirmOrder(BigInt.parse(orderID),
        credentials: credentials, transaction: transaction);
  }

Let me know if you manage to do it :)

0xCaso avatar Feb 16 '22 10:02 0xCaso

Hi, thanks for extensive answer! :) I will try to implement it in my enviroment. I'll let you know my results

pozniejzmienie avatar Feb 16 '22 11:02 pozniejzmienie

Why do I need to create contract.abi.json?

Wont be enough to connect wallets Flutter app <=> Metamask accout and then proceed transactions?

I try do implement same logic like we have in OpenSea mobile app (when you press the button and integrate OpenSea account with Metamask)

pozniejzmienie avatar Feb 16 '22 12:02 pozniejzmienie

Sorry, I took for granted you wanted to interact with a smart contract. If not, you won't need that.

0xCaso avatar Feb 16 '22 12:02 0xCaso

Have to say, it is quite complicated, but you showed me appropriate way! I am grateful. :)

pozniejzmienie avatar Feb 16 '22 12:02 pozniejzmienie

It works without contract. By pressing the button it open Metamask app and ask for connection. Great! Thank you! Now I have to menage transactions and onther autentication stuff.

pozniejzmienie avatar Feb 16 '22 13:02 pozniejzmienie

Perfect! Good luck then :)

0xCaso avatar Feb 16 '22 17:02 0xCaso

Thanks for the explanation. I had the same problem.

EBP20 avatar Feb 18 '22 17:02 EBP20

hi, When the Metamsk opens, cann't the user automatically return to the app? how can I handle this?

EBP20 avatar Feb 20 '22 21:02 EBP20

Hi, Do you mean, can't go back when pressing 'connect' button on Metamusk? I haven't done this yet. But if you've found solution, lets us know :)

pozniejzmienie avatar Feb 21 '22 09:02 pozniejzmienie

yea didn't think about it honestly, I think it's something Metamask app has to manage, but not sure about it

0xCaso avatar Feb 21 '22 09:02 0xCaso

@matteocasonato Have you menaged sending transactions via Metamusk?

pozniejzmienie avatar Feb 21 '22 12:02 pozniejzmienie

yes, I showed it a bit in the example above.

0xCaso avatar Feb 21 '22 14:02 0xCaso

The problem I have now is that when it connects to Metamsk, it doesn't show a connection page to the user and doesn't ask if it wants to connect but this screen is displayed to connect to the Trustwallet.

EBP20 avatar Feb 21 '22 14:02 EBP20

The code is executed up to this line: if (!connector.connected) { session = await connector.createSession( chainId: 43113, onDisplayUri: (uri) async => {print(uri), await launch(uri)}); } , when it enters Metamask, the rest of the line isn't executed: account is still null

EBP20 avatar Feb 21 '22 16:02 EBP20

Your chain id should be validated with RPC server, check this case.

pon., 21 lut 2022, 17:28 użytkownik EBP20 @.***> napisał:

The code is executed up to this line: if (!connector.connected) { session = await connector.createSession( chainId: 43113, onDisplayUri: (uri) async => {print(uri), await launch(uri)}); } , when it enters Metamask, the rest of the line isn't executed: account is still null

— Reply to this email directly, view it on GitHub https://github.com/MetaMask/metamask-mobile/issues/3735#issuecomment-1047052052, or unsubscribe https://github.com/notifications/unsubscribe-auth/AXVFDX3OI5E3GILILTGAWI3U4JR37ANCNFSM5OG6KZ4A . You are receiving this because you commented.Message ID: @.***>

pozniejzmienie avatar Feb 21 '22 16:02 pozniejzmienie

Your chain id should be validated with RPC server, check this case. pon., 21 lut 2022, 17:28 użytkownik EBP20 @.> napisał: The code is executed up to this line: if (!connector.connected) { session = await connector.createSession( chainId: 43113, onDisplayUri: (uri) async => {print(uri), await launch(uri)}); } , when it enters Metamask, the rest of the line isn't executed: account is still null — Reply to this email directly, view it on GitHub <#3735 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AXVFDX3OI5E3GILILTGAWI3U4JR37ANCNFSM5OG6KZ4A . You are receiving this because you commented.Message ID: @.>

I used rinkeby, chain id for that is 4 Do you think this code has a problem?

_walletConnect() async { final connector = WalletConnect( bridge: 'https://bridge.walletconnect.org', clientMeta: const PeerMeta( name: 'WalletConnect', description: 'WalletConnect Developer App', url: 'https://walletconnect.org', icons: [ 'https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media' ], ), ); // Subscribe to events connector.on('connect', (session) => print(session)); connector.on('session_update', (payload) => print(payload)); connector.on('disconnect', (session) => print(session));

// Create a new session
if (!connector.connected) {
  session = await connector.createSession(
      chainId: 4,
      onDisplayUri: (uri) async => {print(uri), await launch(uri)});
}

setState(() {
  account = session.accounts[0];
  print(account);
});

if (account != null) {
  print('account != null');
  final client = Web3Client(infura, Client());
  EthereumWalletConnectProvider provider =
      EthereumWalletConnectProvider(connector);
  credentials = WalletConnectEthereumCredentials(provider: provider);
  print('test_credentials${credentials}');
  yourContract = ethUtils.getDeployedContract(contractAddress!, client);
}

}

EBP20 avatar Feb 21 '22 18:02 EBP20

Show me infura URL :P

I did the same. I also had to create project on infura dashboard. RPC Infura URL should also provide project id. Tomorow I will show you how infura URL looks like in my code.

My connection is ok, but I stucked on sending transactions :/

pozniejzmienie avatar Feb 21 '22 19:02 pozniejzmienie

String infura = "https://rinkeby.infura.io/v3/" + dotenv.env['INFURA_PROJECT_ID']!; Why it doesn't work for me :( If everything is correct, when I return to the app from Metamsk, the account shouldn't be null, right?

EBP20 avatar Feb 21 '22 19:02 EBP20

I have as fallow: final rpcUrl = 'https://rinkeby.infura.io/v3/$INFURA_PROJECT_ID';

just replace INFURA_PROJECT_ID with yours infura project id :)

Make sure your dontenv dependency incjections works well

pozniejzmienie avatar Feb 22 '22 07:02 pozniejzmienie

I found this problem. What is it about?

E/flutter (20605): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(ACTIVITY_NOT_FOUND, No Activity found to handle intent { wc:c33f9376-8b62-45c0-bf98-c1a6fca4645a@1?bridge=https%3A%2F%2Ff.bridge.walletconnect.org&key=491b6b728805351d3f0eb757e14666f6c29132c88c1e9450968a6207066b71c9 }, null, null)

EBP20 avatar Feb 22 '22 23:02 EBP20

No idea.. But if it throw exception, go that way. Have you debuged line by line part of code where the exception was thrown?

pozniejzmienie avatar Feb 23 '22 06:02 pozniejzmienie

@matteocasonato Hello, sorry if you think this is rude, is it possible to look at your source code, it's interesting to see how everything is implemented for you, I would be sincerely grateful.

EGMKVT avatar Feb 25 '22 07:02 EGMKVT

No idea.. But if it throw exception, go that way. Have you debuged line by line part of code where the exception was thrown?

hi. Could you please look at my code? I still couldn't find the problem and all the ways I found it are for web flutter :( I appreciate you very much

EBP20 avatar Feb 28 '22 16:02 EBP20

@matteocasonato Thank you for your provided answer. Thanks to that I am now able to connect my app to Metamask, fetch the wallet's address and sign transactions. So far, so good.

The problem comes when trying to implement an asymemetric/plubic key encryption system for an Authentication workflow. In order to do that, I need to sign a message (a nonce) that comes from the backend. The class WalletConnectEthereumCredentials has a method called signToSignature but is marked as a TODO to implement.

  Future<MsgSignature> signToSignature(Uint8List payload,
      {int? chainId, bool isEIP1559 = false}) {
    // TODO: implement signToSignature
    throw UnimplementedError();
  }

I'm pretty sure that's the method I need to use to sign the message. Have you by any chance implemented this function in your application? Any guess on how could I?

Thank you so much!

Pablo4Coding avatar Mar 11 '22 14:03 Pablo4Coding

Hey, @Pablo4Coding I am also able to connect wallet and fetch wallet addresses but not able to sign transactions. How you have managed to do that? It would be better if you can give some hints, processes, or code snippet for signing transactions.

Thanks in advance.

akshay-bhimani-pedalsup avatar Mar 14 '22 06:03 akshay-bhimani-pedalsup

Hi there! I want to connect metamask to my app to make sure users can buy items in a shop and make a transaction to my address. How can i achieve this?

Jens05 avatar Apr 03 '22 20:04 Jens05