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