Back to Datahaven

Call Permit Precompile

operator/precompiles/call-permit/README.md

0.30.02.8 KB
Original Source

Call Permit Precompile

This precompile aims to be a general-purpose tool to perform gas-less transactions.

It allows a user (we'll call her Alice) to sign a call permit with MetaMask (using the EIP712 standard), which can then be dispatched by another user (we'll call him Bob) with a transaction.

Bob can make a transaction to the Call Permit Precompile with the call data and Alice's signature. If the permit and signature are valid, the precompile will perform the call on the behalf of Alice, as if Alice made a transaction herself. Bob is thus paying the transaction fees and Alice can perform a call without having any native currency to pay for fees (she'll still need to have some if the call includes a transfer).

How to sign the permit

The following code is an example that is working in a Metamask-injected webpage. Bob then need to make a transaction towards the precompile address with the same data and Alice's signature.

js
await window.ethereum.enable();
const accounts = await window.ethereum.request({
  method: "eth_requestAccounts",
});

const from = accounts[0];
const to = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
const value = 42;
const data = "0xdeadbeef";
const gaslimit = 100000;
const nonce = 0;
const deadline = 1000;

const createPermitMessageData = function () {
  const message = {
    from: from,
    to: to,
    value: value,
    data: data,
    gaslimit: gaslimit,
    nonce: nonce,
    deadline: deadline,
  };

  const typedData = JSON.stringify({
    types: {
      EIP712Domain: [
        { name: "name", type: "string" },
        { name: "version", type: "string" },
        { name: "chainId", type: "uint256" },
        { name: "verifyingContract", type: "address" },
      ],
      CallPermit: [
        { name: "from", type: "address" },
        { name: "to", type: "address" },
        { name: "value", type: "uint256" },
        { name: "data", type: "bytes" },
        { name: "gaslimit", type: "uint64" },
        { name: "nonce", type: "uint256" },
        { name: "deadline", type: "uint256" },
      ],
    },
    primaryType: "CallPermit",
    domain: {
      name: "Call Permit Precompile",
      version: "1",
      chainId: 0,
      verifyingContract: "0x000000000000000000000000000000000000080a",
    },
    message: message,
  });

  return {
    typedData,
    message,
  };
};

const method = "eth_signTypedData_v4";
const messageData = createPermitMessageData();
const params = [from, messageData.typedData];

web3.currentProvider.sendAsync(
  {
    method,
    params,
    from,
  },
  function (err, result) {
    if (err) return console.dir(err);
    if (result.error) {
      alert(result.error.message);
      return console.error("ERROR", result);
    }
    console.log("Signature:" + JSON.stringify(result.result));
  }
);