Esta página foi traduzida automaticamente. O original em inglês é a versão canônica. Ler em inglês →
Pular para o conteúdo principal

Autenticação e Assinatura

Requisitos de assinatura EIP-712 para operações de escrita.

Domínio de assinatura de produção

Use o chain ID 999 para produção. A testnet está temporariamente desativada até que a Hypercall adquira mais HYPE de testnet.

Visão Geral​

Todas as operações de escrita exigem assinaturas de dados tipados EIP-712. O signatário deve:

  1. Ser o próprio endereço da carteira (assinatura direta), OU
  2. Ser um agente autorizado para a carteira (veja Autenticação de agente)

Domínio EIP-712​

Domain separator:

{
"name": "Hypercall",
"version": "1",
"chainId": 999,
"verifyingContract": "0x0000000000000000000000000000000000000000"
}

Chain ID: 999 (produção)

Tipos de Mensagem​

PlaceOrder​

Struct com route explícito:

struct PlaceOrder {
address wallet;
string symbol;
string side;
string size;
string price;
string tif;
string route;
string clientId;
uint64 nonce;
}

Struct quando o route é omitido:

struct PlaceOrder {
address wallet;
string symbol;
string side;
string size;
string price;
string tif;
string clientId;
uint64 nonce;
}

Requisitos dos campos:

  • wallet: Endereço da carteira (string hex com prefixo 0x)
  • symbol: Símbolo da opção (por exemplo, "BTC-20250131-100000-C")
  • side: "Buy" ou "Sell" (correspondência exata da string obrigatória)
  • size: Tamanho como string (por exemplo, "0.1") - DEVE corresponder exatamente ao que é enviado no JSON
  • price: Preço como string (por exemplo, "100.0") - DEVE corresponder exatamente ao que é enviado no JSON
  • tif: Time-in-force: "gtc", "ioc" ou "fok" (correspondência exata da string obrigatória)
  • route: Rota de ordem opcional: "best_execution", "book_only" ou "rfq_only" (correspondência exata da string obrigatória quando presente)
  • clientId: ID da ordem fornecido pelo cliente (string, pode ser vazio "")
  • nonce: Nonce único (uint64) para proteção contra replay

Crítico: price e size DEVEM ser strings tanto na mensagem assinada quanto no corpo da requisição JSON. A formatação da string deve corresponder exatamente.

Padrão de route: route é opcional até pelo menos 4 de julho de 2026. Quando omitido do JSON, omita-o também dos dados tipados. O servidor trata route omitido como best_execution. POST /order retorna headers de deprecação quando o route é omitido. Os clientes devem enviar route; ele não se tornará obrigatório antes de 4 de julho de 2026, mas pode se tornar obrigatório depois.

CancelOrder​

Struct:

struct CancelOrder {
address wallet;
string orderId;
uint64 nonce;
}

Requisitos dos campos:

  • wallet: Endereço da carteira
  • orderId: ID da ordem como string (por exemplo, "123")
  • nonce: Nonce único

CancelOrderByClientId​

Struct:

struct CancelOrderByClientId {
address wallet;
string clientId;
uint64 nonce;
}

Requisitos dos campos:

  • wallet: Endereço da carteira
  • clientId: ID da ordem do cliente (string)
  • nonce: Nonce único

ApproveAgent​

Struct:

struct ApproveAgent {
address agent;
uint64 nonce;
}

Requisitos dos campos:

  • agent: Endereço da carteira do agente a ser autorizado
  • nonce: Nonce único

Nota: A carteira que autoriza o agente é derivada da assinatura recuperada.

RevokeAgent​

Struct:

struct RevokeAgent {
address agent;
uint64 nonce;
}

Requisitos dos campos:

  • agent: Endereço da carteira do agente a ser revogado
  • nonce: Nonce único

Processo de Assinatura​

Passo 1: Construir a Mensagem​

Exemplo para PlaceOrder com route explícito:

const message = {
wallet: "0x1111111111111111111111111111111111111111",
symbol: "BTC-20250131-100000-C",
side: "Buy",
size: "0.1", // MUST be string
price: "100.0", // MUST be string
tif: "gtc",
route: "best_execution",
clientId: "mm-1",
nonce: 123
};

Passo 2: Definir o Domínio​

const domain = {
name: "Hypercall",
version: "1",
chainId: 999,
verifyingContract: "0x0000000000000000000000000000000000000000"
};

Passo 3: Assinar os Dados Tipados​

Usando ethers.js:

const signature = await signer._signTypedData(domain, {
PlaceOrder: [
{ name: "wallet", type: "address" },
{ name: "symbol", type: "string" },
{ name: "side", type: "string" },
{ name: "size", type: "string" },
{ name: "price", type: "string" },
{ name: "tif", type: "string" },
{ name: "route", type: "string" },
{ name: "clientId", type: "string" },
{ name: "nonce", type: "uint64" }
]
}, message);

Passo 4: Enviar a Requisição​

Crítico: O corpo da requisição JSON deve usar exatamente os mesmos valores de string para price e size:

{
"wallet": "0x1111111111111111111111111111111111111111",
"symbol": "BTC-20250131-100000-C",
"price": "100.0", // Same string as signed
"size": "0.1", // Same string as signed
"side": "Buy",
"tif": "gtc",
"route": "best_execution",
"client_id": "mm-1",
"nonce": 123,
"signature": "0x..."
}

Gerenciamento de Nonce​

Requisitos:

  • Os nonces DEVEM ser únicos por carteira
  • Os nonces DEVEM ser incrementais (previne ataques de replay)
  • Os nonces não são validados quanto à monotonicidade estrita (lacunas são permitidas)

Boas práticas:

  • Use um contador persistente por carteira
  • Incremente após cada assinatura bem-sucedida
  • Trate lacunas de nonce de forma adequada (por exemplo, se uma requisição falhar, tente novamente com o mesmo nonce)

Autorização de Agentes​

Se estiver usando uma carteira de agente para assinar:

  1. Aprovar o agente (uma única vez):

    POST /approve-agent
    {
    "agent": "0x...",
    "nonce": 1,
    "signature": "0x..." # Signed by wallet owner
    }
  2. Assinar ordens com a carteira do agente:

    • Use a carteira do agente para assinar mensagens PlaceOrder / CancelOrder
    • Defina o campo wallet com o endereço da carteira de trading
    • O middleware verifica se o agente está autorizado para essa carteira

Veja Autenticação de agente para detalhes completos sobre autorização de agentes.

Ordens de Perpétuos (Compatível com Hyperliquid)​

Ordens de contratos perpétuos usam um domínio EIP-712 e um formato de mensagem diferentes (compatibilidade com Hyperliquid Core):

Domínio:

{
"name": "Exchange",
"version": "1",
"chainId": 1337,
"verifyingContract": "0x0000000000000000000000000000000000000000"
}

Mensagem: struct Agent com dados da ordem codificados em MessagePack.

Consulte o guia atual de codificação de assinaturas para os campos exatos.

Erros Comuns​

"Signature verification failed"​

Causas:

  1. price ou size enviados como número em vez de string
  2. Formatação da string alterada entre a assinatura e o envio (por exemplo, "100.0" vs "100")
  3. Nonce incorreto
  4. Domínio incorreto (chain ID, name, version)
  5. Agente não autorizado para a carteira

"Unauthorized: signer not authorized for wallet"​

Causa: O signatário não é a própria carteira e não é um agente autorizado.

Solução: Aprove o agente via POST /approve-agent ou assine diretamente com a carteira.

Exemplos de Implementação​

Python (eth_account)​

from eth_account import Account
from eth_account.messages import encode_structured_data

domain = {
"name": "Hypercall",
"version": "1",
"chainId": 999,
"verifyingContract": "0x0000000000000000000000000000000000000000"
}

message = {
"wallet": "0x1111111111111111111111111111111111111111",
"symbol": "BTC-20250131-100000-C",
"side": "Buy",
"size": "0.1",
"price": "100.0",
"tif": "gtc",
"route": "best_execution",
"clientId": "mm-1",
"nonce": 123
}

types = {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"PlaceOrder": [
{"name": "wallet", "type": "address"},
{"name": "symbol", "type": "string"},
{"name": "side", "type": "string"},
{"name": "size", "type": "string"},
{"name": "price", "type": "string"},
{"name": "tif", "type": "string"},
{"name": "route", "type": "string"},
{"name": "clientId", "type": "string"},
{"name": "nonce", "type": "uint64"}
]
}

structured_msg = {
"types": types,
"domain": domain,
"primaryType": "PlaceOrder",
"message": message
}

encoded = encode_structured_data(structured_msg)
signed = Account.sign_message(encoded, private_key)
signature = signed.signature.hex()

JavaScript (ethers.js)​

const { ethers } = require("ethers");

const domain = {
name: "Hypercall",
version: "1",
chainId: 999,
verifyingContract: "0x0000000000000000000000000000000000000000"
};

const types = {
PlaceOrder: [
{ name: "wallet", type: "address" },
{ name: "symbol", type: "string" },
{ name: "side", type: "string" },
{ name: "size", type: "string" },
{ name: "price", type: "string" },
{ name: "tif", type: "string" },
{ name: "route", type: "string" },
{ name: "clientId", type: "string" },
{ name: "nonce", type: "uint64" }
]
};

const message = {
wallet: "0x1111111111111111111111111111111111111111",
symbol: "BTC-20250131-100000-C",
side: "Buy",
size: "0.1",
price: "100.0",
tif: "gtc",
route: "best_execution",
clientId: "mm-1",
nonce: 123
};

const signature = await signer._signTypedData(domain, types, message);

Testando Assinaturas​

Exemplo para testar a geração de assinaturas:

  1. Gere a assinatura com a sua implementação
  2. Envie uma ordem de teste via POST /order
  3. Verifique a resposta: status="ACKED" ou status="REJECTED" com o motivo
  4. Se receber "signature_verification_failed", verifique:
    • Formatação das strings de price e size
    • Parâmetros do domínio (especialmente chainId)
    • Correção do nonce

Considerações de Segurança​

  1. Nunca exponha chaves privadas: Use hardware wallets ou gerenciamento seguro de chaves
  2. Gerenciamento de nonce: Use nonces persistentes e incrementais por carteira
  3. Autorização de agentes: Audite regularmente os agentes autorizados via GET /authorized-agents
  4. Codificação de strings: Garanta que price e size sejam strings tanto na assinatura quanto na requisição

Referências​