SDK
Biconomy SDK (Mexa), enables meta transactions or gasless transactions in your DApp out of the box.
Biconomy's SDK (Mexa) is a javascript based implementation that can be easily integrated into your Dapp. The SDK can be used on client side code running in a browser or a javascript based Dapp running on a backend server.
It works alongside your existing Web3 library by acting as a Web3 provider that submits meta transactions to our relayer infrastructure - instead of directly to the network.

Get your API Key First

In order to integrate SDK, you will need an API Key. Check out how to get API key from the dashboard.

SDK Frontend Integration

1. Installing and importing SDK

Mexa can be installed either via npm repository or using standalone javascript file using html <script/> tag
Via NPM
Standalone JS File
1
// Install Biconomy
2
npm install @biconomy/mexa
3
​
4
// Import Biconomy
5
import {Biconomy} from "@biconomy/mexa";
Copied!
1
// Install Biconomy
2
<script src="https://cdn.jsdelivr.net/npm/@biconomy/[email protected]/dist/mexa.js"></script>
3
​
4
// Import Biconomy
5
let Biconomy = window.Biconomy;
Copied!

2. Initializing SDK

You can use Mexa either with Web3.js or Ethers.js library. It works with both libraries.
web3
Ethers
1
import {Biconomy} from "@biconomy/mexa";
2
​
3
const biconomy = new Biconomy(<web3 provider>,{apiKey: <API Key>, debug: true});
4
web3 = new Web3(biconomy);
Copied!
1
import {Biconomy} from "@biconomy/mexa";
2
​
3
const biconomy = new Biconomy(<ethers provider>,{apiKey: <API Key>, debug: true});
4
const ethersProvider = new ethers.providers.Web3Provider(biconomy);
Copied!

3. Initialize your DApp after Mexa initialization

Since mexa fetches data from Biconomy server in order to serve better. It's better to initialize your DApp or perform any action after the biconomy.READY event.
Or if there is some error while initializing Mexa, it's better to catch and log biconomy.ERROR event for better debugging.
1
biconomy.onEvent(biconomy.READY, () => {
2
// Initialize your dapp here like getting user accounts etc
3
}).onEvent(biconomy.ERROR, (error, message) => {
4
// Handle error while initializing mexa
5
});
Copied!

4. Call ExecuteMetaTransaction Method

Since we have enabled native meta transaction in a custom way, instead of calling your smart contract methods directly, we will call executeMetaTransaction method and pass the target method info as a parameter to this function.
Web3 + EIP712 Sign
Web3 + Personal Sign
Ethers + EIP712 Sign
Ethers + Personal Sign
​Check the repository here for complete example code with this approach.
1
// This web3 instance is used to get user signature from connected wallet
2
let walletWeb3 = new Web3(window.ethereum);
3
​
4
// Initialize constants
5
const domainType = [
6
{ name: "name", type: "string" },
7
{ name: "version", type: "string" },
8
{ name: "verifyingContract", type: "address" },
9
{ name: "salt", type: "bytes32" },
10
];
11
const metaTransactionType = [
12
{ name: "nonce", type: "uint256" },
13
{ name: "from", type: "address" },
14
{ name: "functionSignature", type: "bytes" }
15
];
16
// replace the chainId 42 if network is not kovan
17
let domainData = {
18
name: "TestContract",
19
version: "1",
20
verifyingContract: config.contract.address,
21
// converts Number to bytes32. pass your chainId instead of 42 if network is not Kovan
22
salt: '0x' + (42).toString(16).padStart(64, '0')
23
};
24
​
25
let contract = new web3.eth.Contract(
26
<Your Contract ABI>,
27
<Your Contract Address>
28
);
29
30
let nonce = await contract.methods.getNonce(userAddress).call();
31
// Create your target method signature.. here we are calling setQuote() method of our contract
32
let functionSignature = contract.methods.setQuote(newQuote).encodeABI();
33
let message = {};
34
message.nonce = parseInt(nonce);
35
message.from = userAddress;
36
message.functionSignature = functionSignature;
37
​
38
const dataToSign = JSON.stringify({
39
types: {
40
EIP712Domain: domainType,
41
MetaTransaction: metaTransactionType
42
},
43
domain: domainData,
44
primaryType: "MetaTransaction",
45
message: message
46
});
47
​
48
// NOTE: Using walletWeb3 here, as it is connected to the wallet where user account is present.
49
// Get the EIP-712 Signature and send the transaction
50
walletWeb3.currentProvider.send({
51
jsonrpc: "2.0",
52
id: 999999999999,
53
method: "eth_signTypedData_v4",
54
params: [userAddress, dataToSign]
55
},function(error, response) {
56
// Check github repository for getSignatureParameters helper method
57
let { r, s, v } = getSignatureParameters(response.result);
58
let tx = contract.methods.executeMetaTransaction(userAddress,
59
functionSignature, r, s, v)
60
.send(from: userAddress);
61
​
62
tx.on("transactionHash", function(hash) {
63
// Handle transaction hash
64
}).once("confirmation", function(confirmationNumber, receipt) {
65
// Handle confirmation
66
}).on("error", function(error) {
67
// Handle error
68
});
69
70
}
71
);
Copied!
​Check the repository here for complete example code with this approach.
1
let abi = require('ethereumjs-abi'); //dependencies
2
import {toBuffer} from "ethereumjs-util";
3
​
4
// This web3 instance is used to get user signature from connected wallet
5
let walletWeb3 = new Web3(window.ethereum);
6
​
7
// Initialize constants
8
let contract = new web3.eth.Contract(
9
<Your Contract ABI>,
10
<Your Contract Address>
11
);
12
​
13
let nonce = await contract.methods.getNonce(userAddress).call();
14
let functionSignature = contract.methods.setQuote(newQuote).encodeABI();
15
​
16
let messageToSign = constructMetaTransactionMessage(nonce,
17
<CHAIN_ID>, functionSignature,
18
<YOUR_CONTRACT_ADDRESS>);
19
​
20
// NOTE: We are using walletWeb3 here to get signature from connected wallet
21
const signature = await walletWeb3.eth.personal.sign("0x" +
22
messageToSign.toString("hex"), userAddress);
23
​
24
// Check the repository link mentioned above for helper methods
25
let { r, s, v } = getSignatureParameters(signature);
26
let tx = contract.methods.executeMetaTransaction(
27
userAddress, functionSignature, r, s, v)
28
.send({from: userAddress});
29
​
30
tx.on("transactionHash", (hash)=>{
31
// Handle transaction hash
32
}).once("confirmation", (confirmation, recipet) => {
33
// Handle confirmation
34
}).on("error", error => {
35
// Handle error
36
});
37
​
38
// Helper methods
39
const constructMetaTransactionMessage = (nonce, chainId, functionSignature, contractAddress) => {
40
return abi.soliditySHA3(
41
["uint256","address","uint256","bytes"],
42
[nonce, contractAddress, chainId, toBuffer(functionSignature)]
43
);
44
}
Copied!
​Check the repository here for complete example code with this approach.
1
let walletProvider, walletSigner;
2
​
3
// Initialize Constants
4
const domainType = [
5
{ name: "name", type: "string" },
6
{ name: "version", type: "string" },
7
{ name: "verifyingContract", type: "address" },
8
{ name: "salt", type: "bytes32" },
9
];
10
const metaTransactionType = [
11
{ name: "nonce", type: "uint256" },
12
{ name: "from", type: "address" },
13
{ name: "functionSignature", type: "bytes" }
14
];
15
// replace the chainId 42 if network is not kovan
16
let domainData = {
17
name: "TestContract",
18
version: "1",
19
verifyingContract: config.contract.address,
20
salt: ethers.utils.hexZeroPad((ethers.BigNumber.from(42)).toHexString(), 32)
21
};
22
​
23
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
24
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
25
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
26
​
27
/*
28
This provider is linked to your wallet.
29
If needed, substitute your wallet solution in place of window.ethereum
30
*/
31
walletProvider = new ethers.providers.Web3Provider(window.ethereum);
32
walletSigner = walletProvider.getSigner();
33
​
34
let nonce = await contract.getNonce(userAddress);
35
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
36
37
let message = {};
38
message.nonce = parseInt(nonce);
39
message.from = userAddress;
40
message.functionSignature = functionSignature;
41
​
42
const dataToSign = JSON.stringify({
43
types: {
44
EIP712Domain: domainType,
45
MetaTransaction: metaTransactionType
46
},
47
domain: domainData,
48
primaryType: "MetaTransaction",
49
message: message
50
});
51
​
52
/*Its important to use eth_signTypedData_v3 and not v4 to get EIP712 signature
53
because we have used salt in domain data instead of chainId*/
54
// Get the EIP-712 Signature and send the transaction
55
let signature = await walletProvider.send("eth_signTypedData_v3", [userAddress, dataToSign])
56
let { r, s, v } = getSignatureParameters(signature);
57
let tx = contract.executeMetaTransaction(userAddress,
58
functionSignature, r, s, v);
59
60
await tx.wait(1);
61
console.log("Transaction hash : ", tx.hash);
62
63
64
//////////
65
/*helper*/
66
​
67
const getSignatureParameters = signature => {
68
if (!ethers.utils.isHexString(signature)) {
69
throw new Error(
70
'Given value "'.concat(signature, '" is not a valid hex string.')
71
);
72
}
73
var r = signature.slice(0, 66);
74
var s = "0x".concat(signature.slice(66, 130));
75
var v = "0x".concat(signature.slice(130, 132));
76
v = ethers.BigNumber.from(v).toNumber();
77
if (![27, 28].includes(v)) v += 27;
78
return {
79
r: r,
80
s: s,
81
v: v
82
};
83
};
Copied!
​Check the repository here for complete example code with this approach.
1
// Extra Dependencies
2
import abi from "ethereumjs-abi";
3
import {toBuffer} from "ethereumjs-util";
4
​
5
let walletProvider, walletSigner;
6
​
7
// Initialize Constants
8
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
9
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
10
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
11
​
12
/*
13
This provider is linked to your wallet.
14
If needed, substitute your wallet solution in place of window.ethereum
15
*/
16
walletProvider = new ethers.providers.Web3Provider(window.ethereum);
17
walletSigner = walletProvider.getSigner();
18
​
19
let nonce = await contract.getNonce(userAddress); //BigNumber
20
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
21
22
let messageToSign = constructMetaTransactionMessage(nonce.toNumber(), <CHAIN_ID> functionSignature, <YOUR_CONTRACT_ADDRESS>);
23
const signature = await walletSigner.signMessage(messageToSign);
24
let { r, s, v } = getSignatureParameters(signature);
25
let tx = contract.executeMetaTransaction(userAddress,
26
functionSignature, r, s, v);
27
​
28
await tx.wait(1);
29
console.log("Transaction hash : ", tx.hash);
30
31
32
//////////
33
/**helpers**/
34
​
35
const getSignatureParameters = signature => {
36
if (!ethers.utils.isHexString(signature)) {
37
throw new Error(
38
'Given value "'.concat(signature, '" is not a valid hex string.')
39
);
40
}
41
var r = signature.slice(0, 66);
42
var s = "0x".concat(signature.slice(66, 130));
43
var v = "0x".concat(signature.slice(130, 132));
44
v = ethers.BigNumber.from(v).toNumber();
45
if (![27, 28].includes(v)) v += 27;
46
return {
47
r: r,
48
s: s,
49
v: v
50
};
51
};
52
53
const constructMetaTransactionMessage = (nonce, salt, functionSignature, contractAddress) => {
54
return abi.soliditySHA3(
55
["uint256","address","uint256","bytes"],
56
[nonce, contractAddress, salt, toBuffer(functionSignature)]
57
);
58
}
59
​
Copied!
Congratulations πŸ‘
You have now enabled meta transactions in your DApp via Biconomy.

SDK Backend Integration

Here, we provide more freedom to the developer in case you want to use Biconomy in your project running at the backend where a raw transaction is signed by the user's private key.

Integration Steps

    1.
    Initialize the Mexa SDK in the same way as mentioned above.
    2.
    Call executeMetaTransaction method via web3 or ethers using a private key.
Checkout example code below. Need more reference? check repositories for complete code.
Web3 + EIP712 Sign
Web3 + Personal Sign
Ethers + EIP712 Sign
Ethers + Personal Sign
​Check the repository here for complete example code with this approach
1
let sigUtil = require("eth-sig-util"); // additional dependency
2
​
3
let privateKey = <PRIVATe_KEY>;
4
let nonce = await contract.methods.getNonce(userAddress).call();
5
let functionSignature = contract.methods.setQuote(newQuote).encodeABI();
6
​
7
let message = {};
8
message.nonce = parseInt(nonce);
9
message.from = userAddress;
10
message.functionSignature = functionSignature;
11
​
12
//please refer to SDK front end example for domainType and domainData
13
const dataToSign = JSON.stringify({
14
types: {
15
EIP712Domain: domainType,
16
MetaTransaction: metaTransactionType
17
},
18
domain: domainData,
19
primaryType: "MetaTransaction",
20
message: message
21
});
22
​
23
/*Its important to use eth_signTypedData_v3 and not v4 to get EIP712
24
signature because we have used salt in domain data instead of chainId*/
25
const signature = sigUtil.signTypedMessage(new Buffer.from(privateKey, 'hex'), { data: dataToSign }, 'V3');
26
let { r, s, v } = getSignatureParameters(signature); // same helper used in SDK frontend code
27
let executeMetaTransactionData = contract.methods.executeMetaTransaction(userAddress, functionSignature, r, s, v).encodeABI();
28
​
29
let txParams = {
30
"from": userAddress,
31
"to": config.contract.address,
32
"value": "0x0",
33
"gas": "100000", // (optional) your custom gas limit
34
"data": executeMetaTransactionData
35
};
36
37
const signedTx = await web3.eth.accounts.signTransaction(txParams, `0x${privateKey}`);
38
​
39
let receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction, (error, txHash) => {
40
if (error) {
41
return console.error(error);
42
}
43
console.log("Transaction hash is ", txHash);
44
// do something with transaction hash
45
});
46
47
console.log(receipt.transactionHash);
Copied!
​Check the repository here for complete example code with this approach
1
let privateKey = <PRIVATe_KEY>;
2
let nonce = await contract.methods.getNonce(userAddress).call();
3
let functionSignature = contract.methods.setQuote(newQuote).encodeABI();
4
let chainId = 42;
5
​
6
//same helper constructMetaTransactionMessage used in SDK front end code
7
let messageToSign = constructMetaTransactionMessage(nonce, chainId, functionSignature, config.contract.address);
8
​
9
let {signature} = web3.eth.accounts.sign("0x" + messageToSign.toString("hex"), privateKey);
10
let { r, s, v } = getSignatureParameters(signature); // same helper used in SDK frontend code
11
let executeMetaTransactionData = contract.methods.executeMetaTransaction(userAddress, functionSignature, r, s, v).encodeABI();
12
​
13
let txParams = {
14
"from": userAddress,
15
"to": config.contract.address,
16
"value": "0x0",
17
"gas": "100000", // (optional) your custom gas limit
18
"data": executeMetaTransactionData
19
};
20
21
const signedTx = await web3.eth.accounts.signTransaction(txParams, `0x${privateKey}`);
22
​
23
let receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction, (error, txHash) => {
24
if (error) {
25
return console.error(error);
26
}
27
console.log("Transaction hash is ", txHash);
28
// do something with transaction hash
29
});
30
31
console.log(receipt.transactionHash);
Copied!
​Check the repository here for complete example code with this approach
1
let sigUtil = require("eth-sig-util"); // additional dependency
2
​
3
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
4
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
5
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
6
​
7
let privateKey = <PRIVATe_KEY>;
8
let wallet = new ethers.Wallet(privateKey);
9
let nonce = await contract.getNonce(userAddress);
10
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
11
​
12
let message = {};
13
message.nonce = parseInt(nonce);
14
message.from = userAddress;
15
message.functionSignature = functionSignature;
16
​
17
//please refer to SDK front end example for domainType and domainData
18
const dataToSign = JSON.stringify({
19
types: {
20
EIP712Domain: domainType,
21
MetaTransaction: metaTransactionType
22
},
23
domain: domainData,
24
primaryType: "MetaTransaction",
25
message: message
26
});
27
​
28
/*Its important to use eth_signTypedData_v3 and not v4 to get EIP712
29
signature because we have used salt in domain data instead of chainId*/
30
const signature = sigUtil.signTypedMessage(new Buffer.from(privateKey, 'hex'), { data: dataToSign }, 'V3');
31
let { r, s, v } = getSignatureParameters(signature); // same helper used in SDK frontend code
32
​
33
​
34
let rawTx, tx;
35
rawTx = {
36
to: <YOUR_CONTRACT_ADDRESS>,
37
data: contractInterface.encodeFunctionData("executeMetaTransaction", [userAddress, functionData, r, s, v]),
38
from: userAddress
39
};
40
tx = await wallet.signTransaction(rawTx);
41
​
42
let transactionHash;
43
try {
44
let receipt = await ethersProvider.sendTransaction(tx);
45
console.log(receipt);
46
} catch (error) {
47
/*Ethers check the hash from user's signed tx and hash returned from Biconomy
48
Both hash are expected to be different as biconomy send the transaction from its relayers*/
49
50
// You could also refer to https://github.com/bcnmy/metatx-standard/blob/kovan-demo-ethers-backend/example/react-ui/src/App.js
51
if (error.returnedHash && error.expectedHash) {
52
console.log("Transaction hash : ", error.returnedHash);
53
transactionHash = error.returnedHash;
54
}
55
else{
56
console.log(error);
57
showErrorMessage("Error while sending transaction");
58
}
59
}
60
​
61
if (transactionHash) {
62
// display transactionHash
63
let receipt = await ethersProvider.waitForTransaction(transactionHash);
64
console.log(receipt);
65
//show Success Message
66
} else
67
{
68
showErrorMessage("Could not get transaction hash");
69
}
Copied!
​Check the repository here for complete example code with this approach
1
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
2
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
3
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
4
let chainId = 42;
5
​
6
let privateKey = <PRIVATe_KEY>;
7
let wallet = new ethers.Wallet(privateKey);
8
let nonce = await contract.getNonce(