API
Use our APIs to easily implement meta transactions!

Note:

Before using this API, make sure you have Trusted Forwarder support in your smart contracts i.e., you have removed the dependency on msg.sender property from your smart contracts and trusted forwarder is added.
post
https://api.biconomy.io
/api/v2/meta-tx/native
/api/v2/meta-tx/native

Example Curl Request

curl --request POST 'https://api.biconomy.io/api/v2/meta-tx/native' --header 'x-api-key: <api_key_from_dashboard>' --header 'Content-Type: application/json' --data-raw '{ "from": "<user_public_address>", "apiId": "<api_id_from_dashboard>", "params": [<param1>,<param2>,...... ], "to": "<recipient_contract_address>","gasLimit":"0xF4240","signatureType":"EIP712Sign" }'
If the signature type is EIP712 params are request, domainSeperator, signature.
If signature type is personal sign (assumed default if signatureType parameter is omitted in API request body) params are request and signature.
​
Biconomy has helpers available to build above mentioned parameters from your dapp transaction.
For EIP2771, below is an example code on how to use Biconomy helpers JS to build request, domain separator and data to sign for each signature type and then send API request to the Biconomy servers where our relayer infrastructure will process your transaction.

Sample code

web3
ethers
​Check the repository here for complete example code
1
import {
2
helperAttributes,
3
getDomainSeperator,
4
getDataToSignForPersonalSign,
5
getDataToSignForEIP712,
6
buildForwardTxRequest,
7
getBiconomyForwarderConfig
8
} from './biconomyForwarderHelpers';
9
10
let sigUtil = require("eth-sig-util"); // additional dependency
11
​
12
// This web3 instance is used to get user signature from connected wallet
13
let walletWeb3 = new Web3(window.ethereum);
14
​
15
​
16
let userAddress = <selected address>;
17
let networkId = <your provider networkId here>;
18
​
19
// Initialize Contracts
20
let contract = new web3.eth.Contract(
21
<CONTRACT_ABI>,
22
<CONTRACT_ADDRESS>
23
);
24
​
25
let functionSignature = contract.methods
26
.setQuote(newQuote)
27
.encodeABI();
28
let txGas = await contract.methods
29
.setQuote(newQuote)
30
.estimateGas({ from: userAddress });
31
32
let forwarder = await getBiconomyForwarderConfig(networkId);
33
let forwarderContract = new web3.eth.Contract(
34
forwarder.abi,
35
forwarder.address
36
);
37
​
38
//const batchId = await forwarderContract.methods.getBatch(userAddress).call();
39
const batchNonce = await forwarderContract.methods.getNonce(address,0).call();
40
const gasLimitNum = Number(txGas);
41
const to = <CONTRACT_ADDRESS>;
42
​
43
const request = await buildForwardTxRequest({account:userAddress,to,gasLimitNum,batchId,batchNonce,data:functionSignature});
44
​
45
/* If you wish to use EIP712 Signature type check below code*/
46
​
47
​
48
const hashToSign = getDataToSignForPersonalSign(request);
49
const sig = await walletWeb3.eth.personal.sign("0x" + hashToSign.toString("hex"), userAddress);
50
sendTransaction({userAddress, request, sig, signatureType:biconomy.PERSONAL_SIGN});
51
// notice domain seperator is not passed here
52
​
53
///////////////////////////////////////////////////////////
54
​
55
// If you wish to use EIP712 Signature type
56
//build the request as mentioned above
57
const domainSeparator = await getDomainSeperator(42);
58
console.log(domainSeparator);
59
const dataToSign = await getDataToSignForEIP712(request,networkId);
60
61
​
62
walletWeb3.currentProvider.send(
63
{
64
jsonrpc: "2.0",
65
id: 999999999999,
66
method: "eth_signTypedData_v4",
67
params: [userAddress, dataToSign]
68
},
69
function (error, response) {
70
console.info(`User signature is ${response.result}`);
71
if (error || (response && response.error)) {
72
showErrorMessage("Could not get user signature");
73
} else if (response && response.result) {
74
let sig = response.result;
75
sendTransaction({userAddress, request, domainSeparator, sig, signatureType:biconomy.EIP712_SIGN});
76
}
77
}
78
);
79
80
Copied!
​Check the repository here for complete example code
1
import {
2
helperAttributes,
3
getDomainSeperator,
4
getDataToSignForPersonalSign,
5
getDataToSignForEIP712,
6
buildForwardTxRequest,
7
getBiconomyForwarderConfig
8
} from './biconomyForwarderHelpers';
9
​
10
// Extra Dependencies
11
import abi from "ethereumjs-abi";
12
import {toBuffer} from "ethereumjs-util";
13
​
14
let walletProvider, walletSigner;
15
​
16
walletProvider = new ethers.providers.Web3Provider(window.ethereum);
17
walletSigner = walletProvider.getSigner();
18
​
19
// Initialize Constants
20
​
21
let userAddress = <selected address>;
22
let networkId = <your provider networkId here>;
23
​
24
// Initialize Contracts
25
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
26
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
27
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
28
​
29
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
30
let gasPrice = await ethersProvider.getGasPrice();
31
let gasLimit = await ethersProvider.estimateGas({
32
to: contract.address,
33
from: address,
34
data: functionSignature
35
});
36
37
let forwarder = await getBiconomyForwarderConfig(networkId);
38
let forwarderContract = new ethers.Contract(
39
forwarder.address,
40
forwarder.abi,
41
biconomy.getSignerByAddress(userAddress)
42
);
43
​
44
const batchNonce = await forwarderContract.getNonce(userAddress,0);
45
//const batchId = await forwarderContract.getBatch(userAddress);
46
const to = <CONTRACT_ADDRESS>;
47
const gasLimitNum = Number(gasLimit.toNumber().toString());
48
const request = await buildForwardTxRequest({account:userAddress,to,gasLimitNum,batchId,batchNonce,data});
49
​
50
/* If you wish to use EIP712 Signature type */
51
const domainSeparator = getDomainSeperator(networkId);
52
const dataToSign = await getDataToSignForEIP712(request,networkId);
53
​
54
let sig;
55
// get the user's signature
56
walletProvider.send("eth_signTypedData_v3", [userAddress, dataToSign])
57
.then(function(sig){
58
sendTransaction({userAddress, request, domainSeparator, sig, signatureType:biconomy.EIP712_SIGN});
59
})
60
.catch(function(error) {
61
console.log(error)
62
});
63
64
/////////////////////////////
65
​
66
/** If you wish to use Personal Signature type
67
* //domain seperator is not required
68
* //build the request as mentioned above
69
* const hashToSign = getDataToSignForPersonalSign(request);
70
*/
71
72
/* walletSigner.signMessage(hashToSign)
73
.then(function(sig){
74
console.log('signature ' + sig);
75
// make API call
76
//sendTransaction({userAddress, request, sig, signatureType:biconomy.PERSONAL_SIGN});
77
})
78
.catch(function(error) {
79
console.log(error)
80
});
81
*/
Copied!
Great! once your parameters are built you are ready to make the API call to send gasless transactions!
1
const sendTransaction = async ({userAddress, request, sig, domainSeparator, signatureType}) => {
2
if (web3 && contract) {
3
let params;
4
if (domainSeparator) {
5
params = [request, domainSeparator, sig];
6
} else {
7
params = [request, sig];
8
}
9
try {
10
fetch(`https://api.biconomy.io/api/v2/meta-tx/native`, {
11
method: "POST",
12
headers: {
13
"x-api-key": <YOUR_DAPP_API_KEY>,
14
"Content-Type": "application/json;charset=utf-8",
15
},
16
body: JSON.stringify({
17
to: <CONTRACT_ADDRESS>,
18
apiId: <METHOD_API_ID>,
19
params: params,
20
from: userAddress,
21
signatureType: signatureType
22
}),
23
})
24
.then((response) => response.json())
25
.then(async function (result) {
26
console.log(result);
27
showInfoMessage(
28
`Transaction sent by relayer with hash ${result.txHash}`
29
);
30
​
31
let receipt = await getTransactionReceiptMined(
32
result.txHash,
33
2000
34
);
35
setTransactionHash(result.txHash);
36
showSuccessMessage("Transaction confirmed on chain");
37
getQuoteFromNetwork();
38
})
39
.catch(function (error) {
40
console.log(error);
41
});
42
} catch (error) {
43
console.log(error);
44
}
45
}
46
};
47
48
////helper///
49
​
50
//for web3
51
const getTransactionReceiptMined = (txHash, interval) => {
52
const self = this;
53
const transactionReceiptAsync = async function(resolve, reject) {
54
var receipt = await web3.eth.getTransactionReceipt(txHash);
55
if (receipt == null) {
56
setTimeout(
57
() => transactionReceiptAsync(resolve, reject),
58
interval ? interval : 500);
59
} else {
60
resolve(receipt);
61
}
62
};
63
64
if (typeof txHash === "string") {
65
return new Promise(transactionReceiptAsync);
66
} else {
67
throw new Error("Invalid Type: " + txHash);
68
}
69
};
Copied!

​

Congratulations πŸ‘

You're now ready to use the trusted forwarder approach and enable gasless transactions in your Dapp using SDK and/or APIs. Check out other implementations and in depth technical details in the following sections.
Last modified 1mo ago