Diese Seite wurde maschinell übersetzt. Das englische Original ist maßgeblich. Auf Englisch lesen
Zum Hauptinhalt springen

Authentifizierung & Signierung

EIP-712-Signaturanforderungen für Schreiboperationen.

Produktions-Signaturdomäne

Verwenden Sie die Chain-ID 999 für die Produktion. Das Testnet ist vorübergehend deaktiviert, bis Hypercall mehr Testnet-HYPE erwirbt.

Überblick

Alle Schreiboperationen erfordern typisierte EIP-712-Datensignaturen. Der Signierer muss entweder:

  1. die Wallet-Adresse selbst sein (direkte Signierung), ODER
  2. ein autorisierter Agent für die Wallet sein (siehe Agent-Autorisierung)

EIP-712-Domain

Domain Separator:

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

Chain-ID: 999 (Produktion)

Nachrichtentypen

PlaceOrder

Struct mit expliziter Route:

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

Struct, wenn die Route weggelassen wird:

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

Feldanforderungen:

  • wallet: Wallet-Adresse (Hex-String mit 0x-Präfix)
  • symbol: Options-Symbol (z. B. "BTC-20250131-100000-C")
  • side: "Buy" oder "Sell" (exakte Zeichenkettenübereinstimmung erforderlich)
  • size: Größe als String (z. B. "0.1") – MUSS exakt mit dem übereinstimmen, was im JSON gesendet wird
  • price: Preis als String (z. B. "100.0") – MUSS exakt mit dem übereinstimmen, was im JSON gesendet wird
  • tif: Time-in-Force: "gtc", "ioc" oder "fok" (exakte Zeichenkettenübereinstimmung erforderlich)
  • route: Optionale Order-Route: "best_execution", "book_only" oder "rfq_only" (exakte Zeichenkettenübereinstimmung erforderlich, falls vorhanden)
  • clientId: Vom Client bereitgestellte Order-ID (String, kann leer sein "")
  • nonce: Eindeutige Nonce (uint64) zum Schutz vor Replay-Angriffen

Kritisch: price und size MÜSSEN Strings sein, sowohl in der signierten Nachricht als auch im JSON-Request-Body. Die String-Formatierung muss exakt übereinstimmen.

Route-Standardwert: route ist mindestens bis zum 4. Juli 2026 optional. Wenn sie im JSON weggelassen wird, lassen Sie sie auch in den typisierten Daten weg. Der Server behandelt eine weggelassene route als best_execution. POST /order gibt bei weggelassener Route Deprecation-Header zurück. Clients sollten route senden; sie wird nicht vor dem 4. Juli 2026 verpflichtend, kann aber später verpflichtend werden.

CancelOrder

Struct:

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

Feldanforderungen:

  • wallet: Wallet-Adresse
  • orderId: Order-ID als String (z. B. "123")
  • nonce: Eindeutige Nonce

CancelOrderByClientId

Struct:

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

Feldanforderungen:

  • wallet: Wallet-Adresse
  • clientId: Client-Order-ID (String)
  • nonce: Eindeutige Nonce

ApproveAgent

Struct:

struct ApproveAgent {
address agent;
uint64 nonce;
}

Feldanforderungen:

  • agent: Zu autorisierende Agent-Wallet-Adresse
  • nonce: Eindeutige Nonce

Hinweis: Die Wallet, die den Agent autorisiert, wird aus der wiederhergestellten Signatur abgeleitet.

RevokeAgent

Struct:

struct RevokeAgent {
address agent;
uint64 nonce;
}

Feldanforderungen:

  • agent: Zu widerrufende Agent-Wallet-Adresse
  • nonce: Eindeutige Nonce

Signierungsprozess

Schritt 1: Nachricht erstellen

Beispiel für PlaceOrder mit expliziter Route:

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

Schritt 2: Domain definieren

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

Schritt 3: Typisierte Daten signieren

Mit 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);

Schritt 4: Anfrage senden

Kritisch: Der JSON-Request-Body muss exakt dieselben String-Werte für price und size verwenden:

{
"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..."
}

Nonce-Verwaltung

Anforderungen:

  • Nonces MÜSSEN pro Wallet eindeutig sein
  • Nonces SOLLTEN aufsteigend sein (verhindert Replay-Angriffe)
  • Nonces werden nicht auf strikte Monotonie geprüft (Lücken sind erlaubt)

Best Practices:

  • Verwenden Sie einen persistenten Zähler pro Wallet
  • Erhöhen Sie den Zähler nach jeder erfolgreichen Signatur
  • Behandeln Sie Nonce-Lücken tolerant (z. B. wenn eine Anfrage fehlschlägt, wiederholen Sie sie mit derselben Nonce)

Agent-Autorisierung

Wenn Sie eine Agent-Wallet zum Signieren verwenden:

  1. Agent genehmigen (einmalig):

    POST /approve-agent
    {
    "agent": "0x...",
    "nonce": 1,
    "signature": "0x..." # Signed by wallet owner
    }
  2. Orders mit der Agent-Wallet signieren:

    • Verwenden Sie die Agent-Wallet zum Signieren von PlaceOrder- / CancelOrder-Nachrichten
    • Setzen Sie das Feld wallet auf die Adresse der Trading-Wallet
    • Die Middleware prüft, ob der Agent für diese Wallet autorisiert ist

Siehe Agent-Autorisierung für vollständige Details zur Agent-Autorisierung.

Perp-Orders (Hyperliquid-kompatibel)

Perp-Orders verwenden eine andere EIP-712-Domain und ein anderes Nachrichtenformat (Hyperliquid-Core-Kompatibilität):

Domain:

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

Nachricht: Agent-Struct mit MessagePack-kodierten Order-Daten.

Die genauen Felder finden Sie im aktuellen Leitfaden zur Signaturkodierung.

Häufige Fehler

"Signature verification failed"

Ursachen:

  1. price oder size als Zahl statt als String gesendet
  2. String-Formatierung zwischen Signieren und Senden geändert (z. B. "100.0" vs. "100")
  3. Falsche Nonce
  4. Falsche Domain (Chain-ID, Name, Version)
  5. Agent nicht für die Wallet autorisiert

"Unauthorized: signer not authorized for wallet"

Ursache: Der Signierer ist weder die Wallet selbst noch ein autorisierter Agent.

Lösung: Genehmigen Sie den Agent über POST /approve-agent oder signieren Sie direkt mit der Wallet.

Implementierungsbeispiele

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);

Signaturen testen

Beispiel für das Testen der Signaturgenerierung:

  1. Generieren Sie eine Signatur mit Ihrer Implementierung
  2. Senden Sie eine Test-Order über POST /order
  3. Prüfen Sie die Antwort: status="ACKED" oder status="REJECTED" mit Begründung
  4. Bei "signature_verification_failed" prüfen Sie:
    • Die String-Formatierung von price und size
    • Die Domain-Parameter (insbesondere chainId)
    • Die Korrektheit der Nonce

Sicherheitsüberlegungen

  1. Private Schlüssel niemals offenlegen: Verwenden Sie Hardware-Wallets oder sichere Schlüsselverwaltung
  2. Nonce-Verwaltung: Verwenden Sie persistente, aufsteigende Nonces pro Wallet
  3. Agent-Autorisierung: Überprüfen Sie autorisierte Agents regelmäßig über GET /authorized-agents
  4. String-Kodierung: Stellen Sie sicher, dass price und size sowohl beim Signieren als auch in der Anfrage Strings sind

Referenzen