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.
If you're looking for network agnostic transactions check out this section.
This section is divided into Frontend and Backend integration of SDK. You can choose either of them based on your requirement.

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
npm install @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
const biconomy = new Biconomy(<web3 provider>,{apiKey: <API Key>, debug: true});
3
web3 = new Web3(biconomy);
Copied!
1
import {Biconomy} from "@biconomy/mexa";
2
const biconomy = new Biconomy(<ethers provider>,{apiKey: <API Key>, debug: true});
3
let ethersProvider = new ethers.providers.Web3Provider(biconomy);
Copied!

3. Initialize your DApp after Mexa initialization

Since mexa fetches data from the 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!
​
Done! Interact with Web3 the same way you have been doing before! Now, whenever contract call is made (provided that it is registered in the Dashboard) Mexa will ask for the user's signature and handle the transaction - rather than sending a signed transaction directly to the blockchain from the user's wallet. The message the user will be prompted to sign will be in an eth_personalsign format by default.
If you wish to get EIP712 type structured message signature just include signatureType:”EIP712_SIGN” in your transaction parameters and mexa will take care of taking the signature and execution through the trusted forwarder.

Example Code

Web3 + EIP712Sign
Web3 + PersonalSign
Ethers + EIP712Sign
Ethers + PersonalSign
​Check the repository here for complete example code
1
// Initialize constants
2
let contract = new web3.eth.Contract(
3
<Your Contract ABI>,
4
<Your Contract Address>
5
);
6
​
7
let userAddress = <selected address>;
8
​
9
//Call your target method (must be registered on the dashboard).. here we are calling setQuote() method of our contract
10
let tx = contract.methods.setQuote(newQuote).send({
11
from: userAddress,
12
signatureType: biconomy.EIP712_SIGN,
13
//optionally you can add other options like gasLimit
14
});
15
​
16
tx.on("transactionHash", function (hash) {
17
console.log(`Transaction hash is ${hash}`);
18
showInfoMessage(`Transaction sent. Waiting for confirmation ..`);
19
}).once("confirmation", function (confirmationNumber, receipt) {
20
console.log(receipt);
21
console.log(receipt.transactionHash);
22
//do something with transaction hash
23
});
Copied!
​Check the repository here for complete example code
1
// Initialize constants
2
let contract = new web3.eth.Contract(
3
<Your Contract ABI>,
4
<Your Contract Address>
5
);
6
​
7
let userAddress = <selected address>;
8
​
9
//Call your target method.. here we are calling setQuote() method of our contract
10
let tx = contract.methods.setQuote(newQuote).send({
11
from: userAddress,
12
signatureType: biconomy.PERSONAL_SIGN
13
//signatureType is optional and can be omitted in case of personal sign
14
});
15
​
16
tx.on("transactionHash", function (hash) {
17
console.log(`Transaction hash is ${hash}`);
18
showInfoMessage(`Transaction sent. Waiting for confirmation ..`);
19
}).once("confirmation", function (confirmationNumber, receipt) {
20
console.log(receipt);
21
console.log(receipt.transactionHash);
22
//do something with transaction hash
23
});
Copied!
​Check the repository here for complete example code
1
// Initialize Constants
2
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
3
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
4
5
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
6
​
7
let userAddress = <Selected Address>;
8
​
9
// Create your target method signature.. here we are calling setQuote() method of our contract
10
let { data } = await contract.populateTransaction.setQuote(newQuote);
11
let provider = biconomy.getEthersProvider();
12
​
13
let gasLimit = await provider.estimateGas({
14
to: config.contract.address,
15
from: userAddress,
16
data: data
17
});
18
console.log("Gas limit : ", gasLimit);
19
​
20
let txParams = {
21
data: data,
22
to: <CONTRACT_ADDRESS>,
23
from: userAddress,
24
gasLimit: gasLimit, // optional
25
signatureType: "EIP712_SIGN"
26
};
27
​
28
// as ethers does not allow providing custom options while sending transaction
29
let tx = await provider.send("eth_sendTransaction", [txParams]);
30
console.log("Transaction hash : ", tx);
31
​
32
//event emitter methods
33
provider.once(tx, (transaction) => {
34
// Emitted when the transaction has been mined
35
//show success message
36
console.log(transaction);
37
//do something with transaction hash
38
});
Copied!
​Check the repository here for complete example code
1
// Initialize Constants
2
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
3
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
4
5
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
6
​
7
let userAddress = <Selected Address>;
8
​
9
// Create your target method signature.. here we are calling setQuote() method of our contract
10
let { data } = await contract.populateTransaction.setQuote(newQuote);
11
let provider = biconomy.getEthersProvider();
12
​
13
let gasLimit = await provider.estimateGas({
14
to: config.contract.address,
15
from: userAddress,
16
data: data
17
});
18
console.log("Gas limit : ", gasLimit);
19
​
20
let txParams = {
21
data: data,
22
to: <CONTRACT_ADDRESS>,
23
from: userAddress,
24
gasLimit: gasLimit, // optional
25
signatureType: "PERSONAL_SIGN" // Or omit this because by default mexa will consider personal sign
26
};
27
28
let tx = await provider.send("eth_sendTransaction", [txParams]);
29
console.log("Transaction hash : ", tx);
30
​
31
//event emitter methods
32
provider.once(tx, (transaction) => {
33
// Emitted when the transaction has been mined
34
//show success message
35
console.log(transaction);
36
//do something with transaction hash
37
});
Copied!
​
Congratulations πŸ‘
You have now enabled meta transactions in your DApp.

SDK Backend Integration

Here, we provide more freedom to the developer in case you want to use Biconomy in the 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.
    There is no change in the way you send the transactions from the backend except adding the extra information about the signature type.
The developer can choose to take the user’s signatures with any signature type they wish to.

Passing extra information using web3

web3.eth.sendSignedTransaction(data, callback) will be used here but with parameters mentioned below

Parameters

data A JSON object containing the user's signature, the raw transaction, forward request object and signature type. Data to be signed can be generated using the methodgetForwardRequestAndMessageToSign(rawTransaction)
callback Optional callback, returns an error object as first parameter and the result as second.

Returns

PromiEvent A promise combined event emitter. Will be resolved when the transaction receipt is available

Passing extra information using ethers

provider.send("eth_sendRawTransaction", [data], callback) will be used here but with parameters mentioned below

Parameters

data A JSON object containing the user's signature, the raw transaction, forward request object and signature type. Data to be signed can be generated using the methodgetForwardRequestAndMessageToSign(rawTransaction)
callback Optional callback, returns an error object as first parameter and the result as second.

Returns

Promise A promise which will not resolve until the transaction hash is mined. Promise resolves with transaction hash. One can use the event emitter method of ethers provider to get the mined transaction receipts.
Based on your Meta Transaction Type on the recipient contract (registered as Trusted Forwarder) Mexa will prepare the data to sign in the EIP712 format as well as personal signature format (default)

Example Code

Web3 + EIP712Sign
Web3 + PersonalSign
Ethers + EIP712Sign
Ethers + PersonalSign
​Check the repository here for complete example code
1
let sigUtil = require("eth-sig-util"); // additional dependency
2
​
3
// Initialize constants
4
let contract = new web3.eth.Contract(
5
<CONTRACT_ABI>,
6
<CONTRACT_ADDRESS>
7
);
8
​
9
let userAddress = <selected address>;
10
let privateKey = <PRIVATE_KEY>;
11
​
12
let txParams = {
13
"from": userAddress,
14
//gasLimit is optional or can be pre estimated for your method call before passing
15
"gasLimit": web3.utils.toHex(300000),
16
"to": <CONTRACT_ADDRESS>,
17
//optional. Note: Native meta transactions would not work if your method call is expecting ether value transfer and has checkes on msg.value
18
"value": "0x0",
19
//Call your target method. here we are calling setQuote() method of our contract
20
"data": contract.methods.setQuote(newQuote).encodeABI(),
21
};
22
​
23
const signedTx = await web3.eth.accounts.signTransaction(txParams, `0x${privateKey}`);
24
const forwardData = await biconomy.getForwardRequestAndMessageToSign(signedTx.rawTransaction);
25
const signature = sigUtil.signTypedMessage(new Buffer.from(privateKey, 'hex'),
26
{ data: forwardData.eip712Format }, 'V4');
27
// forwardData has personalSignatureFormat available as well. Check the next tab on the right
28
29
let rawTransaction = signedTx.rawTransaction;
30
​
31
let data = {
32
signature: signature,
33
forwardRequest: forwardData.request,
34
rawTransaction: rawTransaction,
35
signatureType: biconomy.EIP712_SIGN
36
};
37
​
38
// Get the transaction Hash using the Event Emitter returned
39
web3.eth.sendSignedTransaction(data)
40
.on('transactionHash', (hash)=> {
41
console.log(`Transaction hash is ${hash}`)
42
})
43
.once('confirmation', (confirmation, receipt)=> {
44
console.log(`Transaction Confirmed.`);
45
console.log(receipt);
46
//do Something
47
});
48
​
49
/********* OR *********/
50
​
51
// Use any one of the methods below to check for transaction confirmation
52
// USING PROMISE
53
let receipt = await web3.eth.sendSignedTransaction(data, (error, txHash)=>{
54
if(error) {
55
return console.error(error);
56
}
57
console.log(txHash);
58
});
Copied!
​Check the repository here for complete example code
1
let sigUtil = require("eth-sig-util"); // additional dependency
2
​
3
// Initialize constants
4
let contract = new web3.eth.Contract(
5
<CONTRACT_ABI>,
6
<CONTRACT_ADDRESS>
7
);
8
​
9
let userAddress = <selected address>;
10
let privateKey = <PRIVATE_KEY>;
11
​
12
let txParams = {
13
"from": userAddress,
14
//gasLimit is optional or can be pre estimated for your method call before passing
15
"gasLimit": web3.utils.toHex(300000),
16
"to": <CONTRACT_ADDRESS>,
17
//optional. Note: Native meta transactions would not work if your method call is expecting ether value transfer and has checkes on msg.value
18
"value": "0x0",
19
//Call your target method. here we are calling setQuote() method of our contract
20
"data": contract.methods.setQuote(newQuote).encodeABI(),
21
};
22
​
23
const signedTx = await web3.eth.accounts.signTransaction(txParams, `0x${privateKey}`);
24
const forwardData = await biconomy.getForwardRequestAndMessageToSign(signedTx.rawTransaction);
25
​
26
// const signature = sigUtil.personalSign(new Buffer.from(privateKey, 'hex'), { data: forwardData.personalSignatureFormat });
27
let {signature} = web3.eth.accounts.sign("0x" + forwardData.personalSignatureFormat.toString("hex"), privateKey);
28
29
let rawTransaction = signedTx.rawTransaction;
30
​
31
let data = {
32
signature: signature,
33
forwardRequest: forwardData.request,
34
rawTransaction: rawTransaction,
35
signatureType: biconomy.PERSONAL_SIGN //optional. mexa will assume personal signature by default if this is omitted
36
};
37
​
38
// Get the transaction Hash using the Event Emitter returned
39
web3.eth.sendSignedTransaction(data)
40
.on('transactionHash', (hash)=> {
41
console.log(`Transaction hash is ${hash}`)
42
})
43
.once('confirmation', (confirmation, receipt)=> {
44
console.log(`Transaction Confirmed.`);
45
console.log(receipt);
46
//do Something
47
});
48
​
49
/********* OR *********/
50
​
51
// Use any one of the methods below to check for transaction confirmation
52
// USING PROMISE
53
let receipt = await web3.eth.sendSignedTransaction(data, (error, txHash)=>{
54
if(error) {
55
return console.error(error);
56
}
57
console.log(txHash);
58
});
Copied!
​Check the repository here for complete example code
1
let sigUtil = require("eth-sig-util"); // additional dependency
2
​
3
// Initialize Constants
4
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
5
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
6
7
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
8
​
9
let userAddress = <Selected Address>;
10
let privateKey = <PRIVATE_KEY>;
11
​
12
let userSigner = new ethers.Wallet(privateKey);
13
​
14
// Create your target method signature.. here we are calling setQuote() method of our contract
15
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
16
​
17
let rawTx = {
18
to: <CONTRACT_ADDRESS>,
19
data: functionSignature,
20
from: userAddress
21
};
22
​
23
let signedTx = await userSigner.signTransaction(rawTx);
24
// should get user message to sign for EIP712 or personal signature types
25
const forwardData = await biconomy.getForwardRequestAndMessageToSign(signedTx);
26
console.log(forwardData);
27
​
28
// optionally one can sign using sigUtil
29
const signature = sigUtil.signTypedMessage(new Buffer.from(privateKey, 'hex'), { data: forwardData.eip712Format }, 'V3');
30
​
31
let data = {
32
signature: signature,
33
forwardRequest: forwardData.request,
34
rawTransaction: signedTx,
35
signatureType: biconomy.EIP712_SIGN
36
};
37
​
38
let provider = biconomy.getEthersProvider();
39
// send signed transaction with ethers
40
// promise resolves to transaction hash
41
let txHash = await provider.send("eth_sendRawTransaction", [data]);
42
​
43
let receipt = await provider.waitForTransaction(txHash);
44
//do something
Copied!
​Check the repository here for complete example code
1
let sigUtil = require("eth-sig-util"); // additional dependency
2
​
3
// Initialize Constants
4
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
5
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
6
7
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
8
​
9
let userAddress = <Selected Address>;
10
let privateKey = <PRIVATE_KEY>;
11
​
12
let userSigner = new ethers.Wallet(privateKey);
13
​
14
// Create your target method signature.. here we are calling setQuote() method of our contract
15
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
16
​
17
let rawTx = {
18
to: <CONTRACT_ADDRESS>,
19
data: functionSignature,
20
from: userAddress
21
};
22
​
23
let signedTx = await userSigner.signTransaction(rawTx);
24
// should get user message to sign for EIP712 or personal signature types
25
const forwardData = await biconomy.getForwardRequestAndMessageToSign(signedTx);
26
console.log(forwardData);
27
​
28
// optionally one can sign using sigUtil
29
const signature = await userSigner.signMessage(forwardData.personalSignatureFormat);
30
​
31
let data = {
32
signature: signature,
33
forwardRequest: forwardData.request,
34
rawTransaction: signedTx,
35
signatureType: biconomy.PERSONAL_SIGN //optional. as mexa will assume personal signature by default this can be omitted
36
};
37
​
38
let provider = biconomy.getEthersProvider();
39
// send signed transaction with ethers
40
// promise resolves to transaction hash
41
let txHash = await provider.send("eth_sendRawTransaction", [data]);
42
​
43
let receipt = await provider.waitForTransaction(txHash);
44
//do something
Copied!
If you don't wish to use the SDK, gasless meta transactions can be directly sent using Biconomy's APIs also. Check next section for implementation steps.
​
Last modified 7mo ago