에이전트 권한 부여
마켓메이커를 위한 다중 키 서명 지원입니다.
개요
에이전트 권한 부여를 사용하면 전용 서명 키(에이전트)가 거래 지갑을 대신해 주문을 제출하고 취소할 수 있습니다. 다음과 같은 경우에 유용합니다.
- 보안: 거래 지갑 키는 콜드 스토리지에 보관하고, 서명에는 핫 키를 사용합니다
- 운영: 여러 팀원이 각자 다른 키로 서명할 수 있습니다
- 자동화: 자동화 시스템이 전용 키로 서명할 수 있습니다
두 가지 권한 부여 모델
Hypercall은 상품에 따라 두 가지 별도의 위임 시스템을 사용합니다.
| 상품 | 위임 유형 | 저장 위치 | 설정 방법 |
|---|---|---|---|
| 옵션 (REST API) | 에이전트 권한 부여 | Hypercall 엔진 상태 | POST /approve-agent |
| 퍼프 | API 지갑 | 온체인 (사용자의 Account 컨트랙트) | hc_update_api_wallet 디렉티브 |
두 시스템은 서로 독립적입니다. 옵션 거래용으로 에이전트를 승인해도 퍼프 거래 권한이 부여되지 않으며, 그 반대도 마찬가지입니다.
옵션: 에이전트 권한 부여 (오프체인)
에이전트는 거래 지갑을 대신해 PlaceOrder 및 CancelOrder EIP-712 메시지에 서명합니다. API는 주문이 엔진에 도달하기 전에 엔진의 권한 상태를 기준으로 에이전트를 확인합니다.
퍼프: API 지갑 (온체인)
API 지갑은 HypercallApiSign EIP-712 도메인에서 퍼프 주문과 취소에 서명합니다. Hypercall은 주문을 접수하기 전에 사용자의 Account 컨트랙트(isApiWalletActive())를 기준으로 서명자를 확인합니다. TEE 경로(앱 기본값이며, API 클라이언트는 옵트인 방식)에서는 사용자 Account 전용의 별도 Hyperliquid 거래 키를 보관한 보안 엔클레이브가 네이티브 주문에 서명하고 전송합니다. 앱의 고급 설정인 스마트 컨트랙트 경로에서는 주문이 사용자의 Account 컨트랙트를 거칩니다. 서명 레퍼런스는 EIP-712 서명을, 처리 흐름은 퍼프를 참고하십시오.
API 지갑을 추가하거나 제거하려면 계정의 매니저 지갑으로 서명한 hc_update_api_wallet 디렉티브를 제출합니다.
이는 Hyperliquid의 API 지갑 모델과 동일합니다. Hyperliquid는 Nonces and API wallets에서 이를 API 지갑(에이전트 지갑이라고도 함)으로 설명하며, Exchange endpoint에서 approveAgent를 설명합니다.
논스 재전송 방지
논스 추적은 Hyperliquid 논스 모델을 따릅니다. 서명된 모든 액션(주문, 에이전트 승인/철회, QP 핸드셰이크)은 서명자별 논스 공간을 공유합니다. 엔진은 서명자 주소별로 가장 높은 논스 100개를 저장합니다. 새 논스는 다음 조건을 충족하면 승인됩니다.
!stored_set.contains(nonce): 중복이 아니어야 합니다- 집합에 100개 항목이 채워진 경우
nonce > min(stored_set): 저장된 논스 중 가장 작은 값보다 커야 합니다 nonce가 서버 타임스탬프 기준 (T - 2일, T + 1일) 범위 안에 있어야 합니다
순서가 뒤바뀐 논스도 허용됩니다(예: 두 값 모두 집합의 최솟값보다 크다면 논스 105 다음에 102를 사용해도 모두 유효합니다). 집합은 최대 100개 항목으로 제한되므로, 새 항목이 추가되면 가장 오래된 항목이 제거됩니다. 이를 통해 하나의 큰 논스로 인해 계정이 사용 불능 상태가 되는 것을 방지합니다.
논스 서명자는 EIP-712 메시지에 서명한 주소입니다. 주문의 경우 API 지갑, 에이전트 권한 부여의 경우 복구된 서명자, 핸드셰이크의 경우 QP 지갑입니다. 클라이언트는 Date.now()(밀리초 단위 epoch)를 논스 시드로 사용하고 단조 증가시켜야 합니다.
옵션 에이전트 권한 부여
직접 서명
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
}
참고:
- 에이전트 권한은 저널에 기록되는 엔진 상태이며 재시작 후에도 유지됩니다
- 이 엔드포인트로 승인한 권한은 만료되지 않습니다
에이전트 철회
엔드포인트: DELETE /revoke-agent
요청: RevokeAgentRequest
{
"agent": "0x...",
"nonce": 2,
"signature": "0x..."
}
서명:
- 지갑 소유자가
RevokeAgent메시지에 서명합니다 - 지갑은 복구된 서명에서 도출됩니다
EIP-712 구조체:
struct RevokeAgent {
address agent;
uint64 nonce;
}
응답: RevokeAgentResponse
참고:
- 저널에 기록되는
RevokeAgent명령을 엔진 소유 권한 상태에 적용합니다 - 거래 API 권한 확인은 백엔드 상태를 읽어 거래 요청에 철회를 적용합니다. Trollbox 게시는 에이전트의 승인된 권한을 더 오래 캐시할 수 있으므로, 최근 철회된 에이전트라도 해당 캐시가 만료되거나 최선 노력(best-effort) 방식의 무효화가 관련 엣지 위치에 도달할 때까지는 게시가 가능할 수 있습니다. 이 캐시는 거래 API 변경 요청을 승인하지 않습니다.
- 철회 후에는 에이전트가 더 이상 해당 지갑을 대신해 서명할 수 없습니다
모든 에이전트 철회
엔드포인트: DELETE /revoke-all-agents
요청: { "nonce": 3, "signature": "0x..." }, 지갑 소유자가 RevokeAllAgents(uint64 nonce)에 대해 서명합니다. 복구된 지갑의 모든 에이전트를 제거합니다.
권한 부여된 에이전트 조회
엔드포인트: GET /authorized-agents?wallet=...
쿼리 파라미터:
wallet(필수)
응답:
{
"agents": [
"0x...",
"0x..."
]
}
참고:
- 활성 상태이며 만료되지 않은 에이전트만 반환합니다
에이전트 사용
에이전트로 주문 서명
에이전트가 승인되면 다음과 같이 사용합니다.
- 에이전트 지갑으로 주문 서명: 에이전트 지갑으로
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. The REST body uses snake_case `client_id`.
const { clientId, ...rest } = message;
const response = await fetch('/order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...rest,
client_id: clientId,
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을 지원하며, 만료된 에이전트는 권한이 없는 것으로 처리합니다. POST /approve-agent는 이 값을 설정하지 않으므로 API로 승인된 에이전트는 철회될 때까지 활성 상태로 유지됩니다.
보안 고려사항
- 에이전트 키 보안: 에이전트 개인 키를 보호하십시오(하드웨어 지갑 또는 안전한 키 관리 시스템 사용)
- 정기 감사:
GET /authorized-agents로 권한 부여된 에이전트를 정기적으로 검토하십시오 - 사용하지 않는 에이전트 철회: 더 이상 필요하지 않은 에이전트는 철회하십시오
모범 사례
- 자동화에 에이전트 사용: 거래 지갑 키는 콜드 스토리지에 보관하고, 자동화 시스템에는 에이전트를 사용하십시오
- 에이전트 범위 제한: 접근이 필요한 에이전트만 승인하십시오
- 에이전트 사용 모니터링: 어떤 에이전트가 주문을 제출하는지 추적하십시오
- 신속한 철회: 더 이상 필요하지 않은 에이전트는 즉시 철회하십시오
자주 발생하는 문제
"Signer 0x... is not authorized to act for wallet 0x..."
원인: 에이전트가 승인되지 않았거나 권한이 만료 또는 철회되었습니다.
해결 방법: POST /approve-agent로 에이전트를 승인하거나 지갑으로 직접 서명하십시오.
에이전트 권한이 작동하지 않는 경우
원인:
- 에이전트가 엔진 소유 권한 상태에 존재하지 않습니다
- 권한이 철회되었습니다
- 권한이 만료되었습니다
해결 방법: GET /authorized-agents?wallet=...로 에이전트 상태를 확인하십시오.