エージェント認可
マーケットメイカー向けのマルチキー署名サポートです。
概要
エージェント認可により、専用の署名キー(エージェント)が取引ウォレットに代わって注文の発注およびキャンセルを行うことができます。これは以下の用途に役立ちます:
- セキュリティ: 取引ウォレットのキーをコールドストレージに保管し、署名にはホットキーを使用する
- 運用: 複数のチームメンバーが異なるキーで署名できる
- 自動化: 自動化システムが専用キーで署名できる
2つの認可モデル
Hypercallには、プロダクトに応じて2つの独立した委任システムがあります:
| プロダクト | 委任タイプ | 保存先 | 設定方法 |
|---|---|---|---|
| オプション (REST API) | エージェント認可 | オフチェーン(データベース) | POST /approve-agent |
| パーペチュアル (オンチェーンディレクティブ) | APIウォレット | オンチェーン(Exchangeコントラクト) | hc_update_api_wallet ディレクティブ |
これらは独立したシステムです。オプション取引用にエージェントを承認しても、パーペチュアル取引の認可にはならず、その逆も同様です。
オプション: エージェント認可(オフチェーン)
エージェントは、取引ウォレットに代わって PlaceOrder および CancelOrder のEIP-712メッセージに署名します。APIサーバーは、エンジンに転送する前にデータベースに対して認可を検証します。
パーペチュアル: APIウォレット(オンチェーン)
APIウォレットは、HypercallAgentSign EIP-712ドメインを使用してパーペチュアルのディレクティブに署名します。Exchangeコントラクトは、isApiWalletActive() を介してオンチェーンで認可を検証します。ディレクティブ署名の完全なリファレンスについては、EIP-712 Signing を参照してください。
APIウォレットを追加または削除するには、アカウントのマネージャーウォレットで署名した hc_update_api_wallet ディレクティブを送信します。
これはHyperliquidのAPIウォレットモデルを踏襲しています。Hyperliquidは、Nonces and API wallets においてこれらをAPIウォレット(エージェントウォレットとも呼ばれます)として文書化しており、approveAgent については Exchange endpoint で文書化しています。
ノンスによるリプレイ防止
ノンスの追跡は、Hyperliquidのノンスモデル に従います。すべての署名済みアクション(注文、エージェントの承認/取り消し、QPハンドシェイク)は、署名者ごとのノンス空間を共有します。エンジンは署名者アドレスごとに上位100個のノンスを保存します。新しいノンスは、以下の条件を満たす場合に受け入れられます:
nonce > min(stored_set)-- 保存されている最小のノンスより大きいこと!stored_set.contains(nonce)-- 重複していないことnonceがサーバータイムスタンプの (T - 2日, T + 1日) の範囲内であること
順不同のノンスも許可されます(例: ノンス105の後に102を使っても、どちらもセットの最小値より大きければ両方とも有効です)。セットは100エントリに制限されており、新しいエントリが追加されると最も古いエントリが削除されます。これにより、単一の大きなノンスによってアカウントが使用不能になることを防ぎます。
ノンスの署名者は、EIP-712メッセージに署名したアドレスです。注文の場合はAPIウォレット、エージェント認可の場合はリカバリーされた署名者、ハンドシェイクの場合はQPウォレットです。クライアントは、ノンスのシードとして Date.now()(ミリ秒エポック)を使用し、単調に増加させることをおすすめします。
オプションのエージェント認可
直接署名
signer == wallet の場合、注文は常に認可されます(自己署名)。
エージェント署名
signer != wallet の場合、署名者は認可されたエージェントである必要があります:
- エージェントがエンジン所有の認可状態に存在していること
- 認可の有効期限が切れていないこと
- エンジンのスナップショットとエンジンジャーナルが、再起動時の永続的な復元ソースです
エージェントの承認
エンドポイント: POST /approve-agent
リクエスト: ApproveAgentRequest
{
"agent": "0x...",
"nonce": 1,
"signature": "0x..."
}
署名:
- ウォレット所有者が
ApproveAgentメッセージに署名します - ウォレットはリカバリーされた署名から導出されます
- エージェントはリクエスト内の
agentフィールドです
EIP-712構造体:
struct ApproveAgent {
address agent;
uint64 nonce;
}
レスポンス: ApproveAgentResponse
{
"success": true,
"error": null
}
注意事項:
- エージェント認可は永続的です(DBに保存されます)
expires_atが設定されていない限り、認可の有効期限は切れません(未実装)
エージェントの取り消し
エンドポイント: DELETE /revoke-agent
リクエスト: RevokeAgentRequest
{
"agent": "0x...",
"nonce": 2,
"signature": "0x..."
}
署名:
- ウォレット所有者が
RevokeAgentメッセージに署名します - ウォレットはリカバリーされた署名から導出されます
EIP-712構造体:
struct RevokeAgent {
address agent;
uint64 nonce;
}
レスポンス: RevokeAgentResponse
注意事項:
- ジャーナル化された
RevokeAgentコマンドがエンジン所有の認可状態に適用されます - 取引APIの認可チェックはバックエンドの状態を読み取り、取引リクエストに対して取り消しを強制します。Trollboxへの投稿は、有効なエージェント認可をより長くキャッシュする場合があるため、直近に取り消されたエージェントでも、そのキャッシュが期限切れになるか、ベストエフォートの無効化が該当するエッジロケーションに到達するまでは投稿できる可能性があります。このキャッシュは取引APIのミューテーションを認可するものではありません。
- 取り消し後、エージェントはそのウォレットのために署名できなくなります
認可済みエージェントの取得
エンドポイント: GET /authorized-agents?wallet=...
クエリパラメータ:
wallet(必須)
レスポンス:
{
"agents": [
"0x...",
"0x..."
]
}
注意事項:
- アクティブで有効期限切れでないエージェントのみを返します
created_at DESCの順序で並べられます
エージェントの使用方法
エージェントによる注文の署名
エージェントが承認された後の手順:
- エージェントウォレットで注文に署名: エージェントウォレットを使用して
PlaceOrder/CancelOrderメッセージに署名します - walletフィールドの設定:
walletフィールドに取引ウォレットのアドレス(エージェントのアドレスではありません)を設定します - ミドルウェアによる検証: ミドルウェアは、エージェントがそのウォレットに対して認可されていることを検証します
例:
// Agent wallet signs the order
const agentSigner = new ethers.Wallet(agentPrivateKey);
const message = {
wallet: "0x...", // Trading wallet (not agent)
symbol: "BTC-20250131-100000-C",
side: "Buy",
size: "0.1",
price: "100.0",
tif: "gtc",
clientId: "mm-1",
nonce: 1
};
// Sign with agent wallet
const signature = await agentSigner._signTypedData(domain, types, message);
// Send request
const response = await fetch('/order', {
method: 'POST',
body: JSON.stringify({
...message,
signature
})
});
エージェントによる一括注文
一括エンドポイントは、アイテムごとにエージェント認可を検証します:
POST /bulk_orderの各注文は、それぞれ異なるエージェントで署名できます- エージェント認可はアイテムごとにチェックされます
- いずれかのアイテムがエージェント認可に失敗した場合、そのアイテムは
BulkOrderResultでエラーを返します
認可チェック
ミドルウェア(単一注文)
エンドポイント:
POST /orderDELETE /orderDELETE /order_cloid
チェック内容: signature_and_agent_middleware は以下を検証します:
- 署名のリカバリーが成功すること
- 署名者が認可されていること(signer == wallet またはエージェントが認可済み)
ハンドラー(一括注文)
エンドポイント:
POST /bulk_orderDELETE /bulk_orderDELETE /bulk_order_cloid
チェック内容: ハンドラー内でアイテムごとに検証します:
- アイテムごとの署名リカバリー
- アイテムごとのエージェント認可チェック
認可の保存
エージェント認可は、エンジン所有の状態です。ApproveAgent および RevokeAgent コマンドはジャーナル化され、エンジンのリプレイを通じて復元され、APIチェック用に読み取りスナップショットを通じて公開されます。
認可ロジック
署名者の認可は、明示的なOR条件です:
- 直接署名:
signer == walletの場合。 - エージェント署名: エンジンスナップショットに
wallet_address == <wallet>かつagent_address == <signer>の認可レコードが含まれており、expires_atが存在しないか将来の日時である場合。
有効期限
expires_at は、エンジンスナップショットの認可チェックによって強制されます。expires_at < NOW() のエージェントは、未認可として扱われます。
セキュリティ上の考慮事項
- エージェントキーのセキュリティ: エージェントの秘密鍵を保護してください(ハードウェアウォレットまたは安全なキー管理を使用)
- 定期的な監査:
GET /authorized-agentsを通じて、認可済みエージェントを定期的に確認してください - 未使用エージェントの取り消し: 不要になったエージェントは取り消してください
- 有効期限: 承認パスがデフォルト以外の有効期限を公開している場合は、一時的なエージェント認可に
expires_atを使用してください
ベストプラクティス
- 自動化にはエージェントを使用: 取引ウォレットのキーはコールドストレージに保管し、自動化システムにはエージェントを使用してください
- エージェントのスコープを制限: アクセスが必要なエージェントのみを承認してください
- エージェントの使用状況を監視: どのエージェントが注文を出しているかを追跡してください
- 速やかに取り消す: 不要になったエージェントは即座に取り消してください
よくある問題
「Unauthorized: signer not authorized for wallet」
原因: エージェントが承認されていない、または認可が期限切れ/取り消されています。
解決策: POST /approve-agent でエージェントを承認するか、ウォレットで直接署名してください。
エージェント認可が機能しない
原因:
- エージェントがエンジン所有の認可状態に存在しない
- 認可が取り消された
- 認可の有効期限が切れている
解決策: GET /authorized-agents?wallet=... でエージェントのステータスを確認してください。