thirdweb-dev / ozdefender-autotask

17 stars 7 forks source link

OpenZeppelin can sign free gasless but can not create NFT and setting EDITIONCOLEECTION. #4

Open legiarung opened 2 years ago

legiarung commented 2 years ago

I use thirdweb and settup gasless below:

Screen Shot 2022-09-27 at 11 35 11 AM

All is fine except for the code I've highlighted here.

Screen Shot 2022-09-27 at 11 34 21 AM

This is the error received:

Screen Shot 2022-09-27 at 11 41 57 AM

Openzeppelin says success but there is an error here

Screen Shot 2022-09-27 at 11 44 33 AM

Transaction:

https://rinkeby.etherscan.io/tx/0x159df9d9e2e30d8a3487c642eadac7870c02f13e70351e4a866c8559b9c4a7e5

Here is code settup Openzeppelin:

const {
  DefenderRelaySigner,
  DefenderRelayProvider,
} = require("defender-relay-client/lib/ethers");

/**
 *  NOTE FOR GOERLI: replace `ForwarderAddress` with value "0x5001A14CA6163143316a7C614e30e6041033Ac20"
*/
const ForwarderAddress = "0xc82BbE41f2cF04e3a8efA18F7032BDD7f6d98a81";
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`);
  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 } = event.request.body;

    // 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);
  } 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,
};
legiarung commented 2 years ago

@jarrodwatts You can help me check it . Please

haunguyenuit commented 2 years ago

I have same problem when mint NFT with gasless transaction mode.

https://goerli.etherscan.io/tx/0xf7d9ff35a8283bca22a4cd4a598722ec16b38f03ca8c69544a899a83871d4ab4

Screen Shot 2022-10-10 at 14 05 33

Fail with error 'AccessControl: account 0x564c725551444238562f30000000000000000000 is missing role 0x9f2df0fed2c7764... I think the error is coming from function _msgSender() return invalid address

Screen Shot 2022-10-10 at 14 13 12

@nkrishang @kumaryash90 Can you check that issue?

haunguyenuit commented 2 years ago

It seems to be related to function multicall when mint many NFTs

Screen Shot 2022-10-10 at 15 22 43
kumaryash90 commented 2 years ago

@haunguyenuit can you share your relayer autotask code?

@legiarung looks like the relayer code is outdated. can you check the latest one, see if it works: https://github.com/thirdweb-dev/ozdefender-autotask/blob/main/src/forwarder_handler.js https://blog.thirdweb.com/guides/setup-gasless-transactions/

haunguyenuit commented 2 years ago

@kumaryash90 here is autotask code:

`const ethers = require("ethers"); const { DefenderRelaySigner, DefenderRelayProvider, } = require("defender-relay-client/lib/ethers");

/**

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); 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 } = event.request.body;

// 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);

} 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, };`

kumaryash90 commented 2 years ago

thanks @haunguyenuit . You should update this script. You can find the latest one here: https://raw.githubusercontent.com/thirdweb-dev/ozdefender-autotask/main/src/forwarder_handler.js

haunguyenuit commented 2 years ago

ok, thanks @kumaryash90

haunguyenuit commented 2 years ago

@kumaryash90 The error still occurs. I think the problem is coming from smart contract, I can still mint a NFT, the error only appears when I try to mint many NFTs with function "mintBatch"

kumaryash90 commented 2 years ago

@haunguyenuit In the transaction link you shared above, what contract is this: 0x4ee0bf666f4e47e41d2a5e9876e76ac983fb5360, that you're sending the transactions to?

haunguyenuit commented 2 years ago

@kumaryash90 It's the forwarder. It is similar to this smart contract: https://goerli.etherscan.io/address/0x5001a14ca6163143316a7c614e30e6041033ac20#code

legiarung commented 2 years ago

@haunguyenuit can you share your relayer autotask code?

@legiarung looks like the relayer code is outdated. can you check the latest one, see if it works: https://github.com/thirdweb-dev/ozdefender-autotask/blob/main/src/forwarder_handler.js https://blog.thirdweb.com/guides/setup-gasless-transactions/

Ohh Noo, I tried it, but it still can't generate NFT and setting claim of editioncoleection