Cette page a été traduite automatiquement. La version anglaise fait référence. Lire en anglais
Aller au contenu principal

Authentification & Signature

Exigences de signature EIP-712 pour les opérations d'écriture.

Domaine de signature en production

Utilisez le chain ID 999 pour la production. Le testnet est temporairement désactivé jusqu'à ce que Hypercall acquière davantage de HYPE de testnet.

Vue d'ensemble

Toutes les opérations d'écriture nécessitent des signatures de données typées EIP-712. Le signataire doit soit :

  1. Être l'adresse du portefeuille elle-même (signature directe), OU
  2. Être un agent autorisé pour le portefeuille (voir Autorisation d'agent)

Domaine EIP-712

Séparateur de domaine :

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

Chain ID : 999 (production)

Types de messages

PlaceOrder

Struct avec route explicite :

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

Struct lorsque la route est omise :

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

Exigences des champs :

  • wallet : Adresse du portefeuille (chaîne hexadécimale préfixée par 0x)
  • symbol : Symbole de l'option (p. ex., "BTC-20250131-100000-C")
  • side : "Buy" ou "Sell" (correspondance exacte de la chaîne requise)
  • size : Taille sous forme de chaîne de caractères (p. ex., "0.1") - DOIT correspondre exactement à ce qui est envoyé dans le JSON
  • price : Prix sous forme de chaîne de caractères (p. ex., "100.0") - DOIT correspondre exactement à ce qui est envoyé dans le JSON
  • tif : Durée de validité (time-in-force) : "gtc", "ioc" ou "fok" (correspondance exacte de la chaîne requise)
  • route : Route d'ordre optionnelle : "best_execution", "book_only" ou "rfq_only" (correspondance exacte de la chaîne requise lorsqu'elle est présente)
  • clientId : ID d'ordre fourni par le client (chaîne, peut être vide "")
  • nonce : Nonce unique (uint64) pour la protection contre les attaques par rejeu

Critique : price et size DOIVENT être des chaînes de caractères à la fois dans le message signé et dans le corps de la requête JSON. Le formatage des chaînes doit correspondre exactement.

Valeur par défaut de la route : route est optionnel au moins jusqu'au 4 juillet 2026. Lorsqu'il est omis du JSON, omettez-le également des données typées. Le serveur traite une route omise comme best_execution. POST /order renvoie des en-têtes de dépréciation lorsque la route est omise. Les clients devraient envoyer route ; il ne deviendra pas obligatoire avant le 4 juillet 2026, mais pourrait le devenir ultérieurement.

CancelOrder

Struct :

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

Exigences des champs :

  • wallet : Adresse du portefeuille
  • orderId : ID de l'ordre sous forme de chaîne de caractères (p. ex., "123")
  • nonce : Nonce unique

CancelOrderByClientId

Struct :

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

Exigences des champs :

  • wallet : Adresse du portefeuille
  • clientId : ID d'ordre client (chaîne de caractères)
  • nonce : Nonce unique

ApproveAgent

Struct :

struct ApproveAgent {
address agent;
uint64 nonce;
}

Exigences des champs :

  • agent : Adresse du portefeuille de l'agent à autoriser
  • nonce : Nonce unique

Remarque : Le portefeuille autorisant l'agent est dérivé de la signature récupérée.

RevokeAgent

Struct :

struct RevokeAgent {
address agent;
uint64 nonce;
}

Exigences des champs :

  • agent : Adresse du portefeuille de l'agent à révoquer
  • nonce : Nonce unique

Processus de signature

Étape 1 : Construire le message

Exemple pour PlaceOrder avec route explicite :

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

Étape 2 : Définir le domaine

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

Étape 3 : Signer les données typées

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

Étape 4 : Envoyer la requête

Critique : Le corps de la requête JSON doit utiliser exactement les mêmes valeurs de chaîne pour price et 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..."
}

Gestion des nonces

Exigences :

  • Les nonces DOIVENT être uniques par portefeuille
  • Les nonces DEVRAIENT être croissants (prévient les attaques par rejeu)
  • Les nonces ne sont pas validés pour une stricte monotonie (les écarts sont autorisés)

Bonnes pratiques :

  • Utilisez un compteur persistant par portefeuille
  • Incrémentez après chaque signature réussie
  • Gérez les écarts de nonces avec souplesse (p. ex., si une requête échoue, réessayez avec le même nonce)

Autorisation d'agent

Si vous utilisez un portefeuille agent pour signer :

  1. Approuver l'agent (une seule fois) :

    POST /approve-agent
    {
    "agent": "0x...",
    "nonce": 1,
    "signature": "0x..." # Signed by wallet owner
    }
  2. Signer les ordres avec le portefeuille agent :

    • Utilisez le portefeuille agent pour signer les messages PlaceOrder / CancelOrder
    • Définissez le champ wallet sur l'adresse du portefeuille de trading
    • Le middleware vérifie que l'agent est autorisé pour ce portefeuille

Voir Autorisation d'agent pour tous les détails sur l'autorisation des agents.

Ordres sur perpétuels (compatibles Hyperliquid)

Les ordres sur contrats perpétuels utilisent un domaine EIP-712 et un format de message différents (compatibilité avec Hyperliquid Core) :

Domaine :

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

Message : struct Agent avec des données d'ordre encodées en MessagePack.

Consultez le guide d'encodage des signatures en vigueur pour connaître les champs exacts.

Erreurs courantes

« Signature verification failed »

Causes :

  1. price ou size envoyé sous forme de nombre au lieu de chaîne de caractères
  2. Formatage de la chaîne modifié entre la signature et l'envoi (p. ex., "100.0" vs "100")
  3. Nonce incorrect
  4. Domaine incorrect (chain ID, nom, version)
  5. Agent non autorisé pour le portefeuille

« Unauthorized: signer not authorized for wallet »

Cause : Le signataire n'est ni le portefeuille lui-même ni un agent autorisé.

Solution : Approuvez l'agent via POST /approve-agent ou signez directement avec le portefeuille.

Exemples d'implémentation

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

Tester les signatures

Exemple pour tester la génération de signatures :

  1. Générez une signature avec votre implémentation
  2. Envoyez un ordre de test via POST /order
  3. Vérifiez la réponse : status="ACKED" ou status="REJECTED" avec la raison
  4. En cas de "signature_verification_failed", vérifiez :
    • Le formatage des chaînes price et size
    • Les paramètres du domaine (en particulier chainId)
    • L'exactitude du nonce

Considérations de sécurité

  1. N'exposez jamais les clés privées : Utilisez des portefeuilles matériels ou une gestion de clés sécurisée
  2. Gestion des nonces : Utilisez des nonces persistants et croissants par portefeuille
  3. Autorisation des agents : Auditez régulièrement les agents autorisés via GET /authorized-agents
  4. Encodage des chaînes : Assurez-vous que price et size sont des chaînes de caractères à la fois lors de la signature et dans la requête

Références