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