API
Use native meta transaction APIs to easily implement meta transactions!
Before using this API, make sure your smart contracts inherits from one of the contracts mentioned in Custom Implementation , and you have removed the dependency on msg.sender property from your smart contracts by replacing it with msgSender() method.
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 '{ "userAddress": "<user_public_address>", "apiId": "<api_id_from_dashboard>", "params": [<param1>,<param2>,...... ], "gasLimit":"0xF4240" }'

Example Code Snippets

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
// This web3 instance is used to get user signature from connected wallet
4
let walletWeb3 = new Web3(window.ethereum);
5
​
6
// Initialize constants
7
const domainType = [
8
{ name: "name", type: "string" },
9
{ name: "version", type: "string" },
10
{ name: "verifyingContract", type: "address" },
11
{ name: "salt", type: "bytes32" },
12
];
13
const metaTransactionType = [
14
{ name: "nonce", type: "uint256" },
15
{ name: "from", type: "address" },
16
{ name: "functionSignature", type: "bytes" }
17
];
18
// replace the chainId 42 if network is not kovan
19
let domainData = {
20
name: "TestContract",
21
version: "1",
22
verifyingContract: config.contract.address,
23
// converts Number to bytes32. pass your chainId instead of 42 if network is not Kovan
24
salt : '0x' + (42).toString(16).padStart(64, '0')
25
};
26
​
27
let userAddress = <selected address>;
28
let contract = new web3.eth.Contract(
29
<Your Contract ABI>,
30
<Your Contract Address>
31
);
32
33
let nonce = await contract.methods.getNonce(userAddress).call();
34
// Create your target method signature.. here we are calling setQuote() method of our contract
35
let functionSignature = contract.methods.setQuote(newQuote).encodeABI();
36
let message = {};
37
message.nonce = parseInt(nonce);
38
message.from = userAddress;
39
message.functionSignature = functionSignature;
40
​
41
const dataToSign = JSON.stringify({
42
types: {
43
EIP712Domain: domainType,
44
MetaTransaction: metaTransactionType
45
},
46
domain: domainData,
47
primaryType: "MetaTransaction",
48
message: message
49
});
50
​
51
​
52
web3.currentProvider.send(
53
{
54
jsonrpc: "2.0",
55
id: 999999999999,
56
method: "eth_signTypedData_v4",
57
params: [userAddress, dataToSign]
58
},
59
function (error, response) {
60
console.info(`User signature is ${response.result}`);
61
if (error || (response && response.error))
62
{
63
showErrorMessage("Could not get user signature");
64
}
65
else if (response && response.result)
66
{
67
let { r, s, v } = getSignatureParameters(response.result);
68
sendTransaction(userAddress, functionSignature, r, s, v);
69
}
70
}
71
);
72
​
73
///////////
74
//helpers//
75
​
76
const sendTransaction = async (userAddress, functionData, r, s, v) => {
77
if (web3 && contract) {
78
try {
79
fetch(`https://api.biconomy.io/api/v2/meta-tx/native`, {
80
method: "POST",
81
headers: {
82
"x-api-key" : <BICONOMY_DAPP_API_KEY>,
83
'Content-Type': 'application/json;charset=utf-8'
84
},
85
body: JSON.stringify({
86
"to": config.contract.address,
87
"apiId": <METHOD_API_ID>,
88
"params": [userAddress, functionData, r, s, v],
89
"from": userAddress
90
})
91
})
92
.then(response=>response.json())
93
.then(async function(result) {
94
console.log(result);
95
showInfoMessage(`Transaction sent by relayer with hash ${result.txHash}`);
96
97
let receipt = await getTransactionReceiptMined(result.txHash, 2000);
98
setTransactionHash(result.txHash);
99
showSuccessMessage("Transaction confirmed on chain");
100
getQuoteFromNetwork();
101
}).catch(function(error) {
102
console.log(error)
103
});
104
} catch (error) {
105
console.log(error);
106
}
107
}
108
};
109
110
const getTransactionReceiptMined = (txHash, interval) => {
111
const self = this;
112
const transactionReceiptAsync = async function(resolve, reject) {
113
var receipt = await web3.eth.getTransactionReceipt(txHash);
114
if (receipt == null) {
115
setTimeout(
116
() => transactionReceiptAsync(resolve, reject),
117
interval ? interval : 500);
118
} else {
119
resolve(receipt);
120
}
121
};
122
123
if (typeof txHash === "string") {
124
return new Promise(transactionReceiptAsync);
125
} else {
126
throw new Error("Invalid Type: " + txHash);
127
}
128
};
129
130
131
const getSignatureParameters = signature => {
132
if (!web3.utils.isHexStrict(signature)) {
133
throw new Error(
134
'Given value "'.concat(signature, '" is not a valid hex string.')
135
);
136
}
137
var r = signature.slice(0, 66);
138
var s = "0x".concat(signature.slice(66, 130));
139
var v = "0x".concat(signature.slice(130, 132));
140
v = web3.utils.hexToNumber(v);
141
if (![27, 28].includes(v)) v += 27;
142
return {
143
r: r,
144
s: s,
145
v: v
146
};
147
};
Copied!
​Check the repository here for complete example code.
1
let abi = require('ethereumjs-abi'); //dependency
2
​
3
// This web3 instance is used to get user signature from connected wallet
4
let walletWeb3 = new Web3(window.ethereum);
5
​
6
// Initialize constants
7
let contract = new web3.eth.Contract(
8
<Your Contract ABI>,
9
<Your Contract Address>
10
);
11
​
12
let nonce = await contract.methods.getNonce(userAddress).call();
13
let functionSignature = contract.methods.setQuote(newQuote).encodeABI();
14
​
15
let messageToSign = constructMetaTransactionMessage(nonce,
16
<CHAIN_ID>, functionSignature,
17
<YOUR_CONTRACT_ADDRESS>);
18
19
// NOTE: We are using walletWeb3 here to get signature from connected wallet
20
const signature = await walletWeb3.eth.personal.sign(
21
"0x" + messageToSign.toString("hex"),
22
userAddress
23
);
24
​
25
let { r, s, v } = getSignatureParameters(signature);
26
sendTransaction(userAddress, functionSignature, r, s, v);
27
​
28
///////////
29
//helpers//
30
​
31
const sendTransaction = async (userAddress, functionData, r, s, v) => {
32
if (web3 && contract) {
33
try {
34
fetch(`https://api.biconomy.io/api/v2/meta-tx/native`, {
35
method: "POST",
36
headers: {
37
"x-api-key" : <BICONOMY_DAPP_API_KEY>,
38
'Content-Type': 'application/json;charset=utf-8'
39
},
40
body: JSON.stringify({
41
"to": config.contract.address,
42
"apiId": <METHOD_API_ID>,
43
"params": [userAddress, functionData, r, s, v],
44
"from": userAddress
45
})
46
})
47
.then(response=>response.json())
48
.then(async function(result) {
49
console.log(result);
50
showInfoMessage(`Transaction sent by relayer with hash ${result.txHash}`);
51
52
let receipt = await getTransactionReceiptMined(result.txHash, 2000);
53
setTransactionHash(result.txHash);
54
showSuccessMessage("Transaction confirmed on chain");
55
getQuoteFromNetwork();
56
}).catch(function(error) {
57
console.log(error)
58
});
59
} catch (error) {
60
console.log(error);
61
}
62
}
63
};
64
65
const getTransactionReceiptMined = (txHash, interval) => {
66
const self = this;
67
const transactionReceiptAsync = async function(resolve, reject) {
68
var receipt = await web3.eth.getTransactionReceipt(txHash);
69
if (receipt == null) {
70
setTimeout(
71
() => transactionReceiptAsync(resolve, reject),
72
interval ? interval : 500);
73
} else {
74
resolve(receipt);
75
}
76
};
77
78
if (typeof txHash === "string") {
79
return new Promise(transactionReceiptAsync);
80
} else {
81
throw new Error("Invalid Type: " + txHash);
82
}
83
};
84
85
86
const getSignatureParameters = signature => {
87
if (!web3.utils.isHexStrict(signature)) {
88
throw new Error(
89
'Given value "'.concat(signature, '" is not a valid hex string.')
90
);
91
}
92
var r = signature.slice(0, 66);
93
var s = "0x".concat(signature.slice(66, 130));
94
var v = "0x".concat(signature.slice(130, 132));
95
v = web3.utils.hexToNumber(v);
96
if (![27, 28].includes(v)) v += 27;
97
return {
98
r: r,
99
s: s,
100
v: v
101
};
102
};
103
104
const constructMetaTransactionMessage = (nonce, salt, functionSignature, contractAddress) => {
105
return abi.soliditySHA3(
106
["uint256","address","uint256","bytes"],
107
[nonce, contractAddress, salt, toBuffer(functionSignature)]
108
);
109
}
Copied!
​Check the repository here for complete example code.
1
let walletProvider, walletSigner;
2
​
3
walletProvider = new ethers.providers.Web3Provider(window.ethereum);
4
walletSigner = walletProvider.getSigner();
5
​
6
// Initialize Constants
7
const domainType = [
8
{ name: "name", type: "string" },
9
{ name: "version", type: "string" },
10
{ name: "verifyingContract", type: "address" },
11
{ name: "salt", type: "bytes32" },
12
];
13
const metaTransactionType = [
14
{ name: "nonce", type: "uint256" },
15
{ name: "from", type: "address" },
16
{ name: "functionSignature", type: "bytes" }
17
];
18
// replace the chainId 42 if network is not kovan
19
let domainData = {
20
name: "TestContract",
21
version: "1",
22
verifyingContract: config.contract.address,
23
// converts Number to bytes32. Change 42 to your chainId if network is not Kovan
24
salt: ethers.utils.hexZeroPad((ethers.BigNumber.from(42)).toHexString(), 32)
25
};
26
​
27
let contract = new ethers.Contract(<CONTRACT_ADDRESS>,
28
<CONTRACT_ABI>, biconomy.getSignerByAddress(userAddress));
29
let contractInterface = new ethers.utils.Interface(<CONTRACT_ABI>);
30
​
31
/*
32
This provider is linked to your wallet.
33
If needed, substitute your wallet solution in place of window.ethereum
34
*/
35
walletProvider = new ethers.providers.Web3Provider(window.ethereum);
36
walletSigner = walletProvider.getSigner();
37
​
38
let nonce = await contract.getNonce(userAddress);
39
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
40
41
let message = {};
42
message.nonce = parseInt(nonce);
43
message.from = userAddress;
44
message.functionSignature = functionSignature;
45
​
46
const dataToSign = JSON.stringify({
47
types: {
48
EIP712Domain: domainType,
49
MetaTransaction: metaTransactionType
50
},
51
domain: domainData,
52
primaryType: "MetaTransaction",
53
message: message
54
});
55
​
56
/*Its important to use eth_signTypedData_v3 and not v4 to get EIP712 signature
57
because we have used salt in domain data instead of chainId*/
58
// Get the EIP-712 Signature and send the transaction
59
let signature = await walletProvider.send("eth_signTypedData_v3", [userAddress, dataToSign])
60
let { r, s, v } = getSignatureParameters(signature);
61
sendTransaction(userAddress, functionSignature, r, s, v);
62
​
63
///////////
64
/*helpers*/
65
​
66
const getSignatureParameters = signature => {
67
if (!ethers.utils.isHexString(signature)) {
68
throw new Error(
69
'Given value "'.concat(signature, '" is not a valid hex string.')
70
);
71
}
72
var r = signature.slice(0, 66);
73
var s = "0x".concat(signature.slice(66, 130));
74
var v = "0x".concat(signature.slice(130, 132));
75
v = ethers.BigNumber.from(v).toNumber();
76
if (![27, 28].includes(v)) v += 27;
77
return {
78
r: r,
79
s: s,
80
v: v
81
};
82
};
83
84
const sendTransaction = async (userAddress, functionData, r, s, v) => {
85
if (ethersProvider && contract) {
86
try {
87
fetch(`https://api.biconomy.io/api/v2/meta-tx/native`, {
88
method: "POST",
89
headers: {
90
"x-api-key" : <BICONOMY_DASHBOARD_API_KEY>,
91
'Content-Type': 'application/json;charset=utf-8'
92
},
93
body: JSON.stringify({
94
"to": config.contract.address,
95
"apiId": <METHOD_API_ID>,
96
"params": [userAddress, functionData, r, s, v],
97
"from": userAddress
98
})
99
})
100
.then(response=>response.json())
101
.then(async function(result) {
102
console.log(result);
103
showInfoMessage(`Transaction sent by relayer with hash ${result.txHash}`);
104
let receipt = await ethersProvider.waitForTransaction(
105
result.txHash
106
);
107
console.log(receipt);
108
setTransactionHash(receipt.transactionHash);
109
showSuccessMessage("Transaction confirmed on chain");
110
getQuoteFromNetwork();
111
}).catch(function(error) {
112
console.log(error)
113
});
114
} catch (error) {
115
console.log(error);
116
}
117
}
118
};
Copied!
​Check the repository here for complete example code.
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
​
20
let nonce = await contract.getNonce(userAddress); //BigNumber
21
let functionSignature = contractInterface.encodeFunctionData("setQuote", [newQuote]);
22
23
let messageToSign = constructMetaTransactionMessage(nonce.toNumber(), <CHAIN_ID> functionSignature, <YOUR_CONTRACT_ADDRESS>);
24
const signature = await walletSigner.signMessage(messageToSign);
25
let { r, s, v } = getSignatureParameters(signature);
26
sendTransaction(userAddress, functionSignature, r, s, v);
27
​
28
//////////
29
/**helpers**/
30
​
31
const getSignatureParameters = signature => {
32
if (!ethers.utils.isHexString(signature)) {
33
throw new Error(
34
'Given value "'.concat(signature, '" is not a valid hex string.')
35
);
36
}
37
var r = signature.slice(0, 66);
38
var s = "0x".concat(signature.slice(66, 130));
39
var v = "0x".concat(signature.slice(130, 132));
40
v = ethers.BigNumber.from(v).toNumber();
41
if (![27, 28].includes(v)) v += 27;
42
return {
43
r: r,
44
s: s,
45
v: v
46
};
47
};
48
49
const constructMetaTransactionMessage = (nonce, salt, functionSignature, contractAddress) => {
50
return abi.soliditySHA3(
51
["uint256","address","uint256","bytes"],
52
[nonce, contractAddress, salt, toBuffer(functionSignature)]
53
);
54
}
55
​
56
const sendTransaction = async (userAddress, functionData, r, s, v) => {
57
if (ethersProvider && contract) {
58
try {
59
fetch(`https://api.biconomy.io/api/v2/meta-tx/native`, {
60
method: "POST",
61
headers: {
62
"x-api-key" : <BICONOMY_DASHBOARD_API_KEY>,
63
'Content-Type': 'application/json;charset=utf-8'
64
},
65
body: JSON.stringify({
66
"to": config.contract.address,
67
"apiId": <METHOD_API_ID>,
68
"params": [userAddress, functionData, r, s, v],
69
"from": userAddress
70
})
71
})
72
.then(response=>response.json())
73
.then(async function(result) {
74
console.log(result);
75
showInfoMessage(`Transaction sent by relayer with hash ${result.txHash}`);
76
let receipt = await ethersProvider.waitForTransaction(
77
result.txHash
78
);
79
console.log(receipt);
80
setTransactionHash(receipt.transactionHash);
81
showSuccessMessage("Transaction confirmed on chain");
82
getQuoteFromNetwork();
83
}).catch(function(error) {
84
console.log(error)
85
});
86
} catch (error) {
87
console.log(error);
88
}
89
}
90
};
91
​
92
​
Copied!
​
Last modified 1mo ago