API
How to use the Forward API to enable your users to pay gas using ERC20 tokens
If you're trying to integrate Biconomy into an existing library for handling transactions or logic unique to your app, it may make sense to call Biconomy's API directly. This is especially true if your app is not made in Javascript or Typescript.
Using the API lets you relay the transaction directly to your smart contract by paying the gas fees in supported stable coins. It completely bypasses the Ethers/Web3 etc objects you may use to query contracts and or sign 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 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 made helpers to build above mentioned parameters from your dapp transaction. These have been written in JS, but you could easily port the functions if you're working in another language.
In order to enable paying gas fees in ERC20 tokens, below is example code 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
/* this example demonstrates paying DAI tokens as gas fees*/
2
// token address would be DAI address for your provider network Id
3
​
4
import { helperAttributes,
5
erc20FeeProxyAddressMap,
6
getDomainSeperator,
7
getDataToSignForPersonalSign,
8
getDataToSignForEIP712,
9
buildForwardTxRequest,
10
getBiconomyForwarderConfig,
11
getTokenGasPrice,
12
getDaiPermit} from "./erc20ForwarderHelpers";
13
​
14
let provider = <ethereum provider>;
15
let address = <wallet public address>;
16
let networkId = <your provider networkId here>;
17
let tokenAddress = <address of token to pay with>;
18
​
19
// Initialize Contracts
20
let contract = new web3.eth.Contract(
21
<CONTRACT_ABI>,
22
<CONTRACT_ADDRESS>
23
);
24
​
25
let functionSignature = contract.methods.addRating(1, 5).encodeABI();
26
let gasLimit = await contract.methods.addRating(1, 5).estimateGas({from: address});
27
​
28
let forwarder = await getBiconomyForwarderConfig(networkId);
29
let forwarderContract = new web3.eth.Contract(
30
forwarder.abi,
31
forwarder.address
32
);
33
​
34
//const batchId = await forwarderContract.methods.getBatch(userAddress).call();
35
const batchNonce = await forwarderContract.methods.getNonce(address,0).call();
36
const gasLimitNum = Number(gasLimit);
37
​
38
// only required if user has not given permit earlier to spend dai
39
// all options are optional except network Id
40
const daiPermitOptions = {
41
spender: erc20FeeProxyAddressMap[networkId],
42
expiry: Math.floor(Date.now() / 1000 + 3600),
43
allowed: true,
44
networkId: networkId
45
};
46
​
47
await getDaiPermit(provider,address,daiPermitOptions);
48
​
49
let forwarder = await getBiconomyForwarder(provider,networkId);
50
​
51
//const batchId = await forwarder.getBatch(address);
52
const batchNonce = await forwarder.getNonce(address,0);
53
const tokenGasPrice = await getTokenGasPrice(networkId,tokenAddress);
54
​
55
let builtTx = await buildForwardTxRequest(networkId,{account:address,to:contract.address,gasLimitNum,batchId:0,batchNonce,tokenGasPrice:tokGasPrice,data:functionSignature,tokenAddress});
56
const req = builtTx.request;
57
console.log(req);
58
// Show your users fees!
59
const cost = builtTx.cost;
60
console.log(`paying ${cost} number of tokens as gas fees`);
61
​
62
​
63
/* If you wish to use EIP712 Signature type */
64
const domainSeparator = getDomainSeperator(networkId);
65
const dataToSign = getDataToSignForEIP712(req,networkId);
66
​
67
let sig;
68
// sign message //
69
​
70
// get the signature with RPC method eth_signTypedData_v4
71
// NOTE : for personal signature refer code on line# 60
72
​
73
const promi = new Promise(async function(resolve, reject) {
74
await web3.currentProvider.send(
75
{
76
jsonrpc: "2.0",
77
id: 999999999999,
78
method: "eth_signTypedData_v4",
79
params: [address, dataToSign]
80
}, function(error, res){
81
if(error) {
82
reject(error);
83
} else {
84
resolve(res.result);
85
}
86
});
87
});
88
​
89
promi.then(async function(signature){
90
console.log('signature ' + signature);
91
sig = signature;
92
// make the API call here i.e. sendTransaction
93
}).catch(function(error) {
94
console.log('could not get signature error ' + error);
95
});
96
​
97
/** If you wish to use Personal Signature type
98
* //domain seperator is not required
99
* //build the request as mentioned above
100
* const hashToSign = getDataToSignForPersonalSign(req);
101
* const sig = await web3.eth.personal.sign("0x" + hashToSign.toString("hex"), userAddress);
102
*/
103
​
104
Copied!
​Check the repository here for complete example code
1
/* this example demonstrates paying DAI tokens as gas fees*/
2
// token address would be DAI for your provider network Id
3
// you can use any supported token by passing tokenAddress in forward request and getTokenGasPrice below
4
​
5
import { helperAttributes,
6
erc20FeeProxyAddressMap,
7
getDomainSeperator,
8
getDataToSignForPersonalSign,
9
getDataToSignForEIP712,
10
buildForwardTxRequest,
11
getBiconomyForwarderConfig,
12
getTokenGasPrice,
13
getDaiPermit} from "./erc20ForwarderHelpers";
14
​
15
let provider = <ethereum provider>;
16
let address = <wallet public address>;
17
let networkId = <your provider networkId here>;
18
let tokenAddress = <address of token to pay with>;
19
​
20
let ethersProvider = new ethers.providers.Web3Provider(provider);
21
let signer = ethersProvider.getSigner();
22
​
23
// Initialize Contracts
24
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
25
<CONTRACT_ABI>, signer);
26
​
27
let {data} = await contract.populateTransaction.addRating(1, 5);
28
let gasPrice = await ethersProvider.getGasPrice();
29
let gasLimit = await ethersProvider.estimateGas({
30
to: contract.address,
31
from: address,
32
data: data
33
});
34
35
// only required if user has not given permit earlier to spend dai
36
// all options are optional except network Id
37
const daiPermitOptions = {
38
spender: erc20FeeProxyAddressMap[networkId],
39
expiry: Math.floor(Date.now() / 1000 + 3600),
40
allowed: true,
41
networkId: networkId
42
};
43
​
44
await getDaiPermit(provider,address,daiPermitOptions);
45
​
46
let forwarder = await getBiconomyForwarderConfig(networkId);
47
let forwarderContract = new ethers.Contract(
48
forwarder.address,
49
forwarder.abi,
50
signer
51
);
52
​
53
const batchNonce = await forwarderContract.getNonce(userAddress,0);
54
//const batchId = await forwarderContract.getBatch(userAddress);
55
​
56
const tokenGasPrice = await getTokenGasPrice(networkId,tokenAddress);
57
const gasLimitNum = Number(gasLimit);
58
​
59
let builtTx = await buildForwardTxRequest(networkId,{account:address,to:contract.address,gasLimitNum,batchId:0,batchNonce,tokenGasPrice:tokGasPrice,data,tokenAddress});
60
const req = builtTx.request;
61
console.log(req);
62
// Show your users fees!
63
const cost = builtTx.cost;
64
console.log(`paying ${cost} number of tokens as gas fees`);
65
​
66
​
67
/* If you wish to use EIP712 Signature type */
68
const domainSeparator = getDomainSeperator(networkId);
69
const dataToSign = getDataToSignForEIP712(req,networkId);
70
​
71
let sig;
72
// get the user's signature
73
ethersProvider.send("eth_signTypedData_v4", [address, dataToSign])
74
.then(function(signature){
75
sig = signature;
76
// make the API call
77
//sendTransaction({userAddress, req, domainSeparator, sig, signatureType:"EIP712_SIGN"});
78
})
79
.catch(function(error) {
80
console.log(error)
81
});
82
​
83
/** If you wish to use Personal Signature type
84
* //domain seperator is not required
85
* //build the request as mentioned above
86
* const hashToSign = getDataToSignForPersonalSign(req);
87
*/
88
89
/* signer.signMessage(hashToSign)
90
.then(function(sig){
91
console.log('signature ' + sig);
92
// make API call
93
//sendTransaction({userAddress, req, sig, signatureType:"PERSONAL_SIGN"});
94
})
95
.catch(function(error) {
96
console.log(error)
97
});
98
*/
Copied!
​
Great! once your parameters are built you are ready to make the API call to send gasless transactions!
1
const sendTransaction = ({userAddress, req, sig, domainSeparator, signatureType}) => {
2
if (web3 && contract) {
3
let params;
4
if(domainSeparator) {
5
params = [req, domainSeparator, sig]
6
} else {
7
params = [req, sig]
8
}
9
try {
10
fetch(`https://api.biconomy.io/api/v2/meta-tx/native`, {
11
method: "POST",
12
headers: {
13
"x-api-key" : <dapp api key>,
14
'Content-Type': 'application/json;charset=utf-8'
15
},
16
body: JSON.stringify({
17
"to": contract.address,
18
"apiId": <api_id_from_dashboard>,
19
"params": params,
20
"from": userAddress,
21
"signatureType": signatureType
22
})
23
})
24
.then(response=>response.json())
25
.then(function(result) {
26
console.log('transaction hash ' + result.txHash);
27
})
28
// once you receive transaction hash you can wait for mined transaction receipt here
29
// using Promise in web3 : web3.eth.getTransactionReceipt
30
// or using ethersProvider event emitters
31
.catch(function(error) {
32
console.log(error)
33
});
34
} catch (error) {showInfoMessage(`Transaction sent by relayer with hash ${result.txHash}`);
35
console.log(error);
36
}
37
}
38
};
Copied!

Congratulations πŸ‘

You're now ready to enable paying gas fees for transactions using ERC20 tokens in your DApp using our API.
Last modified 3mo ago