Ir para o conteúdo

Interagindo com o Call Permit Precompile

Introdução

O Call Permit Precompile permite que um usuário assine um permit EIP-712 para qualquer chamada EVM, que pode ser despachada por qualquer pessoa ou contrato inteligente. O despachante paga as taxas, viabilizando transações “gasless” para o signatário (desde que a chamada não envie valor).

O Call Permit Precompile está localizado no seguinte endereço:

0x0000000000000000000000000000000000000802

Note

O uso de precompiladas pode trazer consequências inesperadas. As precompiladas do Tanssi são derivadas das do Moonbeam; portanto, familiarize-se com as considerações de segurança das precompiladas do Moonbeam.

Interface Solidity

CallPermit.sol expõe três funções principais:

CallPermit.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;

/// @dev The CallPermit contract's address.
address constant CALL_PERMIT_ADDRESS = 0x0000000000000000000000000000000000000802;

/// @dev The CallPermit contract's instance.
CallPermit constant CALL_PERMIT_CONTRACT = CallPermit(CALL_PERMIT_ADDRESS);

/// @author The Moonbeam Team
/// @title Call Permit Interface
/// @dev The interface aims to be a general-purpose tool to perform gas-less transactions. It uses the EIP-712 standard,
/// and signed messages can be dispatched by another network participant with a transaction
/// @custom:address 0x0000000000000000000000000000000000000802
interface CallPermit {
    /// @dev Dispatch a call on the behalf of an other user with a EIP712 permit.
    /// Will revert if the permit is not valid or if the dispatched call reverts or errors (such as
    /// out of gas).
    /// If successful the EIP712 nonce is increased to prevent this permit to be replayed.
    /// @param from Who made the permit and want its call to be dispatched on their behalf.
    /// @param to Which address the call is made to.
    /// @param value Value being transferred from the "from" account.
    /// @param data Call data
    /// @param gaslimit Gaslimit the dispatched call requires.
    ///     Providing it prevents the dispatcher to manipulate the gaslimit.
    /// @param deadline Deadline in UNIX seconds after which the permit will no longer be valid.
    /// @param v V part of the signature.
    /// @param r R part of the signature.
    /// @param s S part of the signature.
    /// @return output Output of the call.
    /// @custom:selector b5ea0966
    function dispatch(
        address from,
        address to,
        uint256 value,
        bytes memory data,
        uint64 gaslimit,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (bytes memory output);

    /// @dev Returns the current nonce for given owner.
    /// A permit must have this nonce to be consumed, which will
    /// increase the nonce by one.
    /// @custom:selector 7ecebe00
    function nonces(address owner) external view returns (uint256);

    /// @dev Returns the EIP712 domain separator. It is used to avoid replay
    /// attacks across assets or other similar EIP712 message structures.
    /// @custom:selector 3644e515
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
  • dispatch(from, to, value, data, gasLimit, deadline, v, r, s) — executa a chamada assinada; falha se o permit for inválido ou se a chamada revertir. Aumenta o nonce do signatário após sucesso.
  • nonces(owner) — retorna o nonce atual de owner.
  • DOMAIN_SEPARATOR() — retorna o separador de domínio EIP-712 usado para evitar replay.

DOMAIN_SEPARATOR e assinatura

O separador segue o EIP-712 com name = "Call Permit Precompile", version = "1", chainId da rede e o endereço do precompile como verifyingContract. A mensagem assinada inclui from, to, value, data, gasLimit, deadline e o nonce atual.

Preparar contratos de exemplo

Use o contrato SetMessage.sol para demonstrar:

// 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;
    }
}

No Remix:

  1. Crie arquivos CallPermit.sol e SetMessage.sol.
  2. Compile ambos.
  3. Implemente SetMessage.sol normalmente.
  4. Acesse o Call Permit Precompile clicando em At Address e informando 0x0000000000000000000000000000000000000802.

Gerar a assinatura

Você pode gerar a assinatura com Ethers.js usando EIP-712:

import { ethers } from 'ethers';

const from = 'INSERT_FROM_ADDRESS';
const to = 'INSERT_TO_ADDRESS';
const value = 0;
const data =
  '0x4ed3885e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000';
const gaslimit = 100000;
const nonce = 'INSERT_SIGNERS_NONCE';
const deadline = 'INSERT_DEADLINE';

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

  const typedData = {
    types: {
      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: INSERT-CHAIN-ID,
      verifyingContract: '0x0000000000000000000000000000000000000802',
    },
    message: message,
  };

  return {
    typedData,
    message,
  };
};

const messageData = createPermitMessageData();

// For demo purposes only. Never store your private key in a JavaScript/TypeScript file
const privateKey = 'INSERT_PRIVATE_KEY';
const wallet = new ethers.Wallet(privateKey);

const signature = await wallet.signTypedData(messageData.typedData.domain, messageData.typedData.types, messageData.message);

console.log(`Transaction successful with hash: ${signature}`);

const ethersSignature = ethers.Signature.from(signature);
const formattedSignature = {
  r: ethersSignature.r,
  s: ethersSignature.s,
  v: ethersSignature.v,
};

console.log(formattedSignature);

Preencha:

  • from: endereço que autoriza.
  • to: endereço do contrato alvo.
  • value: normalmente 0 (a menos que envie valor).
  • data: dados da chamada (ABI-encoded).
  • gasLimit: limite de gás desejado para a chamada.
  • deadline: timestamp UNIX em que o permit expira.
  • chainId: ID da rede.
  • nonce: obtido via nonces(from) no precompile.

Execute node getSignature.js e copie v, r, s para usar na chamada dispatch.

Remember

Guarde chaves privadas em segurança; o exemplo destina-se apenas a testes.

Despachar a chamada

  1. No Remix, expanda o contrato Call Permit.
  2. Preencha os mesmos argumentos usados para assinar (from, to, value, data, gasLimit, deadline, v, r, s).
  3. Clique em transact e confirme no MetaMask.
  4. Verifique o efeito no contrato (SetMessage.sol > get deve retornar a nova mensagem).

Parabéns! Você assinou e despachou uma chamada com o Call Permit Precompile.

As informações apresentadas aqui foram fornecidas por terceiros e estão disponíveis apenas para fins informativos gerais. A Tanssi não endossa nenhum projeto listado e descrito no Site de Documentação da Tanssi (https://docs.tanssi.network/). A Tanssi Foundation não garante a precisão, integridade ou utilidade dessas informações. Qualquer confiança depositada nelas é de sua exclusiva responsabilidade. A Tanssi Foundation se exime de toda responsabilidade decorrente de qualquer confiança que você ou qualquer outra pessoa possa ter em qualquer parte deste conteúdo. Todas as declarações e/ou opiniões expressas nesses materiais são de responsabilidade exclusiva da pessoa ou entidade que as fornece e não representam necessariamente a opinião da Tanssi Foundation. As informações aqui não devem ser interpretadas como aconselhamento profissional ou financeiro de qualquer tipo. Sempre busque orientação de um profissional devidamente qualificado em relação a qualquer assunto ou circunstância em particular. As informações aqui podem conter links ou integração com outros sites operados ou conteúdo fornecido por terceiros, e tais sites podem apontar para este site. A Tanssi Foundation não tem controle sobre esses sites ou seu conteúdo e não terá responsabilidade decorrente ou relacionada a eles. A existência de qualquer link não constitui endosso desses sites, de seu conteúdo ou de seus operadores. Esses links são fornecidos apenas para sua conveniência, e você isenta e exonera a Tanssi Foundation de qualquer responsabilidade decorrente do uso dessas informações ou das informações fornecidas por qualquer site ou serviço de terceiros.
Última atualização: 9 de dezembro de 2025
| Criada: 9 de dezembro de 2025