Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Credentials not getting - WalletConnectEthereumCredentials & Custom contract call #122

Closed
jay-benzatine opened this issue Jul 1, 2023 · 17 comments

Comments

@jay-benzatine
Copy link

Hello,

I have used this library https://pub.dev/packages/walletconnect_dart in my current project and it will not work anymore due to the wallet connect v2 version.

I have changed my code as given by the official doc. And I implemented this library https://pub.dev/packages/walletconnect_flutter_v2.

In my, there is functionality like a custom smart contract method call with dynamic parameters and open Metmask or any wallet for allowance or transaction. In library https://pub.dev/packages/walletconnect_flutter_v2 send transaction method is available but it will not support the custom contract call method how can I do that with this package?

So for that solution, I used this library https://pub.dev/packages/web3dart for custom contract calls.

But in this contract call method, he wants Credentials-WalletConnectEthereumCredentials to verify users.

In this https://pub.dev/packages/walletconnect_flutter_v2 library not getting WalletConnectEthereumCredentials like this lib https://pub.dev/packages/walletconnect_dart.

Here is my custom contract call function for Token Approval. But in that code, I have not gotten Credentials from walletconnect_flutter_v2 2.0.12 new library so how can I do that?


 Future<bool> getMCTApproval({required double price}) async {
  
    String? transactionId;
    try {

     /// THIS IS THE CUSTOM TRANSACTION USING"web3dart" 

      Transaction transaction = Transaction.callContract(
        from: EthereumAddress.fromHex(uData.walletAddress ?? ""),
        contract: contractService.mctContract,
        function:
            contractService.mctContract.function(ContractFunctionsName.approve),
        parameters: [
          EthereumAddress.fromHex(ContractAddressConstant.marketplaceAddress),
          BigInt.from((price) * pow(10, 18))
        ],
      );

      await _initGoToWallet();

     /// HERE IS THE PROBLEM I HAVE NOT GETTING **credential** FOR THE SEND TRANSACTION 
     
      transactionId = await client.sendTransaction(
        credential,
        chainId: chainId,
        transaction,
      );

      Debug.printLog("transactionId", transactionId.toString());
    } on Exception catch (_, e) {
      e.printError();
      Debug.printLog("Catch E", e.toString());
    }

    return (transactionId != null);
  }

Anyone, can you help me with this how can I do this? My project is stopped working. I want a solution urgently.

@Jens05
Copy link

Jens05 commented Jul 1, 2023

I have the exact same problem. Did you even manage to connect your wallet with the new package? Because my wallet is opening, but no request is shown to connect. Im really need a solution urgently, because by app relies on that code too...

@Luzzotica
Copy link
Contributor

Luzzotica commented Jul 1, 2023

In the example dapp, this is how I send a transaction:

static Future<dynamic> ethSendTransaction({
    required Web3App web3App,
    required String topic,
    required String chainId,
    required EthereumTransaction transaction,
  }) async {
    return await web3App.request(
      topic: topic,
      chainId: chainId,
      request: SessionRequestParams(
        method: methods[EIP155Methods.ethSendTransaction]!,
        params: [transaction.toJson()],
      ),
    );
  }

And the EthereumTransaction class:

@JsonSerializable(includeIfNull: false)
class EthereumTransaction {
  final String from;
  final String to;
  final String value;
  final String? nonce;
  final String? gasPrice;
  final String? maxFeePerGas;
  final String? maxPriorityFeePerGas;
  final String? gas;
  final String? gasLimit;
  final String? data;

  EthereumTransaction({
    required this.from,
    required this.to,
    required this.value,
    this.nonce,
    this.gasPrice,
    this.maxFeePerGas,
    this.maxPriorityFeePerGas,
    this.gas,
    this.gasLimit,
    this.data,
  });

  factory EthereumTransaction.fromJson(Map<String, dynamic> json) =>
      _$EthereumTransactionFromJson(json);

  Map<String, dynamic> toJson() => _$EthereumTransactionToJson(this);

  @override
  String toString() {
    return 'WCEthereumTransaction(from: $from, to: $to, nonce: $nonce, gasPrice: $gasPrice, maxFeePerGas: $maxFeePerGas, maxPriorityFeePerGas: $maxPriorityFeePerGas, gas: $gas, gasLimit: $gasLimit, value: $value, data: $data)';
  }
}

I built the EthereumTransaction myself. You might be able to use Web3Dart library and serialize it to JSON that way.

@bobwith2bees
Copy link

I have the exact same problem. Did you even manage to connect your wallet with the new package? Because my wallet is opening, but no request is shown to connect. Im really need a solution urgently, because by app relies on that code too...

@Jens05 - if the wallet app opens but there is no request - double check you have encoded the URI. With V2 there are [] in the URI that need to be properly encoded.

There is code in this ticket #113 (comment)

@Luzzotica
Copy link
Contributor

Perhaps you can use this repo:

https://pub.dev/packages/walletconnect_modal_flutter

And call the launchCurrentWallet function to open the currently connected wallet.
Either way, you can use that repo as an example of how to launch the currently connected wallet.

@Luzzotica
Copy link
Contributor

Luzzotica commented Jul 1, 2023

I understand opening the wallet is not the issue.

Your issue is that the URI is not encoded properly.
I dealt with this while building this package https://pub.dev/packages/walletconnect_modal_flutter

Here is the code I use to build the proper URI:

@override
  Uri? formatNativeUrl(String? appUrl, String wcUri) {
    if (appUrl == null || appUrl.isEmpty) return null;

    if (isHttpUrl(appUrl)) {
      return formatUniversalUrl(appUrl, wcUri);
    }

    String safeAppUrl = appUrl;
    if (!safeAppUrl.contains('://')) {
      safeAppUrl = appUrl.replaceAll('/', '').replaceAll(':', '');
      safeAppUrl = '$safeAppUrl://';
    }

    String encodedWcUrl = Uri.encodeComponent(wcUri);
    LoggerUtil.logger.i('Encoded WC URL: $encodedWcUrl');

    return Uri.parse('${safeAppUrl}wc?uri=$encodedWcUrl');
  }

  @override
  Uri? formatUniversalUrl(String? appUrl, String wcUri) {
    if (appUrl == null || appUrl.isEmpty) return null;

    if (!isHttpUrl(appUrl)) {
      return formatNativeUrl(appUrl, wcUri);
    }
    String plainAppUrl = appUrl;
    if (appUrl.endsWith('/')) {
      plainAppUrl = appUrl.substring(0, appUrl.length - 1);
    }

    String encodedWcUrl = Uri.encodeComponent(wcUri);
    LoggerUtil.logger.i('Encoded WC URL: $encodedWcUrl');

    return Uri.parse('$plainAppUrl/wc?uri=$encodedWcUrl');
  }

Again, you can find this code and use it in the above repo.

@Luzzotica
Copy link
Contributor

@Jens05

You have so many different issues going on dude.

Please open different issue requests for each one, stop hijacking other people's issues with your own stuff.

@klepov
Copy link

klepov commented Jul 2, 2023

same trouble

@Jens05
Copy link

Jens05 commented Jul 2, 2023

In the example dapp, this is how I send a transaction:

static Future<dynamic> ethSendTransaction({
    required Web3App web3App,
    required String topic,
    required String chainId,
    required EthereumTransaction transaction,
  }) async {
    return await web3App.request(
      topic: topic,
      chainId: chainId,
      request: SessionRequestParams(
        method: methods[EIP155Methods.ethSendTransaction]!,
        params: [transaction.toJson()],
      ),
    );
  }

And the EthereumTransaction class:

@JsonSerializable(includeIfNull: false)
class EthereumTransaction {
  final String from;
  final String to;
  final String value;
  final String? nonce;
  final String? gasPrice;
  final String? maxFeePerGas;
  final String? maxPriorityFeePerGas;
  final String? gas;
  final String? gasLimit;
  final String? data;

  EthereumTransaction({
    required this.from,
    required this.to,
    required this.value,
    this.nonce,
    this.gasPrice,
    this.maxFeePerGas,
    this.maxPriorityFeePerGas,
    this.gas,
    this.gasLimit,
    this.data,
  });

  factory EthereumTransaction.fromJson(Map<String, dynamic> json) =>
      _$EthereumTransactionFromJson(json);

  Map<String, dynamic> toJson() => _$EthereumTransactionToJson(this);

  @override
  String toString() {
    return 'WCEthereumTransaction(from: $from, to: $to, nonce: $nonce, gasPrice: $gasPrice, maxFeePerGas: $maxFeePerGas, maxPriorityFeePerGas: $maxPriorityFeePerGas, gas: $gas, gasLimit: $gasLimit, value: $value, data: $data)';
  }
}

I built the EthereumTransaction myself. You might be able to use Web3Dart library and serialize it to JSON that way.

How can i make an interaction with a contract than using this package?

@jay-benzatine
Copy link
Author

jay-benzatine commented Jul 2, 2023

In the example dapp, this is how I send a transaction:

static Future<dynamic> ethSendTransaction({
    required Web3App web3App,
    required String topic,
    required String chainId,
    required EthereumTransaction transaction,
  }) async {
    return await web3App.request(
      topic: topic,
      chainId: chainId,
      request: SessionRequestParams(
        method: methods[EIP155Methods.ethSendTransaction]!,
        params: [transaction.toJson()],
      ),
    );
  }

And the EthereumTransaction class:

@JsonSerializable(includeIfNull: false)
class EthereumTransaction {
  final String from;
  final String to;
  final String value;
  final String? nonce;
  final String? gasPrice;
  final String? maxFeePerGas;
  final String? maxPriorityFeePerGas;
  final String? gas;
  final String? gasLimit;
  final String? data;

  EthereumTransaction({
    required this.from,
    required this.to,
    required this.value,
    this.nonce,
    this.gasPrice,
    this.maxFeePerGas,
    this.maxPriorityFeePerGas,
    this.gas,
    this.gasLimit,
    this.data,
  });

  factory EthereumTransaction.fromJson(Map<String, dynamic> json) =>
      _$EthereumTransactionFromJson(json);

  Map<String, dynamic> toJson() => _$EthereumTransactionToJson(this);

  @override
  String toString() {
    return 'WCEthereumTransaction(from: $from, to: $to, nonce: $nonce, gasPrice: $gasPrice, maxFeePerGas: $maxFeePerGas, maxPriorityFeePerGas: $maxPriorityFeePerGas, gas: $gas, gasLimit: $gasLimit, value: $value, data: $data)';
  }
}

I built the EthereumTransaction myself. You might be able to use the Web3Dart library and serialize it to JSON that way.

This transaction is working but this transaction method doesn't work with contract call, I mean how to integrate custom contract call with this method?

@jay-benzatine
Copy link
Author

@jay-benzatine
Copy link
Author

jay-benzatine commented Jul 2, 2023

@Luzzotica I want WalletConnectEthereumCredentials Form connect session how can get that from the new library???

@jay-benzatine
Copy link
Author

@Luzzotica Hello,

Can you please let me know how can I do transactions using a custom contract using https://pub.dev/packages/walletconnect_flutter_v2 this lib?

If you have a demo then let me know or send an example code for that.

@jay-benzatine jay-benzatine changed the title Credentials not getting - WalletConnectEthereumCredentials Credentials not getting - WalletConnectEthereumCredentials & Custom contract call Jul 4, 2023
@jay-benzatine
Copy link
Author

jay-benzatine commented Jul 4, 2023

Hello,

I have a solution for that.

You can call the custom contract call using the default web3App.request method in walletconnect_flutter_v2.

For the call custom contract, you need to use the library.

  1. walletconnect_flutter_v2 => For connect wallet
  2. web3dart => To make a transaction and send this transaction into the web3App.request method with encoding.

hex.encode(List<int>.from(transaction.data!)),

Here is my full example for the token approval...


  Future<bool> getMCTApproval({required double price}) async {
    String? transactionId;
    try {
    
    /// MAKE TRANSACTION USING web3dart
    
      Transaction transaction = Transaction.callContract(
        from: EthereumAddress.fromHex(uData.walletAddress ?? ""),
        contract: contractService.mctContract,
        function:
            contractService.mctContract.function(ContractFunctionsName.approve),
        parameters: [
          EthereumAddress.fromHex(ContractAddressConstant.marketplaceAddress),
          BigInt.from((price) * pow(10, 18))
        ],
      );
      
     /// MAKE ETERUM TRANSACTION USING THE walletconnect_flutter_v2
      
      EthereumTransaction ethereumTransaction = EthereumTransaction(
        from: uData.walletAddress ?? "",
        to: ContractAddressConstant.mctAddress,
        value: "0x0",
        data: hex.encode(List<int>.from(transaction.data!)), /// ENCODE TRANSACTION USING convert LIB
      );

      await _initGoToWallet();

     ///  REQUEST TO WALLET FOR TRANSACTION USING vwalletconnect_flutter_v2
     
      transactionId = await MyApp.walletConnectHelper.web3App?.request(
        topic: MyApp.walletConnectHelper.sessionData?.topic ?? "",
        chainId: MyApp.walletConnectHelper.chain.chainId,
        request: SessionRequestParams(
          method: EIP155.methods[EIP155Methods.ethSendTransaction] ?? "",
          params: [ethereumTransaction.toJson()],
        ),
      );

      Debug.printLog("TRANSACTION ID", transactionId.toString());
    } on Exception catch (_, e) {
      e.printError();
      Debug.printLog("Catch E", e.toString());
    }
    
    return transactionId != null;
  }

@ContrastPro
Copy link

ContrastPro commented Jul 4, 2023

Hello everyone! I will briefly describe how to send a transaction through Metamask.

  1. Initialize WalletConnect
  static Web3App? _walletConnect;

  Future<void> _initWalletConnect() async {
    _walletConnect = await Web3App.createInstance(
      projectId: 'c4f79cc821944d9680842e34466bfb',
      metadata: const PairingMetadata(
        name: 'Flutter WalletConnect',
        description: 'Flutter WalletConnect Dapp Example',
        url: 'https://walletconnect.com/',
        icons: [
          'https://walletconnect.com/walletconnect-logo.png',
        ],
      ),
    );
  }
  1. Create session with Metamask
  static const String launchError = 'Metamask wallet not installed';
  static const String kShortChainId = 'eip155';
  static const String kFullChainId = 'eip155:80001';

  static String? _url;
  static SessionData? _sessionData;

  String get deepLinkUrl => 'metamask://wc?uri=$_url';

  Future<String?> createSession() async {
    final bool isInstalled = await metamaskIsInstalled();

    if (!isInstalled) {
      return Future.error(launchError);
    }

    if (_walletConnect == null) {
      await _initWalletConnect();
    }

    final ConnectResponse connectResponse = await _walletConnect!.connect(
      requiredNamespaces: {
        kShortChainId: const RequiredNamespace(
          chains: [kFullChainId],
          methods: [
            'eth_sign',
            'eth_signTransaction',
            'eth_sendTransaction',
          ],
          events: [
            'chainChanged',
            'accountsChanged',
          ],
        ),
      },
    );

    final Uri? uri = connectResponse.uri;

    if (uri != null) {
      final String encodedUrl = Uri.encodeComponent('$uri');

      _url = encodedUrl;

      await launchUrlString(
        deepLinkUrl,
        mode: LaunchMode.externalApplication,
      );

      _sessionData = await connectResponse.session.future;

      final String account = NamespaceUtils.getAccount(
        _sessionData!.namespaces.values.first.accounts.first,
      );

      return account;
    }

    return null;
  }
  1. Create custom EthereumTransaction model
class EthereumTransaction {
  const EthereumTransaction({
    required this.from,
    required this.to,
    required this.value,
    this.data,
  });

  final String from;
  final String to;
  final String value;
  final String? data;

  Map<String, dynamic> toJson() => {
        'from': from,
        'to': to,
        'value': value,
        'data': data,
      };
}
  1. Create instance of EthereumTransaction
  Future<EthereumTransaction> generateTransaction({
    required String userId,
    required String reseiverId,
  }) async {
    final EthereumTransaction transaction = EthereumTransaction(
      from: userId,
      to: reseiverId,
      value: '1',
    );

    return transaction;
  }
  1. Sign and send transaction with Metamask
  Future<Stream<dynamic>?> sendTransaction({
    required EthereumTransaction transaction,
  }) async {
    await launchUrlString(
      deepLinkUrl,
      mode: LaunchMode.externalApplication,
    );

    final Future<dynamic> signResponse = _walletConnect!.request(
      topic: _sessionData!.topic,
      chainId: kFullChainId,
      request: SessionRequestParams(
        method: 'eth_sendTransaction',
        params: [transaction.toJson()],
      ),
    );

    return signResponse.asStream();
  }
  1. You can check info from request() like this
      final Stream<dynamic>? stream = await sendTransaction(
        transaction: transaction,
      );

      if (stream != null) {
        stream.listen((event) {
          log('$event', name: 'Stream event');
        });
      }

@jay-benzatine
Copy link
Author

@jay-benzatine From which library is this method?

hex.encode(List<int>.from(transaction.data!))

@ContrastPro

I used this lib for encoding transactions.

convert: ^3.1.1 (https://pub.dev/packages/convert)

@4xMafole
Copy link

4xMafole commented Jul 12, 2023

I appreciate all the efforts used to answer this issue. But I can see most of you having different solutions which isn't bad. But I guess the solution I will provide is a close answer to what the issue was created for. I think this will be helpful for others coming on this issue to get a simple answer.

WalletConnectEthereumCredentials (Just copy this class):

// ignore: implementation_imports
import 'package:web3dart/crypto.dart';
import 'package:web3dart/src/crypto/secp256k1.dart';
import 'dart:typed_data';

import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
import 'package:web3dart/web3dart.dart';

class WalletConnectEthereumCredentials extends CustomTransactionSender {
  WalletConnectEthereumCredentials(
      {required this.wcClient, required this.session});

  final Web3App wcClient;
  final SessionData session;

  @override
  Future<String> sendTransaction(Transaction transaction) async {
    final from = await extractAddress();
    final signResponse = await wcClient.request(
      topic: session.topic,
      chainId: 'eip155:80001',
      request: SessionRequestParams(
        method: 'eth_sendTransaction',
        params: [
          {
            'from': from.hex,
            'to': transaction.to?.hex,
            'gas': '0x${transaction.maxGas!.toRadixString(16)}',
            'gasPrice':
                '0x${transaction.gasPrice?.getInWei.toRadixString(16) ?? '0'}',
            'value':
                '0x${transaction.value?.getInWei.toRadixString(16) ?? '0'}',
            'data':
                transaction.data != null ? bytesToHex(transaction.data!) : null,
            'nonce': transaction.nonce,
          }
        ],
      ),
    );

    return signResponse.toString();
  }

  @override
  EthereumAddress get address => EthereumAddress.fromHex(
      session.namespaces.values.first.accounts.first.split(':').last);

  @override
  Future<EthereumAddress> extractAddress() =>
      Future(() => EthereumAddress.fromHex(
          session.namespaces.values.first.accounts.first.split(':').last));

  @override
  MsgSignature signToEcSignature(Uint8List payload,
      {int? chainId, bool isEIP1559 = false}) {
    // TODO: implement signToEcSignature
    throw UnimplementedError();
  }

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

Example (Usage):

 ///Deploys a smart contract using mobile wallet
  Future<String> deployContract(
      {required Web3App walletConnect,
      required SessionData session,
      required Uri walletURI,
      ...
      }) async {
    //Getting credentials from the provider
    var credentials = WalletConnectEthereumCredentials(
      wcClient: walletConnect,
      session: session,
    );

    //Constructing web3 client
    var web3 = EthInitial();

    //Initializing web3 client
    await web3.initWeb3(ethereumCredentials: credentials, chainNetwork: chain);
    ....

    //Creating a transaction
    final Transaction transaction = Transaction(
       ...
      from: web3.credentials.address,
      ....
    );

    //Launching the mobile wallet (Metamask)
    launchUrl(walletURI, mode: LaunchMode.externalApplication);

    //Getting transaction hash
    final String transactionHash =
        await credentials.sendTransaction(transaction);


    //Getting a receipt through transaction hash
    final receipt = await web3.client.getTransactionReceipt(transactionHash);

  }

Example for Custom contract call:


 ///Mints NFT to the specified collection address
  Future<String> mintNFT({
    required String dataUri,
    required int royaltyPercentage,
    required Web3App walletConnector,
    required SessionData session,
    required Uri walletUri,
    required Chain chain,
    String? collectionAddress,
  }) async {
    //Getting credentials from the provider
    var credentials = WalletConnectEthereumCredentials(
      wcClient: walletConnector,
      session: session,
    );

    //Constructing a web3 client
    var web3 = EthInitial();

    //Initializing web3 client
    await web3.initWeb3(ethereumCredentials: credentials, chainNetwork: chain);

    //Getting the deployed contract using ABI and contract address
    final contract = DeployedContract(
      ContractAbi.fromJson(NFTContract.contractABI, ''),
      EthereumAddress.fromHex(collectionAddress!),
    );

    //Create a minting function
    final mintFunction = contract.function('mint');

    //Create a transaction from contract, mint function and data uri.
    var transaction = Transaction.callContract(
      contract: contract,
      function: mintFunction,
     .....
      parameters: [dataUri, BigInt.from(royaltyPercentage)],
    );
    
     ....

  }
  

Hope, this solves the issue. Thanks.

@oyen-bright
Copy link

Hello,

I have a solution for that.

You can call the custom contract call using the default web3App.request method in walletconnect_flutter_v2.

For the call custom contract, you need to use the library.

  1. walletconnect_flutter_v2 => For connect wallet
  2. web3dart => To make a transaction and send this transaction into the web3App.request method with encoding.

hex.encode(List<int>.from(transaction.data!)),

Here is my full example for the token approval...


  Future<bool> getMCTApproval({required double price}) async {
    String? transactionId;
    try {
    
    /// MAKE TRANSACTION USING web3dart
    
      Transaction transaction = Transaction.callContract(
        from: EthereumAddress.fromHex(uData.walletAddress ?? ""),
        contract: contractService.mctContract,
        function:
            contractService.mctContract.function(ContractFunctionsName.approve),
        parameters: [
          EthereumAddress.fromHex(ContractAddressConstant.marketplaceAddress),
          BigInt.from((price) * pow(10, 18))
        ],
      );
      
     /// MAKE ETERUM TRANSACTION USING THE walletconnect_flutter_v2
      
      EthereumTransaction ethereumTransaction = EthereumTransaction(
        from: uData.walletAddress ?? "",
        to: ContractAddressConstant.mctAddress,
        value: "0x0",
        data: hex.encode(List<int>.from(transaction.data!)), /// ENCODE TRANSACTION USING convert LIB
      );

      await _initGoToWallet();

     ///  REQUEST TO WALLET FOR TRANSACTION USING vwalletconnect_flutter_v2
     
      transactionId = await MyApp.walletConnectHelper.web3App?.request(
        topic: MyApp.walletConnectHelper.sessionData?.topic ?? "",
        chainId: MyApp.walletConnectHelper.chain.chainId,
        request: SessionRequestParams(
          method: EIP155.methods[EIP155Methods.ethSendTransaction] ?? "",
          params: [ethereumTransaction.toJson()],
        ),
      );

      Debug.printLog("TRANSACTION ID", transactionId.toString());
    } on Exception catch (_, e) {
      e.printError();
      Debug.printLog("Catch E", e.toString());
    }
    
    return transactionId != null;
  }

@jay-benzatine

  await _initGoToWallet();

How do i go to the wallet ? is it with the createInstance url ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants