Kimlik Doğrulama ve İmzalama
Yazma işlemleri için EIP-712 imza gereksinimleri.
Üretim için chain ID 999 kullanın. Testnet, Hypercall daha fazla testnet HYPE edinene kadar geçici olarak devre dışıdır.
Genel Bakış
Tüm yazma işlemleri EIP-712 tipli veri imzaları gerektirir. İmzalayan taraf şunlardan biri olmalıdır:
- Cüzdan adresinin kendisi (doğrudan imzalama), VEYA
- Cüzdan için yetkilendirilmiş bir agent (bkz. Agent auth)
EIP-712 Domain
Domain separator:
{
"name": "Hypercall",
"version": "1",
"chainId": 999,
"verifyingContract": "0x0000000000000000000000000000000000000000"
}
Chain ID: 999 (üretim)
Mesaj Türleri
PlaceOrder
Açık route içeren struct:
struct PlaceOrder {
address wallet;
string symbol;
string side;
string size;
string price;
string tif;
string route;
string clientId;
uint64 nonce;
}
Route atlandığında struct:
struct PlaceOrder {
address wallet;
string symbol;
string side;
string size;
string price;
string tif;
string clientId;
uint64 nonce;
}
Alan gereksinimleri:
wallet: Cüzdan adresi (0x ön ekli hex string)symbol: Opsiyon sembolü (örn."BTC-20250131-100000-C")side:"Buy"veya"Sell"(tam string eşleşmesi gereklidir)size: String olarak büyüklük (örn."0.1") - JSON'da gönderilenle birebir aynı OLMALIDIRprice: String olarak fiyat (örn."100.0") - JSON'da gönderilenle birebir aynı OLMALIDIRtif: Time-in-force:"gtc","ioc"veya"fok"(tam string eşleşmesi gereklidir)route: İsteğe bağlı emir yönlendirmesi:"best_execution","book_only"veya"rfq_only"(mevcut olduğunda tam string eşleşmesi gereklidir)clientId: İstemci tarafından sağlanan emir ID'si (string, boş olabilir"")nonce: Yeniden oynatma (replay) korumasına yönelik benzersiz nonce (uint64)
Kritik: price ve size, hem imzalanan mesajda hem de JSON istek gövdesinde string OLMALIDIR. String formatı birebir eşleşmelidir.
Route varsayılanı: route, en az 4 Temmuz 2026'ya kadar isteğe bağlıdır. JSON'dan çıkarıldığında, tipli veriden de çıkarın. Sunucu, atlanan route değerini best_execution olarak değerlendirir. POST /order, atlanan route için deprecation başlıkları döndürür. İstemciler route göndermelidir; 4 Temmuz 2026'dan önce zorunlu olmayacaktır, ancak daha sonra zorunlu hale gelebilir.
CancelOrder
Struct:
struct CancelOrder {
address wallet;
string orderId;
uint64 nonce;
}
Alan gereksinimleri:
wallet: Cüzdan adresiorderId: String olarak emir ID'si (örn."123")nonce: Benzersiz nonce
CancelOrderByClientId
Struct:
struct CancelOrderByClientId {
address wallet;
string clientId;
uint64 nonce;
}
Alan gereksinimleri:
wallet: Cüzdan adresiclientId: İstemci emir ID'si (string)nonce: Benzersiz nonce
ApproveAgent
Struct:
struct ApproveAgent {
address agent;
uint64 nonce;
}
Alan gereksinimleri:
agent: Yetkilendirilecek agent cüzdan adresinonce: Benzersiz nonce
Not: Agent'ı yetkilendiren cüzdan, kurtarılan imzadan türetilir.
RevokeAgent
Struct:
struct RevokeAgent {
address agent;
uint64 nonce;
}
Alan gereksinimleri:
agent: Yetkisi kaldırılacak agent cüzdan adresinonce: Benzersiz nonce
İmzalama Süreci
Adım 1: Mesajı Oluşturun
Açık route içeren PlaceOrder için örnek:
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
};
Adım 2: Domain'i Tanımlayın
const domain = {
name: "Hypercall",
version: "1",
chainId: 999,
verifyingContract: "0x0000000000000000000000000000000000000000"
};
Adım 3: Tipli Veriyi İmzalayın
ethers.js kullanarak:
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);
Adım 4: İsteği Gönderin
Kritik: JSON istek gövdesi, price ve size için birebir aynı string değerlerini kullanmalıdır:
{
"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 Yönetimi
Gereksinimler:
- Nonce'lar her cüzdan için benzersiz OLMALIDIR
- Nonce'lar artan şekilde OLMALIDIR (yeniden oynatma saldırılarını önler)
- Nonce'lar katı monotonluk açısından doğrulanmaz (boşluklara izin verilir)
En iyi uygulamalar:
- Her cüzdan için kalıcı bir sayaç kullanın
- Her başarılı imzadan sonra artırın
- Nonce boşluklarını sorunsuz şekilde yönetin (örn. bir istek başarısız olursa aynı nonce ile yeniden deneyin)
Agent Yetkilendirmesi
İmzalama için bir agent cüzdanı kullanıyorsanız:
-
Agent'ı onaylayın (tek seferlik):
POST /approve-agent{"agent": "0x...","nonce": 1,"signature": "0x..." # Signed by wallet owner} -
Emirleri agent cüzdanı ile imzalayın:
PlaceOrder/CancelOrdermesajlarını imzalamak için agent cüzdanını kullanınwalletalanını işlem cüzdanı adresine ayarlayın- Middleware, agent'ın söz konusu cüzdan için yetkili olduğunu doğrular
Agent yetkilendirmesinin tüm ayrıntıları için Agent auth sayfasına bakın.
Perp Emirleri (Hyperliquid Uyumlu)
Perp emirleri, farklı bir EIP-712 domain'i ve mesaj formatı kullanır (Hyperliquid Core uyumluluğu):
Domain:
{
"name": "Exchange",
"version": "1",
"chainId": 1337,
"verifyingContract": "0x0000000000000000000000000000000000000000"
}
Mesaj: MessagePack ile kodlanmış emir verisi içeren Agent struct'ı.
Alanların tam listesi için güncel imza kodlama rehberine bakın.
Yaygın Hatalar
"Signature verification failed"
Nedenler:
priceveyasizestring yerine sayı olarak gönderildi- String formatı imzalama ile gönderim arasında değişti (örn.
"100.0"ve"100") - Yanlış nonce
- Yanlış domain (chain ID, name, version)
- Agent cüzdan için yetkilendirilmemiş
"Unauthorized: signer not authorized for wallet"
Neden: İmzalayan, cüzdanın kendisi değildir ve yetkili bir agent da değildir.
Çözüm: POST /approve-agent üzerinden agent'ı onaylayın veya doğrudan cüzdan ile imzalayın.
Uygulama Örnekleri
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);
İmzaları Test Etme
İmza üretimini test etmek için örnek:
- Kendi implementasyonunuzla imza üretin
POST /orderüzerinden test emri gönderin- Yanıtı doğrulayın:
status="ACKED"veya nedeni ile birliktestatus="REJECTED" "signature_verification_failed"alırsanız, şunları kontrol edin:pricevesizealanlarının string formatı- Domain parametreleri (özellikle
chainId) - Nonce doğruluğu
Güvenlik Hususları
- Özel anahtarları asla ifşa etmeyin: Donanım cüzdanları veya güvenli anahtar yönetimi kullanın
- Nonce yönetimi: Her cüzdan için kalıcı ve artan nonce'lar kullanın
- Agent yetkilendirmesi:
GET /authorized-agentsüzerinden yetkili agent'ları düzenli olarak denetleyin - String kodlaması:
pricevesizealanlarının hem imzalamada hem de istekte string olduğundan emin olun
Referanslar
- EIP-712: https://eips.ethereum.org/EIPS/eip-712
- Uygulama: imza kurtarma ve middleware yığını tarafından yönetilir.