Skip to main content

EIP-712 주문 서명

Conviction 의 모든 주문은 EIP-712 typed data 로 서명됩니다. 일반 사용자는 UI 가 이 과정을 자동으로 처리하므로 신경 쓸 필요가 없습니다. 이 페이지는 프로그램으로 직접 호가를 올리려는 봇·마켓 메이커 를 위한 참고 자료입니다.

#도메인 (EIP-712 Domain Separator)

{
  name: 'Conviction CTF Exchange', // byte-for-byte 일치 필수
  version: '1',
  chainId: 56,                           // BSC mainnet
  verifyingContract: CTF_EXCHANGE_ADDRESS, // 환경별 주소 다름
}
도메인 name 의 한 글자도 틀리면 안 됩니다

typo 가 있어도 ECDSA 자체는 통과하므로 클라이언트 시점에는 정상으로 보입니다. 그러나 컨트랙트 측 도메인과 다르므로 체결 시점에 reverts 됩니다.

verifyingContract 는 BSC 메인넷의 CTFExchange 주소입니다. 서버 응답이나 환경 변수에서 가져오세요 — 클라이언트에 하드코딩하지 마세요.

#Order 구조

필드타입의미
saltuint256무작위 식별자 (재서명 시마다 새로 생성)
makeraddress사용자 주소. EIP-7702 위임으로 EOA 가 곧 AA 이므로 maker == signer.
signeraddress사용자의 EOA — maker 와 동일한 주소.
takeraddress0x0000... (공개 호가)
tokenIduint256결과의 position ID (= outcome 의 position_id)
makerAmountuint256maker 가 내놓는 자산의 wei 양
takerAmountuint256maker 가 받고 싶은 자산의 wei 양
expirationuint256주문 만료 unix 초. 0 = 만료 없음
nonceuint256CTFExchange.nonces(maker) 로 매번 fresh
feeRateBpsuint256수수료 상한 (basis points)
sideuint8BUY = 0, SELL = 1
signatureTypeuint8EOA = 0 (사용자 주문)

#EIP-712 type 정의

const types = {
  Order: [
    { name: 'salt',          type: 'uint256' },
    { name: 'maker',         type: 'address' },
    { name: 'signer',        type: 'address' },
    { name: 'taker',         type: 'address' },
    { name: 'tokenId',       type: 'uint256' },
    { name: 'makerAmount',   type: 'uint256' },
    { name: 'takerAmount',   type: 'uint256' },
    { name: 'expiration',    type: 'uint256' },
    { name: 'nonce',         type: 'uint256' },
    { name: 'feeRateBps',    type: 'uint256' },
    { name: 'side',          type: 'uint8'   },
    { name: 'signatureType', type: 'uint8'   },
  ],
};

#단일 주문 서명 예시 (viem)

import { parseUnits } from 'viem';
 
const SHARES = parseUnits('100', 18);
 
const order = {
  salt: BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)),
  maker: eoaAddress,                 // EIP-7702: EOA 가 곧 AA
  signer: eoaAddress,                // 같은 주소
  taker: '0x0000000000000000000000000000000000000000',
  tokenId: BigInt(outcome.position_id),
  // BUY 100 shares at 0.5 USDT each
  makerAmount: parseUnits('50', 18),       // 100 × 0.5 = 50 USDT
  takerAmount: SHARES,                     // 100 shares
  expiration: BigInt(Math.floor(Date.now() / 1000) + 3600),
  nonce: await ctfExchange.read.nonces([eoaAddress]),
  feeRateBps: BigInt(market.platformFee),
  side: 0n,                                 // BUY
  signatureType: 0,                         // EOA
};
 
const signature = await walletClient.signTypedData({
  account: eoaAddress,
  domain,
  types: { Order: types.Order },
  primaryType: 'Order',
  message: order,
});
 
// POST /orders
await fetch('/api/orders', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    marketId: market.id,
    outcomeId: outcome.id,
    side: 'buy',
    type: 'limit',
    price: 0.5,
    shares: 100,
    userId: user.id,
    salt: '0x' + order.salt.toString(16),
    signature,
    signerAddress: order.signer,
    makerAddress: order.maker,
    expiration: order.expiration.toString(),
    signatureType: 0,
    tokenId: order.tokenId.toString(),
    makerAmount: order.makerAmount.toString(),
    takerAmount: order.takerAmount.toString(),
    feeRateBps: market.platformFee,
  }),
});

#makerAmount / takerAmount 계산 규칙

모든 금액은 18-decimal wei 정수입니다.

SideMaker 가 내놓는 것Maker 가 받는 것makerAmounttakerAmount
BUYUSDT결과 토큰shares × price (USDT wei)shares (token wei)
SELL결과 토큰USDTshares (token wei)shares × price (USDT wei)
  • price 는 0..1 float 이지만, wei-space 에서는 price × 1e18 로 곱셈에 사용합니다.
  • 모든 곱은 bigint 산술 로 — JS Number 금지 (1 USDT = 10^18 이라 안전한 정수 범위를 넘습니다).

#흔한 실수

이 네 가지를 가장 자주 봅니다
  1. makersigner 에 다른 주소 넣기. EIP-7702 모델에서는 둘 다 사용자의 EOA 주소로 동일해야 합니다.
  2. nonce 캐시. 매번 CTFExchange.nonces(maker) 로 fresh fetch 하세요. incrementNonce() 가 호출되면 직전 nonce 의 모든 미체결 주문이 reverts 됩니다.
  3. price 를 percent 또는 bps 로 보내기. payload 의 price 는 0..1 float 입니다.
  4. makerAmount / takerAmount 를 사람이 읽는 단위로 보내기. 항상 18-dec wei 의 decimal string.

#서명은 체결 시점에 검증됩니다

  • 백엔드는 서명을 검증하지 않습니다 — 서명된 페이로드를 그대로 저장하고 체결 시 체인에 넘깁니다.
  • 잘못된 서명은 체결 시점에 온체인 ECDSA 복원이 다른 주소를 돌려주어 reverts → 해당 주문이 실패로 마크됩니다.
  • 따라서 서명 단계가 정확해야 합니다 — 위의 네 가지 실수를 피하세요.

#다음

이 페이지가 도움이 되었나요?