mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
## 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>
101 lines
2.8 KiB
Markdown
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));
|
|
}
|
|
);
|
|
```
|