Inherit BasicMetaTransaction contract in your contract
Replace msg.sender in your contract with msgSender()
Inherit EIP712MetaTransaction contract in your contract
Replace msg.sender in your contract with msgSender()
Hardcode chainId as "1" in EIP712Base.sol
Deploy contract on the desired network.
Now the contracts are updated and deployed, it's time to do the changes on your client-side.
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.
It is important that you need to make 2 providers on your client-side.
One normal provider from your connected wallet eg 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 (network provider) should be a provider initialised with network RPC URL.
That's it.
In this custom approach instead of calling you contract methods directly, you will need to call executeMetaTransaction() method and pass your contract method in functionSignature
along with other params.
So the flow will be like this, to take the user signature you need to use the wallet provider. Once you get the user signature, pass that signature in the executeMetaTransaction()
along with functionSignature
and other params and call executeMetaTransaction() method using the second provider which was passed to Biconomy object.
In basic terms, you need one provider object for sending meta transactions using Biconomy (Mexa), another for signing meta transaction parameters.
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";// 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 biconomyWeb3 = 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";// We're creating a 2nd Ethers provider linked to your L2 network of choicelet biconomy = new Biconomy(new ethers.providers.JsonRpcProvider("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.
biconomy.onEvent(biconomy.READY, () => {// Initialize your dapp here like getting user accounts etc}).onEvent(biconomy.ERROR, (error, message) => {// Handle error while initializing mexa});
Example code below is being updated. Please wait until we update the code below.
// Extra Dependenciesimport abi from "ethereumjs-abi";import {toBuffer} from "ethereumjs-util";// WalletSigner will be used to take the user Signaturelet walletSigner = walletProvider.getSigner();let contract = new ethers.Contract(<CONTRACT_ADDRESS>,<CONTRACT_ABI>, networkProvider);let nonce = await contract.getNonce(userAddress);let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);// Create your target method signature.. here we are calling setQuote() method of our contractlet functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);let messageToSign = abi.soliditySHA3(["uint256","address","uint256","bytes"],[parseInt(nonce), <CONTRACT_ADDRESS>, chainId, toBuffer(functionSignature)]);const signature = await walletSigner.signMessage(messageToSign);let { r, s, v } = getSignatureParameters(signature);const txData = await contract.populateTransaction.executeMetaTransaction(userAddress, functionData, r, s, v);txData.from = userAddress;const txHash = await networkProvider.send("eth_sendTransaction",[txData]);let txReceipt = await networkProvider.waitForTransaction(txHash);console.log(txReceipt);
let contract = new networkWeb3.eth.Contract(jsonInterface[, address][, options])let nonce = await contract.methods.getNonce(userAddress).call();// Get target method's function signaturelet functionSignature = contract.methods.mint(userAddress, "100").encodeABI();let messageToSign = abi.soliditySHA3(["uint256","address","uint256","bytes"],[parseInt(nonce), contract.options.address, chainId, toBuffer(functionSignature)]);const signature = await walletWeb3.eth.personal.sign("0x" +messageToSign.toString("hex"), userAddress);let { r, s, v } = getSignatureParameters(signature);let tx = contract.methods.executeMetaTransaction(userAddress,functionSignature, r, s, v).send(from: userAddress);tx.on("transactionHash", function(hash) {// Handle transaction hash}).once("confirmation", function(confirmationNumber, receipt) {// Handle confirmation}).on("error", function(error) {// Handle error});});
When using Basic or Personal Signature When using EIP-712 Signature