I am trying to execute the following function with a Relayer setup in Defender:
function mint(address _to) external onlyRole(MINTER_ROLE) nonReentrant returns(uint256) {
require(random == false, "Variant only available as random mint");
require(getRemainingSupplyOfTokenType(1) > 0);
uint256 mintedToken = doMint(_to, totalSupply(), 1);
return mintedToken;
}
I am getting this error from my autotask:
cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (error={"code":-32000}, method="call", transaction={"from":"0xC9A08E1a1014aBbA1E2d0577788DC1583332F0AD","to":"0xbBB6E97CA58B081f0A70Ad30A313A833536DA759","data":"0xbf5d3bdb000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000003e2f11e9a522e7ae74e38a01776c9cf2bf7fc55c000000000000000000000000d5068cca0f4c3761d88ca773eef59b84e0d14b63000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000246a6278420000000000000000000000003e2f11e9a522e7ae74e38a01776c9cf2bf7fc55c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414b567ea5af22fca5b0fc78c9dd9c7fe0f83f546465ca81b0b39a2e194f9b8d146e7c81b98c41da087aa7bd3b480a72434e03dccf1653d6308a33261a3a27dff91c00000000000000000000000000000000000000000000000000000000000000","accessList":null}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.5.2)
This is the code from my autotask:
const ethers = require("ethers");
const {
DefenderRelaySigner,
DefenderRelayProvider,
} = require("defender-relay-client/lib/ethers");
const speed = "fastest";
const ForwarderAbi = [
{ inputs: [], stateMutability: "nonpayable", type: "constructor" },
{
inputs: [
{
components: [
{ internalType: "address", name: "from", type: "address" },
{ internalType: "address", name: "to", type: "address" },
{ internalType: "uint256", name: "value", type: "uint256" },
{ internalType: "uint256", name: "gas", type: "uint256" },
{ internalType: "uint256", name: "nonce", type: "uint256" },
{ internalType: "bytes", name: "data", type: "bytes" },
],
internalType: "struct MinimalForwarder.ForwardRequest",
name: "req",
type: "tuple",
},
{ internalType: "bytes", name: "signature", type: "bytes" },
],
name: "execute",
outputs: [
{ internalType: "bool", name: "", type: "bool" },
{ internalType: "bytes", name: "", type: "bytes" },
],
stateMutability: "payable",
type: "function",
},
{
inputs: [{ internalType: "address", name: "from", type: "address" }],
name: "getNonce",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
components: [
{ internalType: "address", name: "from", type: "address" },
{ internalType: "address", name: "to", type: "address" },
{ internalType: "uint256", name: "value", type: "uint256" },
{ internalType: "uint256", name: "gas", type: "uint256" },
{ internalType: "uint256", name: "nonce", type: "uint256" },
{ internalType: "bytes", name: "data", type: "bytes" },
],
internalType: "struct MinimalForwarder.ForwardRequest",
name: "req",
type: "tuple",
},
{ internalType: "bytes", name: "signature", type: "bytes" },
],
name: "verify",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
stateMutability: "view",
type: "function",
},
];
const erc20PermitAbi = [
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
{
internalType: "address",
name: "spender",
type: "address",
},
{
internalType: "uint256",
name: "value",
type: "uint256",
},
{
internalType: "uint256",
name: "deadline",
type: "uint256",
},
{
internalType: "uint8",
name: "v",
type: "uint8",
},
{
internalType: "bytes32",
name: "r",
type: "bytes32",
},
{
internalType: "bytes32",
name: "s",
type: "bytes32",
},
],
name: "permit",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
];
async function relayGeneric(forwarder, request, signature) {
// Validate request on the forwarder contract
const valid = await forwarder.verify(request, signature);
if (!valid) throw new Error(`Invalid request`);
// Send meta-tx through relayer to the forwarder contract
const gasLimit = (parseInt(request.gas) + 50000).toString();
return await forwarder.execute(request, signature, { gasLimit });
}
async function relayTokenApproval(
permitContract,
permitMessage,
permitSignature
) {
// Tx args
const { owner, spender, value, deadline, v, r, s } = permitMessage;
// Send meta-tx through relayer to the forwarder contract
return await permitContract.permit(owner, spender, value, deadline, v, r, s);
}
async function handler(event) {
// Parse webhook payload
if (!event.request || !event.request.body) throw new Error(`Missing payload`);
console.log(event.request.body);
const { type } = event.request.body;
console.log("Type", type);
// Initialize Relayer provider and signer, and forwarder contract
const credentials = { ...event };
const provider = new DefenderRelayProvider(credentials);
const signer = new DefenderRelaySigner(credentials, provider, {
speed,
});
let tx;
if (type == "permit") {
// ERC20 Permit
const { request, signature } = event.request.body;
// Initialize permitContract
const permitContract = new ethers.Contract(
request.to,
erc20PermitAbi,
signer
);
tx = await relayTokenApproval(permitContract, request, signature);
} else if (type == "forward") {
// Gasless tx
const { request, signature, forwarderAddress } = event.request.body;
console.log(forwarderAddress);
// Initialize forwarder contract
const forwarder = new ethers.Contract(
forwarderAddress,
ForwarderAbi,
signer
);
console.log(`Relaying`, request);
console.log(`Signature`, signature);
// fix ledger live where signature result in v = 0, 1.
const fixedSig = ethers.utils.joinSignature(ethers.utils.splitSignature(signature));
console.log(`Fixed Signature`, fixedSig);
tx = await relayGeneric(forwarder, request, fixedSig);
console.log(tx);
} else {
throw new Error(
`Invalid gasless transaction type. Provide type 'permit' or 'forwarder'.`
);
}
console.log(`Sent meta-tx: ${tx.hash}`);
return { txHash: tx.hash, txResponse: tx };
}
module.exports = {
handler,
};
From my understanding, the transaction that is failing is the one that goes from the Relayer to the Forwarder. What is it that I am doing wrong?
I have checked that all roles are assigned correctly for the function to work, and all require
statements are met.