Inherit BaseRelayRecipient contract in your contract.
Add BiconomyForwarder address in BaseRelayReceipent while deploying your contract.
Replace msg.sender in your contract with msgSender().
Now the contracts are updated and deployed, it's time to do the changes in your script.
After Registering Your DApp, SmartContact & Contract method on Desired network on Biconomy Dashboard, copy the <api-key>, you will need it on the client-side code.
Here comes the most important part, you need to make 2 providers on your client-side. One normal provider from your connected wallet e.g. window.ethereum
for Metamask, to take the User Signature on the wallet pop-up window.
Second provider object to be used with Biconomy to send the transaction. This second provider should be a normal provider initialised with network RPC URL.
That's it.
So the flow will be, to take the user signature you need to use the normal provider, once you get the user signature. pass that signature in the
This does not work with those EIP712 Signatures where chainId is used in DomainData. Metamask will throw an error if the ChainId specified does not match that of the current RPC it is connected to.
In basic terms, you need one provider object for using Biconomy (Mexa), another for signing meta transactions.
npm install @biconomy/mexa
// Install Biconomy<script src="https://cdn.jsdelivr.net/npm/@biconomy/mexa@latest/dist/mexa.js"></script>// Import Biconomylet Biconomy = window.Biconomy.default;
You can use Mexa either with Web3.js or Ethers.js. We'll be making two provider objects, one linked to your dApp's network RPC, and the other to your user's wallet.
import {Biconomy} from "@biconomy/mexa";import { Web3 } from "web3";// We're creating a 2nd Ethers provider linked to your L2 network of choicelet biconomy = new Biconomy(new Web3.providers.HttpProvider("YOUR RPC URL HERE"),{apiKey: <API Key>, debug: true});let networkWeb3 = new Web3(biconomy);/*This provider linked to your wallet.If needed, substitute your wallet solution in place of window.ethereum*/let walletWeb3 = new Web3(window.ethereum);
import {Biconomy} from "@biconomy/mexa";import { ethers } from "ethers";// We're creating a 2nd Ethers provider linked to your L2 network of choicelet biconomy = new Biconomy(new Web3.providers.HttpProvider("YOUR RPC URL HERE"),{apiKey: <API Key>, debug: true});let networkProvider = new ethers.providers.Web3Provider(biconomy);/*This provider linked to your wallet.If needed, substitute your wallet solution in place of window.ethereum*/let walletProvider = new ethers.providers.Web3Provider(window.ethereum);
Mexa fetches data from Biconomy's servers. Because of this, it's best to initialize your DApp or perform any action after the biconomy.READY event occurs.
If there is an error while initializing Mexa, it's good to catch and log a biconomy.ERROR event for better debugging.
In this scenario, you may need to initialise both instances of Mexa.
biconomy.onEvent(biconomy.READY, () => {// Initialize stuff that needs to interact with your node endpoint// ie. view calls to contracts}).onEvent(biconomy.ERROR, (error, message) => {// Handle error while initializing mexa});
Example code below is being updated. Please wait until we update the code below.
let walletSigner = walletProvider.getSigner();let networkSigner = networkProvider.getSigner();// Initialize Contractslet contract = new ethers.Contract(<CONTRACT_ADDRESS>,<CONTRACT_ABI>, networkSigner.connectUnchecked());let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);let address = <wallet public address>; //user addresslet functionSignature = contractInterface.encodeFunctionData("addRating", [1,5]);let gasPrice = await ethersProvider.getGasPrice();let gasLimit = await ethersProvider.estimateGas({to: <CONTRACT_ADDRESS>,from: address,data: functionSignature});console.log(gasLimit.toString())let rawTx = {to: <CONTRACT_ADDRESS>,data: functionSignature,from: address};const metaTxData = await biconomy.getForwardRequestAndMessageToSign(rawTx);console.log(dataToSign);const hashToSign = metaTxData.personalSignatureFormat;const signature = await walletSigner.signMessage(hashToSign);let data = {signature: signature,rawTransaction: signedTx,signatureType: biconomy.PERSONAL_SIGN,};// send signed transaction with ethers// promise resolves to transaction hashlet tx = networkProvider.send("eth_sendRawTransaction", [data]);console.log("Transaction hash : ", tx);//event emitter for ethersnetworkProvider.once(tx, (transaction) => {// Emitted when the transaction has been minedconsole.log(transaction);//doStuff});
// Import sigUtil for signing datavar sigUtil = require('eth-sig-util')let address = <wallet public address>;let privateKey = <private key>;let txParams = {"from": address,"gasLimit": web3.utils.toHex(210000),"to": contractAddress,"value": "0x0","data": contract.methods.addRating(1, 5).encodeABI()};const dataToSign = await biconomy.getForwardRequestMessageToSign(txParams);// dataToSign has personalFormat available as wellconst signature = sigUtil.signTypedMessage(new Buffer.from(privateKey, 'hex'),{data: dataToSign.eip712Format}, 'V4');let rawTransaction = signedTx.rawTransaction;let data = {signature: signature,rawTransaction: rawTransaction,signatureType: biconomy.EIP712_SIGN};// Use any one of the methods below to check for transaction confirmation// USING PROMISElet receipt = await networkWeb3.eth.sendSignedTransaction(data, (error, txHash)=>{if(error) {return console.error(error);}console.log(txHash);});/********* OR *********/// Get the transaction Hash using the Event Emitter returnednetworkWeb3.eth.sendSignedTransaction(data).on('transactionHash', (hash)=> {console.log(`Transaction hash is ${hash}`)}).once('confirmation', (confirmation, receipt)=> {console.log(`Transaction Confirmed.`);console.log(receipt);});