metamask-mobile
metamask-mobile copied to clipboard
Connect Metamask with a native mobile app built with Flutter
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).
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?
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
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?
- By deep linking create wallet connections beeten Fluter app and Metamask app
- Then connect to proper chain
- By using private keys I'll be able to do transactions directly from Flutter app as Metamask account
Is it correct way of thinking?
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
- Create a file with your contract's ABI,
contract.abi.json
. - From this you can generate
contract.g.dart
, with the commandflutter pub run build_runner build
. - 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. - 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.
- 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 (hereescrow
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 :)
Hi, thanks for extensive answer! :) I will try to implement it in my enviroment. I'll let you know my results
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)
Sorry, I took for granted you wanted to interact with a smart contract. If not, you won't need that.
Have to say, it is quite complicated, but you showed me appropriate way! I am grateful. :)
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.
Perfect! Good luck then :)
Thanks for the explanation. I had the same problem.
hi, When the Metamsk opens, cann't the user automatically return to the app? how can I handle this?
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 :)
yea didn't think about it honestly, I think it's something Metamask app has to manage, but not sure about it
@matteocasonato Have you menaged sending transactions via Metamusk?
yes, I showed it a bit in the example above.
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.
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
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: @.***>
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);
}
}
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 :/
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?
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
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)
No idea.. But if it throw exception, go that way. Have you debuged line by line part of code where the exception was thrown?
@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.
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
@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!
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.
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?