What I need your help with: I have followed this guide: https://blog.openzeppelin.com/gasless-metatransactions-with-openzeppelin-defender/ & to put it shortly, I am trying to mint ERC-721 through a default MinimalForwarder.
The problem: sometimes approx. 1 in 7 txs are getting back invalid request
. The rest work without a problem, and then sometimes they just dont.
Environment
I am really lost on this, really appreciate any help or idea about what may be happening!
Code
const EIP712Domain = [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
];
const ForwardRequest = [
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "gas", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "data", type: "bytes" },
];
function getMetaTxTypeData(chainId, verifyingContract) {
return {
types: {
EIP712Domain,
ForwardRequest,
},
domain: {
name: "MinimalForwarder",
version: "0.0.1",
chainId,
verifyingContract,
},
primaryType: "ForwardRequest",
};
}
task("sign", "Sign meta-tx").setAction(async (taskArgs, hre) => {
async function buildRequest(forwarder, input) {
const nonce = await forwarder
.getNonce(input.from)
.then((nonce) => nonce.toString());
return { value: 0, gas: 1e6, nonce, ...input };
}
async function buildTypedData(forwarder, request) {
const chainId = await forwarder.provider
.getNetwork()
.then((n) => n.chainId);
const typeData = getMetaTxTypeData(chainId, forwarder.address);
return { ...typeData, message: request };
}
async function signTypedData(signer, from, data) {
// If signer is a private key, use it to sign
if (typeof signer === "string") {
const privateKey = Buffer.from(signer.replace(/^0x/, ""), "hex");
return signTypedMessage(privateKey, { data });
}
}
async function signMetaTxRequest(signer, forwarder, input) {
const request = await buildRequest(forwarder, input);
const toSign = await buildTypedData(forwarder, request);
const signature = await signTypedData(signer, input.from, toSign);
return { signature, request };
}
function getInstance(name, address) {
return hre.ethers.getContractFactory(name).then((f) => f.attach(address));
}
console.log("Signing...");
const forwarder = await getInstance("MinimalForwarder", ForwarderAddress);
const contractFactory = await getInstance(
"ContractFactory",
"string data"
);
const from = new hre.ethers.Wallet(taskArgs.privatekey).address;
const data = contractFactory.interface.encodeFunctionData("mintNFT", [
"string data",
"string data",
]);
const result = await signMetaTxRequest(taskArgs.privatekey, forwarder, {
to: contractFactory.address,
from,
data,
});
const { request, signature } = result;
// Initialize Relayer provider and signer, and forwarder contract
const credentials = {
apiKey: "###",
apiSecret: "###",
};
const provider = new DefenderRelayProvider(credentials);
const signer = new DefenderRelaySigner(credentials, provider, {
speed: "average",
});
const forwarderContract = new hre.ethers.Contract(
ForwarderAddress,
ForwarderAbi,
signer
);
// Validate request on the forwarder contract
console.log("Verifying... ");
const valid = await forwarderContract.verify(request, signature);
if (!valid) throw new Error("Invalid request");
console.log("TX valid... ");
// Relay transaction!
const gasLimit = (parseInt(request.gas) + 50000).toString();
const tx = await forwarderContract.execute(request, signature, {
gasLimit,
});
// const tx = await relay(forwarder, request, signature);
console.log(`Sent meta-tx: ${tx.hash}`);
return { txHash: tx.hash };
});