Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save tomarsachin2271/2e9ece19d5dabb98bb430c74669d4763 to your computer and use it in GitHub Desktop.
Save tomarsachin2271/2e9ece19d5dabb98bb430c74669d4763 to your computer and use it in GitHub Desktop.
// To enable meta transaction, let create a generic method that calls executeMetaTransaction by taking the user signature
// and then execute transaction via Biconomy
// Dependencies
import {toBuffer} from "ethereumjs-util";
import abi from "ethereumjs-abi";
import events from "events";
// Initialization of web3
let web3 = new Web3(window.ethereum);
// Lets first create a method that takes user address, actual method encoded ABI (eg transfer method of ERC20 token) and
// the smart contract instance
// Add some helper methods also we'll create to make the code modular
const executeMetaTransaciton = async (userAddress, functionSignature, contract, contractAddress, chainId) => {
var eventEmitter = new events.EventEmitter();
if(contract && userAddress && functionSignature, chainId, contractAddress) {
let nonce = await contract.methods.getNonce(userAddress).call();
let messageToSign = constructMetaTransactionMessage(nonce, chainId, functionSignature, contractAddress);
const signature = await web3.eth.personal.sign(
"0x" + messageToSign.toString("hex"),
userAddress
);
console.info(`User signature is ${signature}`);
let { r, s, v } = getSignatureParameters(signature);
console.log("before transaction listener");
// No need to calculate gas limit or gas price here
let transactionListener = contract.methods.executeMetaTransaction(userAddress, functionSignature, r, s, v).send({
from: userAddress
});
transactionListener.on("transactionHash", (hash)=>{
eventEmitter.emit("transactionHash", hash);
}).once("confirmation", (confirmation, recipet) => {
eventEmitter.emit("confirmation", confirmation, recipet);
}).on("error", error => {
eventEmitter.emit("error", error);
});
return eventEmitter;
} else {
console.log("All params userAddress, functionSignature, chainId, contract address and contract object are mandatory");
}
}
const constructMetaTransactionMessage = (nonce, chainId, functionSignature, contractAddress) => {
return abi.soliditySHA3(
["uint256","address","uint256","bytes"],
[nonce, contractAddress, chainId, toBuffer(functionSignature)]
);
}
const getSignatureParameters = signature => {
if (!web3.utils.isHexStrict(signature)) {
throw new Error(
'Given value "'.concat(signature, '" is not a valid hex string.')
);
}
var r = signature.slice(0, 66);
var s = "0x".concat(signature.slice(66, 130));
var v = "0x".concat(signature.slice(130, 132));
v = web3.utils.hexToNumber(v);
if (![27, 28].includes(v)) v += 27;
return {
r: r,
s: s,
v: v
};
};
// Now all you need is to call this executeMetaTransaciton method with required parameters from where you are calling your
// actual function eg transfer or mint
/******** BEFORE ********/
let result = contract.methods.transfer(recipientAddress, tokenToTransfer.toString()).send({
from: selectedAddress
});
result.on("transactionHash", (hash)=>{
// On transacion Hash
}).once("confirmation", (confirmation, recipet) => {
// On Confirmation
}).on("error", error => {
// On Error
})
/******* AFTER *******/
let functionSignature = contract.methods.transfer(recipientAddress, tokenToTransfer.toString()).encodeABI();
let result = await executeMetaTransaciton(userAddress, functionSignature, contract, contractAddress, "42");
result.on("transactionHash", (hash)=>{
// On transacion Hash
}).once("confirmation", (confirmation, recipet) => {
// On Confirmation
}).on("error", error => {
// On Error
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment