datahaven/operator/precompiles/call-permit/README.md
Steve Degosserie 479af2e192
feat: Add Moonbeam CallPermit precompile (#140)
## Summary

This PR adds Moonbeam's CallPermit precompile to DataHaven, enabling
gasless meta-transactions through EIP-712 signature-based permissions.
Users can sign transaction permits offline, allowing relayers to execute
transactions on their behalf while maintaining full security and
authentication.

## Key Features

### CallPermit Precompile Functions
- **`dispatch(address from, address to, uint256 value, bytes data,
uint64[] gasLimit, uint256 deadline, uint8 v, bytes32 r, bytes32 s)`**:
Execute permitted calls with signature verification
- **`nonces(address owner)`**: Get current nonce for permit validation

### Technical Implementation
- **Address**: `0x080A` (2058 in decimal)
- **EIP-712 Compliance**: Structured signature validation with proper
domain separation
- **Nonce Management**: Per-user nonce tracking for replay protection
- **Deadline Validation**: Time-bound permits for enhanced security
- **Gas Forwarding**: Proper gas limit enforcement and forwarding

Depends on https://github.com/datahaven-xyz/datahaven/pull/137

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-07 15:00:37 +02:00

101 lines
2.8 KiB
Markdown

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