Цю сторінку перекладено автоматично. Оригінал англійською мовою є канонічним. Читати англійською →
Перейти к основному содержимому

Автентифікація та підписання

Вимоги до підписів EIP-712 для операцій запису.

Домен підпису для продакшену

Використовуйте chain ID 999 для продакшену. Тестнет тимчасово вимкнено, доки Hypercall не отримає більше тестнет HYPE.

Огляд​

Усі операції запису вимагають підписів типізованих даних EIP-712. Підписант повинен:

  1. Бути самою адресою гаманця (пряме підписання), АБО
  2. Бути авторизованим агентом для гаманця (див. Авторизація агентів)

Домен EIP-712​

Доменний роздільник:

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

Chain ID: 999 (продакшен)

Типи повідомлень​

PlaceOrder​

Структура з явним route:

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

Структура, коли route опущено:

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

Вимоги до полів:

  • wallet: адреса гаманця (hex-рядок з префіксом 0x)
  • symbol: символ опціону (наприклад, "BTC-20250131-100000-C")
  • side: "Buy" або "Sell" (потрібен точний збіг рядка)
  • size: розмір як рядок (наприклад, "0.1") - МАЄ точно збігатися з тим, що надсилається в JSON
  • price: ціна як рядок (наприклад, "100.0") - МАЄ точно збігатися з тим, що надсилається в JSON
  • tif: час дії заявки (time-in-force): "gtc", "ioc" або "fok" (потрібен точний збіг рядка)
  • route: необов'язковий маршрут ордера: "best_execution", "book_only" або "rfq_only" (потрібен точний збіг рядка, якщо присутній)
  • clientId: ідентифікатор ордера, наданий клієнтом (рядок, може бути порожнім "")
  • nonce: унікальний nonce (uint64) для захисту від повторного відтворення

Критично важливо: price та size МАЮТЬ бути рядками як у підписаному повідомленні, так і в тілі JSON-запиту. Форматування рядків має збігатися точно.

Значення route за замовчуванням: route є необов'язковим щонайменше до 4 липня 2026 року. Якщо його опущено в JSON, опустіть його й у типізованих даних. Сервер трактує опущений route як best_execution. POST /order повертає заголовки про застарілість (deprecation) для опущеного route. Клієнтам варто надсилати route; він не стане обов'язковим до 4 липня 2026 року, але може стати обов'язковим пізніше.

CancelOrder​

Структура:

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

Вимоги до полів:

  • wallet: адреса гаманця
  • orderId: ідентифікатор ордера як рядок (наприклад, "123")
  • nonce: унікальний nonce

CancelOrderByClientId​

Структура:

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

Вимоги до полів:

  • wallet: адреса гаманця
  • clientId: клієнтський ідентифікатор ордера (рядок)
  • nonce: унікальний nonce

ApproveAgent​

Структура:

struct ApproveAgent {
address agent;
uint64 nonce;
}

Вимоги до полів:

  • agent: адреса гаманця агента для авторизації
  • nonce: унікальний nonce

Примітка: гаманець, що авторизує агента, визначається з відновленого підпису.

RevokeAgent​

Структура:

struct RevokeAgent {
address agent;
uint64 nonce;
}

Вимоги до полів:

  • agent: адреса гаманця агента для відкликання
  • nonce: унікальний nonce

Процес підписання​

Крок 1: Створіть повідомлення​

Приклад для PlaceOrder з явним 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
};

Крок 2: Визначте домен​

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

Крок 3: Підпишіть типізовані дані​

За допомогою 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);

Крок 4: Надішліть запит​

Критично важливо: тіло JSON-запиту має використовувати точно ті самі рядкові значення для price та 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..."
}

Керування nonce​

Вимоги:

  • Nonce МАЮТЬ бути унікальними для кожного гаманця
  • Nonce ВАРТО збільшувати послідовно (запобігає атакам повторного відтворення)
  • Nonce не перевіряються на сувору монотонність (пропуски дозволені)

Найкращі практики:

  • Використовуйте постійний лічильник для кожного гаманця
  • Збільшуйте лічильник після кожного успішного підпису
  • Коректно обробляйте пропуски nonce (наприклад, якщо запит не вдався, повторіть із тим самим nonce)

Авторизація агентів​

Якщо для підписання використовується гаманець агента:

  1. Схваліть агента (одноразово):

    POST /approve-agent
    {
    "agent": "0x...",
    "nonce": 1,
    "signature": "0x..." # Signed by wallet owner
    }
  2. Підписуйте ордери гаманцем агента:

    • Використовуйте гаманець агента для підписання повідомлень PlaceOrder / CancelOrder
    • Установіть у полі wallet адресу торгового гаманця
    • Проміжне програмне забезпечення перевіряє, що агент авторизований для цього гаманця

Див. Авторизація агентів для повних деталей авторизації агентів.

Ордери на перпи (сумісні з Hyperliquid)​

Ордери на безстрокові контракти використовують інший домен EIP-712 та формат повідомлення (сумісність з Hyperliquid Core):

Домен:

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

Повідомлення: структура Agent з даними ордера, закодованими у форматі MessagePack.

Точні поля див. в актуальному посібнику з кодування підписів.

Поширені помилки​

"Signature verification failed"​

Причини:

  1. price або size надіслано як число замість рядка
  2. Форматування рядка змінилося між підписанням і надсиланням (наприклад, "100.0" проти "100")
  3. Неправильний nonce
  4. Неправильний домен (chain ID, name, version)
  5. Агент не авторизований для гаманця

"Unauthorized: signer not authorized for wallet"​

Причина: підписант не є самим гаманцем і не є авторизованим агентом.

Рішення: схваліть агента через POST /approve-agent або підписуйте безпосередньо гаманцем.

Приклади реалізації​

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

Тестування підписів​

Приклад тестування генерації підпису:

  1. Згенеруйте підпис за допомогою вашої реалізації
  2. Надішліть тестовий ордер через POST /order
  3. Перевірте відповідь: status="ACKED" або status="REJECTED" із зазначенням причини
  4. Якщо "signature_verification_failed", перевірте:
    • Форматування рядків price та size
    • Параметри домену (особливо chainId)
    • Коректність nonce

Міркування безпеки​

  1. Ніколи не розкривайте приватні ключі: використовуйте апаратні гаманці або безпечне керування ключами
  2. Керування nonce: використовуйте постійні, послідовно зростаючі nonce для кожного гаманця
  3. Авторизація агентів: регулярно перевіряйте авторизованих агентів через GET /authorized-agents
  4. Кодування рядків: переконайтеся, що price та size є рядками як під час підписання, так і в запиті

Джерела​

  • EIP-712: https://eips.ethereum.org/EIPS/eip-712
  • Реалізація: обробляється стеком відновлення підписів і проміжного програмного забезпечення.