Interacting with the Call Permit Precompile
Introduction
The Call Permit Precompile on Tangle allows a user to sign a permit, an EIP-712 (opens in a new tab) signed message, for any EVM call. It can then be dispatched by anyone or any smart contract. The user who signed the permit is effectively “authorizing” another account or contract to execute the call on their behalf. This enables gas-less transactions because the dispatcher pays for fees on behalf of the signer.
For example, Alice signs a call permit and Bob dispatches it. Bob pays for the transaction fees, so Alice does not need to hold any native tokens to cover gas. However, keep in mind that if the call includes a token transfer, the signer must have a sufficient balance of that token.
Precompile Address
On Tangle, the Call Permit Precompile is located at the well-known address 0x0000000000000000000000000000000000000805
. Below are the addresses you can use depending on the network:
- Tangle Mainnet:
0x0000000000000000000000000000000000000805
- Tangle Testnet:
0x0000000000000000000000000000000000000805
The Call Permit Solidity Interface
Below is the recommended Solidity interface for interacting with the Call Permit Precompile on Tangle. Note that it follows the EIP-712 standard and can be used to dispatch gas-less transactions.
When dispatch
is called, the precompile checks the signed permit and the current nonce of the signer. If the permit is valid, the call is executed as if the signer itself had made the transaction. After a successful dispatch
, the signer’s nonce is incremented automatically.
Setup the Example
This section guides you through a simple usage example. You will:
- Deploy a sample contract,
SetMessage.sol
. - Generate and sign the permit using one account (for example, Alice).
- Dispatch the call using another account (for example, Bob).
Prerequisites
To follow this demonstration, you should:
- Have MetaMask installed (opens in a new tab) in your browser.
- Connect MetaMask to Tangle Testnet (or Tangle Mainnet, if you prefer).
- Have at least two accounts on Tangle, one funded for paying fees (Bob) and one to act as the signer (Alice).
Example Contract (SetMessage.sol)
Here is a simple contract used to illustrate the call permit process. It stores a string message:
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.7;
contract SetMessage {
string storedMessage;
function set(string calldata x) public {
storedMessage = x;
}
function get() public view returns (string memory) {
return storedMessage;
}
}
Remix Setup
A common way to work with the Tangle Precompile Registry is via Remix (opens in a new tab). You can deploy the sample contract and interact with the Call Permit Precompile. Steps:
- Open Remix and enable the "File explorers".
- Create a file named
SetMessage.sol
and paste the code above. - Also create a file named
CallPermit.sol
(or any name you choose) and paste the interface from this documentation (if needed for reference). - Compile both files by selecting each and pressing the "Compile" button.
Deploying the Example Contract
- In Remix, go to the “Deploy & run transactions” panel.
- Select “Injected Web3” or “Injected Provider - Metamask” from the Environment dropdown (ensuring your MetaMask is connected to Tangle).
- Deploy
SetMessage.sol
. Confirm the transaction in MetaMask. - You should see the deployed contract under “Deployed Contracts”.
Record or copy the newly deployed SetMessage
contract address; you will need it when forming the permit data.
Accessing the Call Permit Precompile
Since the Call Permit contract is precompiled and already deployed, you do not deploy it yourself. Instead, you point Remix to the address:
- Go to the “Deploy & run transactions” panel in Remix.
- Leave the Environment set to “Injected Provider - Metamask”.
- Next to "At Address", paste the well-known precompile address:
0x0000000000000000000000000000000000000805
- Click “At Address” to tell Remix to load the Call Permit contract interface at that address.
- Remix adds the Call Permit Precompile contract to your “Deployed Contracts” list.
Generate Call Permit Signature
To dispatch a call permit, one must first sign the EIP-712 message that includes:
- The signer’s address (
from
) - The contract you want to call (
to
) - The
value
(in wei/fungible tokens) - The
data
you want to send, including function signatures and arguments - A
gaslimit
to ensure the dispatcher doesn’t choose excessive gas - The
deadline
for expiration - The signer’s
nonce
from the Call Permit Precompile
Determining the Signer’s Nonce
In Remix, expand the CallPermit precompile entry under “Deployed Contracts”; then input the signer’s address into the nonces
function and press the button to read the current nonce.
Example Data
For SetMessage.sol
, suppose you want to set the message to "hello world". The contract’s function signature for set(string)
is:
- 4-byte function selector for
set(string)
- Encoded string argument
In hex, the payload can look like this:
0x4ed3885e
0000000000000000000000000000000000000000000000000000000000000020
000000000000000000000000000000000000000000000000000000000000000b
68656c6c6f20776f726c64000000000000000000000000000000000000000000
We recommend a gas limit of around 100000 for this example.
Signing the Permit in the Browser
You can sign the permit in multiple ways. Below is an example using JSFiddle (opens in a new tab) and the MetaMask provider directly:
- In JSFiddle (or any similar environment), add Ethers.js as a resource.
- Use this snippet (simplified example):
// Example snippet to sign the data via MetaMask in the browser
// IMPORTANT: This is a simplified code snippet for demonstration only.
async function main() {
// Request accounts from MetaMask
const accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
const from = accounts[0];
// Replace these as appropriate
const to = "0x1234567890123456789012345678901234567890";
const value = 0; // Setting to 0 for this example
const data = "0x4ed3885e..." // (truncated) your data from above
const gaslimit = 100000;
const nonce = 0; // The first time you do this, it might be 0
const deadline = Math.floor(Date.now() / 1000) + 600; // 10 mins from "now"
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: 3799, // Tangle Testnet
verifyingContract: "0x0000000000000000000000000000000000000805",
},
message: {
from,
to,
value,
data,
gaslimit,
nonce,
deadline,
},
});
// Request the user to sign typed data
const signature = await window.ethereum.request({
method: "eth_signTypedData_v4",
params: [from, typedData],
});
console.log("Signature:", signature);
}
main().catch(console.error);
- Run the snippet. MetaMask prompts you to sign. Approve the message, and you should see the signature in your console (it should look like a hex string, typically “0x” followed by 64 bytes plus the “v” byte).
You can decode the signature into v
, r
, s
fields using Ethers.js (opens in a new tab). You’ll need these fields to call dispatch
.
Signing the Permit in Node.js
Alternatively, you can use the MetaMask @metamask/eth-sig-util
(opens in a new tab) package with a private key in Node.js. Doing so requires you to be mindful about key storage. Once you have the signature, the process is the same: you break it down into v
, r
, and s
.
Interact with the Precompile
Once you have the call permit signature, you can test dispatch
on Tangle.
Dispatch a Call
- In Remix, switch to the account that will pay fees (Bob).
- Expand the Call Permit Precompile contract under “Deployed Contracts.”
- Find and expand the
dispatch
function. - Fill in the fields with the same
from
,to
,value
,data
,gaslimit
, anddeadline
that you used for the signature. - Paste in
v
,r
, ands
. - Click “transact.”
If your permit is valid and everything matches, the transaction should succeed. The call is effectively executed as if “Alice” had done it, while “Bob” pays the fees.
Verify the Result
Return to the SetMessage
contract you deployed. Call its get
function to see if the stored message was updated to “hello world”. If so, congratulations! You have successfully dispatched a gas-less transaction on Tangle using the Call Permit Precompile.