Authentifizierung & Signierung
EIP-712-Signaturanforderungen für Schreiboperationen.
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:
- die Wallet-Adresse selbst sein (direkte Signierung), ODER
- 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 wirdprice: Preis als String (z. B."100.0") – MUSS exakt mit dem übereinstimmen, was im JSON gesendet wirdtif: 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-AdresseorderId: Order-ID als String (z. B."123")nonce: Eindeutige Nonce
CancelOrderByClientId
Struct:
struct CancelOrderByClientId {
address wallet;
string clientId;
uint64 nonce;
}
Feldanforderungen:
wallet: Wallet-AdresseclientId: Client-Order-ID (String)nonce: Eindeutige Nonce
ApproveAgent
Struct:
struct ApproveAgent {
address agent;
uint64 nonce;
}
Feldanforderungen:
agent: Zu autorisierende Agent-Wallet-Adressenonce: 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-Adressenonce: 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:
-
Agent genehmigen (einmalig):
POST /approve-agent{"agent": "0x...","nonce": 1,"signature": "0x..." # Signed by wallet owner} -
Orders mit der Agent-Wallet signieren:
- Verwenden Sie die Agent-Wallet zum Signieren von
PlaceOrder- /CancelOrder-Nachrichten - Setzen Sie das Feld
walletauf die Adresse der Trading-Wallet - Die Middleware prüft, ob der Agent für diese Wallet autorisiert ist
- Verwenden Sie die Agent-Wallet zum Signieren von
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:
priceodersizeals Zahl statt als String gesendet- String-Formatierung zwischen Signieren und Senden geändert (z. B.
"100.0"vs."100") - Falsche Nonce
- Falsche Domain (Chain-ID, Name, Version)
- 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:
- Generieren Sie eine Signatur mit Ihrer Implementierung
- Senden Sie eine Test-Order über
POST /order - Prüfen Sie die Antwort:
status="ACKED"oderstatus="REJECTED"mit Begründung - Bei
"signature_verification_failed"prüfen Sie:- Die String-Formatierung von
priceundsize - Die Domain-Parameter (insbesondere
chainId) - Die Korrektheit der Nonce
- Die String-Formatierung von
Sicherheitsüberlegungen
- Private Schlüssel niemals offenlegen: Verwenden Sie Hardware-Wallets oder sichere Schlüsselverwaltung
- Nonce-Verwaltung: Verwenden Sie persistente, aufsteigende Nonces pro Wallet
- Agent-Autorisierung: Überprüfen Sie autorisierte Agents regelmäßig über
GET /authorized-agents - String-Kodierung: Stellen Sie sicher, dass
priceundsizesowohl beim Signieren als auch in der Anfrage Strings sind
Referenzen
- EIP-712: https://eips.ethereum.org/EIPS/eip-712
- Implementierung: wird durch den Signature-Recovery- und Middleware-Stack abgewickelt.