GeniusGeek
GeniusGeek

Reputation: 335

Sending transaction to Smart Contract (myContract.methods.myMethod.send() ) returns error: Unknown Account on mobile Wallets

The Dapp sends Bep20 token from a remote wallet(My wallet) to users after they perform a certain activity such as accepting BNB to be sent to ICO, it works well on a desktop in which metamask is installed. The code that executes this is:


    var amountTosend = amount;
     var privateKey = 'PRIVATE_KEY';
  
    var accountFrom = web3provider.eth.accounts.privateKeyToAccount(privateKey).address;
     var contractCall = contractInstance.methods.transfer(addressTo, "0x"+amountTosend.toString(16));
     var icoAccount = web3provider.eth.accounts.privateKeyToAccount(privateKey);
         web3provider.eth.accounts.wallet.add(icoAccount);
contractCall.send({ from: accountFrom, gas: 2000000 }).then(function(hashdata) {
                             console.log(hashdata);

                            var rawTransaction = {
                                    
                                     "from": accountFrom,
                                     "nonce": nonce,
                                     "gasPrice": 20000000000,
                                   
                                     "gas": gas,
                                     "to": SmartContractAddress,
                                  
                                     "value": "0x" + amountTosend.toString(16),
                                    
                                     "data": hashdata.events.Transfer.raw.data,
                                      
                                     "chainId": chainid
                                 };

                                
                                 var privKey = new ethereumjs.Buffer.Buffer(privateKey, 'hex');
                              

                                 let tx = new ethereumjs.Tx(rawTransaction);
                                 tx.sign(privKey)
                                 let serializedTx = tx.serialize();

                                 console.log('serializedTx:', serializedTx);

                                 web3provider.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'), function(err, receipt) {
                                     if (!err) {
                                         console.log('success token sent to user: ' + receipt);
                                         alert('You have received your Tokens in your Wallet');
                                     } else {
                                         console.log('error: ' + err);
                                         alert("An error Occured: " + err);
                                     }
                                 });

})

On mobile wallets such as trust and metamask mobile connected with web3modal using wallet connect as provider, I successfully interacted with the wallet, prompt user to send BNB but

contractCall.send({ from: accountFrom, gas: 2000000 })

which is used to change the state of the smart contract to send user Bep20 token fails with Unknown account error after adding remote wallet to local using:

web3provider.eth.accounts.wallet.add(icoAccount);

Note: If I try skipping

contractCall.send({ from: accountFrom, gas: 2000000 })

and use contractCall.encodeABI() for data parameter, it works fine but no token is actually sent to the user and I guess is because the smart contact state needs to be changed/altered with

myContract.methods.myMethod([param1[, param2[, ...]]]).send

Please I need any useful help/tip to make this work on mobile wallet, I have been on this bug for a few days now, thanks

Upvotes: 0

Views: 3238

Answers (1)

GeniusGeek
GeniusGeek

Reputation: 335

I finally fixed this error, and I will like to answer comprehensively for future visitors. The short answer will come first to implement the solution, and the long comprehensive answer afterward to help you understand better and learn the technicalities of fixing the bug

Note: I am using walletconnect and web3modal under the hood for my DAPP on Mobile browser and Metamask for Desktop and I'm using vanilla javascript for web3 implemenation

Short Answer: I had to set givenProvider to the currentProvider which was initialized on web3 instance and that was my RPC URL provider, for me my RPC URL provider is: https://getblock.io/ which was passed in walletconnect/web3modal provider options paramater

I'm connecting to BSC so my RPC URL is: https://bsc.getblock.io/mainnet/?api_key=API_KEY

You may use other RPC URL providers of your choice

So the givenProvider is set like this:

web3.givenProvider = web3.currentProvider;
web3.eth.givenProvider = web3.currentProvider;
web3.eth.accounts.givenProvider = web3.currentProvider;

read walletconnect docs on setting provider(s) here: https://docs.walletconnect.com/quick-start/dapps/web3-provider

Setting the web3.givenProvider to web3.currentProvider fixed the issue as web3.givenProvider was null

Long Answer:

You need to understand what is currentProvider and givenProvider are, as the bug resolves around the web3 provider. In my understanding as of writting this answer givenProvider is the provider injected in an Ethereum enabled browser while currentProvider is the provider that you passed in on the initialization of web3.

The error is: Unknown account

This problem doesn't appear on a desktop browser where metamask is installed because injects the givenProvider, except of course is a remote account that doesn't exist on the provider node

You have to add the account to the provider node using:

  var account=  web3provider.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
var addAccount = web3provider.eth.accounts.wallet.add(account);

This solves the error on desktop browser without having to configure or set up much and that is because masks makes your browser Ethereum enabled and injects givenProvider

But for mobile browsers, you will face this issue because mobile browsers aren't Ethereum enabled environment. You will have to set givenProvider to a valid provider after initializing web3 through walletconenct.

Note: givenProvider talks to the blockchain through the web and it is null on mobile browsers. Console.log(web3) will show you the values of web3 instance

Putting all together your code should look like this:


import WalletConnectProvider from "@walletconnect/web3-provider";
import Web3 from "web3";

//  Create WalletConnect Provider
const provider = new WalletConnectProvider({
  rpc: {
    1: "https://mainnet.mycustomnode.com",
    3: "https://ropsten.mycustomnode.com",
    100: "https://dai.poa.network",
    // ...
  },
});


await provider.enable();



//  Create Web3 instance
const web3 = new Web3(provider);

//FIX STARTS HERE

//set givenProvider to currentProvider
 web3.givenProvider = web3.currentProvider;
 web3.eth.givenProvider = web3.currentProvider;
 web3.eth.accounts.givenProvider = web3.currentProvider;

//add wallet to provider node
var account= web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
var addAccount = web3.eth.accounts.wallet.add(account);
var accountFrom = account.address;

var gas = contractCall.estimateGas({ from: accountFrom });
 gas.then(function(gasTouse) {
contractCall.send({ 
from: accountFrom, gas: gasTouse }).then(function(hashdata){
   console.log(hashdata);

});
});

Note: Be sure you estimate gas and include it, as without it you may encounter some gas-related errors.

Happy coding :)

Upvotes: 1

Related Questions