Skip to content

API 참조

REST APIs, 스마트 계약 인터페이스, GraphQL 쿼리 및 WebSocket 채널을 다루는 CloudBank 예측 시장에 대한 포괄적인 API 문서입니다.


Authentication

사용자 인증(EIP-191 서명)

1. POST /api/v1/auth/nonce        → get challenge nonce
2. sign message with wallet                → EIP-191 personal_sign
3. POST /api/v1/auth/login         → submit signature and obtain JWT
4. Authorization: Bearer {token}   → include in subsequent requests

JWT은(는) HS256로 서명되었습니다. 클레임에는 JWT(지갑 주소), HS256 및 Subject이 포함됩니다.

관리자 인증

  • JWT 전달자: JWT 액세스 권한 획득 + 새로 고침 토큰
  • 기본 인증: 부트스트랩 단계 Authorization: Basic base64(user:pass) 중에 사용됩니다.
  • RBAC: RBAC(전체 권한) / superadmin(제한된 권한)

REST API

기본 URL: URL

빠른 cURL 예(인증/시장/거래/지갑)

bash
export HOST="https://docs-test.cloudbank.to"
export API="$HOST/api/v1"
export ADDRESS="0xYourWalletAddress"

1) 인증: nonce + 로그인

bash
curl -sS -X POST "$API/auth/nonce" \
  -H "Content-Type: application/json" \
  -d "{\"address\":\"$ADDRESS\"}"
json
{
  "nonce": "7fa3f1d9",
  "message": "CloudBank login nonce: 7fa3f1d9",
  "expiresAt": "2026-03-06T00:00:00Z"
}

서명 후 로그인 제출($SIGNATURE$MESSAGE은 실제 지갑 서명 데이터로 대체되어야 함):

bash
export MESSAGE="CloudBank login nonce: 7fa3f1d9"
export SIGNATURE="0xYourEIP191Signature"

curl -sS -X POST "$API/auth/login" \
  -H "Content-Type: application/json" \
  -d "{\"address\":\"$ADDRESS\",\"message\":\"$MESSAGE\",\"signature\":\"$SIGNATURE\"}"
json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "custodialWalletAddress": "0x4D7E...A9f2",
  "expiresAt": "2026-03-06T12:00:00Z"
}
bash
export TOKEN="eyJhbGciOi..."

2) 시장: 시장 목록 조회

bash
curl -sS "$API/markets?state=TRADING&limit=20&offset=0" \
  -H "Authorization: Bearer $TOKEN"
json
{
  "items": [
    {
      "id": "0x2E79B7190c463CD11793520d23F63A3d035A94c2",
      "question": "Will BTC close above $80k on Friday?",
      "state": "TRADING",
      "closeTime": "2026-03-08T12:00:00Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}

3) 거래: 주문 제출

bash
curl -sS -X POST "$API/orderbook/orders" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "marketId":"0x2E79B7190c463CD11793520d23F63A3d035A94c2",
    "outcome":"YES",
    "side":"BUY",
    "orderType":"LIMIT",
    "price":"0.62",
    "amount":"100"
  }'
json
{
  "orderHash": "0x8f7b...c31d",
  "status": "open",
  "filledAmount": "0",
  "createdAt": "2026-03-05T10:00:00Z"
}

4) 지갑: 정보 얻기 + 출금

bash
curl -sS "$API/wallet/info" \
  -H "Authorization: Bearer $TOKEN"
json
{
  "walletAddress": "0x4D7E...A9f2",
  "balances": {
    "BNB": "0.132",
    "USDC": "1250.50"
  },
  "chainId": 97
}
bash
curl -sS -X POST "$API/wallet/withdraw" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "asset":"USDC",
    "toAddress":"0xReceiverAddress",
    "amount":"25.0"
  }'
json
{
  "txHash": "0x4b17...9f25",
  "asset": "USDC",
  "amount": "25.0",
  "status": "pending"
}

Auth

MethodPathAuth비율 제한Description
POSTPOST-100/min/IPEIP-191 챌린지 생성(주소 -> nonce + 메시지)
POSTPOST-5/min/IP서명을 확인하고 JWT + custodialWalletAddress를 반환합니다.

POST POST

json
// Request
{ "address": "0x..." }
// Response
{ "nonce": "abc123", "message": "Sign this message...", "expiresAt": "2026-01-01T00:00:00Z" }

POST POST

json
// Request
{ "address": "0x...", "signature": "0x...", "message": "Sign this message..." }
// Response
{ "token": "eyJ...", "custodialWalletAddress": "0x...", "expiresAt": "2026-01-01T00:00:00Z" }

Wallet

MethodPathAuth비율 제한Description
GETGETJWT100/min관리 지갑 정보 얻기
DELETEDELETEJWT100/min수탁 지갑 삭제(비기능 상태 필요)
POSTPOSTJWT20/min대신 거래 서명(화이트리스트에 포함된 계약)
POSTPOSTJWT3/minBNB/USDC 출금(일일 한도)

POST POST

json
// Request
{ "to": "0x...", "data": "0x...", "value": "0", "product": "predict" }
// Response
{ "txHash": "0x...", "gasMode": "paymaster", "status": "pending", "createdAt": "..." }

POST POST

json
// Request
{ "asset": "USDC", "toAddress": "0x...", "amount": "100.5" }
// Response
{ "txHash": "0x...", "asset": "USDC", "amount": "100.5", "fee": "0", "gasMode": "user", "status": "pending" }

Orderbook

MethodPathAuth비율 제한Description
GETGET-120/min/IP주문서 깊이 얻기
POSTPOSTJWT30/min주문 제출
POSTPOSTJWT30/min하이브리드 경로(AMM + 주문서)
DELETEDELETEJWT30/min주문 취소
GETGETJWT30/min하트비트 상태 가져오기
GETGETJWT-WebSocket 하트비트 채널

GET GET

?marketId={id}&outcomeTokenId={tokenId}&depth=20
json
// Response
{ "market": "0x...", "bids": [{"price": "0.65", "size": "100"}], "asks": [...] }

관계 초대

MethodPathAuth비율 제한Description
GETGET-120/min/IP초대자별 쿼리 관계
POSTPOSTJWT60/min초대 관계 바인딩
GETGETJWT60/min자신의 초대 관계를 얻으십시오

관리자 — 인증

MethodPathAuth비율 제한Description
POSTPOST-30/min/IP관리자 로그인
POSTPOST-30/min/IP새로고침 토큰

관리자 — 사용자(수퍼관리자)

MethodPathAuthDescription
GETGET관리자+SA모든 관리자 나열
GETGET관리자+SA관리자 세부정보 가져오기
POSTPOST관리자+SA관리자 만들기
PATCHPATCH관리자+SA관리자 업데이트
POSTPOST관리자+SA비밀번호 재설정
POSTPOST관리자+SA바인딩 작업 지갑
POSTPOST관리자+SA작업 지갑 바인딩 해제

관리자 — 관리 사용자

MethodPathAuthDescription
GETGETAdmin관리 사용자를 위한 페이지로 매겨진 쿼리
GETGETAdmin사용자 세부정보 + 지갑 가져오기
DELETEDELETESA사용자 지갑 영구 삭제

관리자 — 지갑 운영(수퍼관리자)

MethodPathAuthDescription
GETGETSA목록 작업 지갑
GETGETSA지갑 세부정보 가져오기
POSTPOSTSA운영 지갑 생성
PATCHPATCHSA업데이트 작업 지갑
POSTPOSTAdmin운영 지갑 서명 대리 거래

관리자 — 화이트리스트

MethodPathAuthDescription
POSTPOSTAdmin계약 화이트리스트 추가
GETGETAdmin쿼리 화이트리스트(제품별로 필터링 가능)
PATCHPATCHAdmin화이트리스트 항목 업데이트
DELETEDELETEAdmin화이트리스트 항목 삭제

관리자 — 관계 초대(수퍼관리자)

MethodPathAuthDescription
GETGETSA페이지를 매긴 쿼리(CSV 내보내기 지원)
POSTPOSTSA대량 가져오기(배치당 <=500행)

System

MethodPathAuthDescription
GETGET-상태 확인(SQL/Redis/BSC 상태)
GETGET-프로메테우스 측정항목

오류 코드

모든 오류 응답은 다음 형식을 따릅니다.

json
{ "code": "error_code", "message": "Human-readable message", "details": {} }
CodeHTTP 상태Description
invalid_request400잘못된 요청 매개변수
invalid_signature401EIP-191 서명 확인 실패
invalid_nonce401Nonce가 유효하지 않거나 만료되었습니다.
unauthorized401인증되지 않았거나 토큰이 만료되었습니다.
forbidden403Forbidden
contract_not_whitelisted403계약이 허용 목록에 없습니다.
insufficient_bnb400BNB 잔액 부족
insufficient_balance400잔액 부족
daily_limit_exceeded429일일 출금 한도 초과
not_found404리소스를 찾을 수 없습니다.
rate_limited429비율 제한을 초과했습니다.
invite_binding_conflict409초대 관계 갈등
invite_request_replayed409중복 바인딩 요청
invite_self_not_allowed400자체 초대는 허용되지 않습니다.
internal_error500내부 서버 오류
service_unavailable503서비스 이용 불가

WebSocket

주문장 하트비트

URL: URL 인증: 전달자 JWT(쿼리 매개변수 또는 헤더)

서버 → 클라이언트

json
{ "type": "heartbeat_connected", "status": { ... } }
{ "type": "heartbeat_ack",       "status": { ... } }
{ "type": "error",               "message": "Error description" }

클라이언트 → 서버

  • 모든 메시지는 하트비트 연결 유지를 트리거할 수 있습니다.
  • 표준 WebSocket ping/pong 지원

스마트 계약 인터페이스

OptimisticController

라이프사이클 상태를 관리하는 이진 예측 시장 컨트롤러: TRADING -> PROPOSED -> DISPUTED -> RESOLVED / CANCELLED.

solidity
// Read methods
function getQuestion() external view returns (string memory);
function getMetadata() external view returns (string memory);
function getState() external view returns (MarketState);
function getConditionId() external view returns (bytes32);
function getPositionIds() external view returns (uint256[] memory);
function isActive() external view returns (bool);
function canSettle() external view returns (bool);
function getFeatureFlags() external pure returns (uint256);

// Market management (onlyOwner)
function setMetadata(string calldata metadata) external;
function setOracleAskAfter(uint256 newAskAfter) external;
function depositStake() external;
function proposeOutcome(uint8 outcome, bytes calldata data) external;
function settle(bytes calldata data) external;
function cancelAsInvalid() external;

// Open operations
function disputeOutcome(bytes calldata data) external;
function requestOracleQuestion(bytes calldata data) external payable;
function refundOracleQuestion() external;
function proposeOutcomeFromOracle(bytes calldata data) external;

이벤트:

  • OutcomeProposed(uint8 indexed outcome, address indexed proposer, uint256 timestamp, bytes32 dataHash)
  • OutcomeDisputed(address indexed disputer, uint256 timestamp, bytes32 dataHash)
  • OracleQuestionRequested(bytes32 indexed oracleQuestionId, uint256 deadline, address indexed requester, uint256 value, bytes32 dataHash)
  • StakeProcessed(address indexed creator, address indexed token, uint256 amount, bool refunded)
  • StateTransition(MarketState indexed from, MarketState indexed to)
  • MarketResolved(uint8 indexed outcome, uint256 timestamp)

BinaryCPMM

지속적인 제품 시장 조성자(x*y=k), Gnosis CTF ERC-1155 포지션 토큰을 기준으로 한 수수료 0%.

solidity
// Read methods
function getReserves() external view returns (uint256 yesReserves, uint256 noReserves);
function getPrice(uint256 tokenId) external view returns (uint256 price1e18);
function quote(uint256 tokenIn, uint256 amountIn) public view returns (uint256 amountOut);

// Liquidity
function addLiquidity(uint256 collateralAmount) external returns (uint256 lpOut);
function removeLiquidity(uint256 lpIn) external returns (uint256 yesOut, uint256 noOut);

// Trading
function swap(uint256 tokenIn, uint256 amountIn, uint256 minOut) external returns (uint256 amountOut);
function swapCollateralForOutcome(uint256 outcomeTokenId, uint256 collateralAmount, uint256 minOutcomeOut) external returns (uint256 outcomeOut);
function sellOutcomeForCollateral(uint256 outcomeTokenId, uint256 outcomeIn, uint256 minCollateralOut) external returns (uint256 collateralOut);
function sellAllOutcomeForCollateral(uint256 outcomeTokenId, uint256 minCollateralOut) external returns (uint256 collateralOut);

이벤트:

  • LiquidityAdded(address indexed provider, uint256 collateralIn, uint256 lpOut)
  • LiquidityRemoved(address indexed provider, uint256 lpIn, uint256 yesOut, uint256 noOut)
  • Swapped(address indexed trader, uint256 indexed tokenIn, uint256 amountIn, uint256 indexed tokenOut, uint256 amountOut)
  • CollateralSwappedForOutcome(address indexed trader, uint256 collateralIn, uint256 indexed outcomeTokenId, uint256 outcomeOut)
  • OutcomeSoldForCollateral(address indexed trader, uint256 indexed outcomeTokenId, uint256 outcomeIn, uint256 collateralOut)

DCPPFactory

EIP-1167 최소 프록시를 통해 시장 + AMM을 배포하는 시장 공장.

solidity
// Create market
function createOptimisticMarket(string calldata question, uint256 livenessPeriod, address, uint256, uint8 outcomeSlots) external returns (address market);
function createOptimisticMarket_V2(string calldata question, uint256 livenessPeriod, address, uint256, uint8 outcomeSlots) external returns (address market);
function createOptimisticMarketWithInitialLiquidity(...) external returns (address market, address amm);
function createOptimisticMarketWithInitialLiquidity_V2(...) external returns (address market, address amm);
function createOptimisticMarket_V3(string calldata question, string calldata metadata, ...) public returns (address market, address amm);
function createOptimisticMarket_V4(..., bytes32 key) external returns (address market, address amm);

// Read methods
function getMarketCount() external view returns (uint256);
function getMarketByIndex(uint256 idx) external view returns (address);

// Management (onlyOwner)
function setOracleAdapter(IYesNoOracleAdapter newAdapter) external;
function setMultiOutcomeFactory(address newFactory) external;

MultiOutcomeMarket

N개의 독립적인 바이너리 시장(1대 나머지 모드)을 구성하는 다중 옵션 시장 래퍼입니다.

solidity
function optionCount() external view returns (uint256);
function getOption(uint256 index) external view returns (string label, address market, address amm, uint256 yesTokenId, uint256 noTokenId);
function proposeWinner(uint8 winnerIndex, bytes calldata data) external;  // onlyOwner
function settleWinner(uint8 winnerIndex) external;  // onlyOwner

// Oracle flow
function requestWinnerFromOracle(uint256 deadline, bytes calldata data) external payable returns (bytes32 qid);
function proposeWinnerFromOracle(bytes calldata data) external;
function settleWinnerFromOracle() external;

SoraOracle

지정된 공급자가 응답한 최소 Yes/No oracle입니다.

solidity
function askYesNoQuestion(string calldata question, uint256 deadline) external payable returns (uint256 questionId);
function provideAnswer(uint256 questionId, bool boolAnswer, uint8 confidenceScore, string calldata dataSource) external;  // onlyOracleProvider
function getQuestionWithAnswer(uint256 questionId) external view returns (string, uint256, string, bool, AnswerStatus, uint256);
function refundUnansweredQuestion(uint256 questionId) external;

CloudBankVerifyingPaymaster

ERC-4337 AA 서명 확인, 일일 할당량 및 보낸 사람 허용 목록이 포함된 Paymaster.

solidity
function validatePaymasterUserOp(UserOperation calldata userOp, bytes32, uint256 maxCost) external returns (bytes memory context, uint256 validationData);
function postOp(PostOpMode, bytes calldata context, uint256 actualGasCost) external;
function getHash(UserOperation calldata userOp, uint48 validUntil, uint48 validAfter) public view returns (bytes32);

// Management (onlyOwner)
function setVerifyingSigner(address newSigner) external;
function setEnforceSenderAllowlist(bool enabled) external;
function setDailySponsorLimit(uint256 newLimit) external;
function setSenderAllowlist(address sender, bool allowed) external;
function batchSetSenderAllowlist(address[] calldata senders, bool allowed) external;
function deposit() external payable;

FactoryRegistry

현재 공장 주소에 대한 안정적인 포인터를 제공하는 공장 레지스트리입니다.

solidity
function factory() external view returns (address);
function setFactory(address newFactory) external;  // onlyOwner

주요 인터페이스

solidity
// IMarket
interface IMarket {
    function getQuestion() external view returns (string memory);
    function getState() external view returns (MarketState);
    function getConditionId() external view returns (bytes32);
    function isActive() external view returns (bool);
    function canSettle() external view returns (bool);
    function settle(bytes calldata data) external;
}

// IYesNoOracleAdapter
interface IYesNoOracleAdapter {
    function ask(string calldata question, uint256 deadline, bytes calldata data) external payable returns (bytes32);
    function getAnswer(bytes32 oracleQuestionId) external view returns (bool finalized, uint8 outcome);
    function refund(bytes32 oracleQuestionId) external returns (uint256 refunded);
}

// IConditionalTokens (Gnosis CTF)
interface IConditionalTokens {
    function prepareCondition(address oracle, bytes32 questionId, uint256 outcomeSlotCount) external;
    function reportPayouts(bytes32 questionId, uint256[] calldata payouts) external;
    function splitPosition(IERC20 collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint256[] calldata partition, uint256 amount) external;
    function mergePositions(IERC20 collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint256[] calldata partition, uint256 amount) external;
    function redeemPositions(IERC20 collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint256[] calldata indexSets) external;
}

GraphQL (더 그래프/골드스키)

종료점: https://api.goldsky.com/api/public/project_{id}/subgraphs/cloudbank-bsc-testnet/{version}/gn

스키마 엔터티

Market

graphql
type Market @entity {
  id: ID!                        # Market contract address
  question: String!              # Market question
  conditionId: Bytes!            # CTF condition ID
  state: Int!                    # 0=TRADING, 1=PROPOSED, 2=RESOLVED
  amm: Bytes                     # AMM contract address
  yesTokenId: BigInt             # YES token ID (ERC-1155)
  noTokenId: BigInt              # NO token ID (ERC-1155)
  creator: Bytes!                # Creator address
  createdAtTimestamp: BigInt!
  totalVolume: BigDecimal!       # Total volume (USDC)
  metadataUri: String            # IPFS metadata URI
  category: String               # Market category (parsed from IPFS)
  isMultiOutcome: Boolean!       # Is multi-outcome market
  multiOutcomeMarket: MultiOutcomeMarket
  optionIndex: Int               # Multi-outcome option index
  optionLabel: String            # Multi-outcome option label
  trades: [Trade!]! @derivedFrom(field: "market")
}

Trade

graphql
type Trade @entity {
  id: ID!                        # txHash-logIndex
  market: Market!
  amm: Bytes!
  trader: Bytes!
  user: User!
  type: String!                  # "buy" | "sell"
  side: String!                  # "yes" | "no"
  amountIn: BigInt!
  amountOut: BigInt!
  price: BigDecimal!             # amountIn / amountOut
  outcomeTokenId: BigInt!
  timestamp: BigInt!
  blockNumber: BigInt!
  txHash: Bytes!
}

User

graphql
type User @entity {
  id: ID!                        # Address (lowercase)
  totalVolume: BigDecimal!
  realizedPnL: BigDecimal!
  totalTrades: Int!
  totalBuys: Int!
  totalSells: Int!
  totalProfitableSells: Int!
  firstTradeTimestamp: BigInt!
  lastTradeTimestamp: BigInt!
  trades: [Trade!]! @derivedFrom(field: "user")
  positions: [UserPosition!]! @derivedFrom(field: "user")
}

UserPosition

graphql
type UserPosition @entity {
  id: ID!                        # user-market-side
  user: User!
  market: Market!
  side: String!                  # "yes" | "no"
  shares: BigDecimal!            # Current holdings
  totalBought: BigDecimal!
  totalSold: BigDecimal!
  costBasis: BigDecimal!         # Cost basis
  proceeds: BigDecimal!          # Cumulative proceeds
  isOpen: Boolean!
  openedAtTimestamp: BigInt!
  lastTradeTimestamp: BigInt!
}

MultiOutcomeMarket

graphql
type MultiOutcomeMarket @entity {
  id: ID!
  question: String!
  metadataUri: String
  creator: Bytes!
  optionCount: Int!
  options: [Market!]! @derivedFrom(field: "multiOutcomeMarket")
  createdAtTimestamp: BigInt!
}

일반적인 쿼리

모든 시장 보기

graphql
query GetMarkets {
  markets(orderBy: createdAtTimestamp, orderDirection: desc) {
    id, question, state, amm, yesTokenId, noTokenId, totalVolume, category
    trades(first: 1, orderBy: timestamp, orderDirection: desc) { price, side }
  }
}

시장 거래 받기

graphql
query GetMarketTrades($marketId: ID!) {
  trades(where: { market: $marketId }, orderBy: timestamp, orderDirection: desc, first: 100) {
    id, trader, type, side, amountIn, amountOut, price, timestamp, txHash
  }
}

사용자 위치 가져오기

graphql
query GetUserPositions($user: ID!) {
  user(id: $user) {
    positions(where: { isOpen: true }) {
      side, shares, costBasis, proceeds
      market { id, question, state }
    }
  }
}

리더보드(볼륨별)

graphql
query GetTopTraders($first: Int!) {
  users(orderBy: totalVolume, orderDirection: desc, first: $first, where: { totalTrades_gt: 0 }) {
    id, totalVolume, realizedPnL, totalTrades, totalProfitableSells
  }
}

다중 결과 시장 확보

graphql
query GetMultiOutcomeMarket($id: ID!) {
  multiOutcomeMarket(id: $id) {
    id, question, optionCount
    options(orderBy: optionIndex) {
      id, optionLabel, state, amm, yesTokenId, noTokenId, totalVolume
    }
  }
}

인덱싱된 이벤트

그래프 하위 그래프는 다음 온체인 이벤트를 수신합니다.

ContractEventHandlerEntity
DCPPFactoryDCPPFactoryDCPPFactoryMarket
DCPPFactoryDCPPFactoryDCPPFactoryMarket
MultiOutcomeMarketFactoryMultiOutcomeMarketFactoryMultiOutcomeMarketFactoryMultiOutcomeMarket
MultiOutcomeMarketFactoryMultiOutcomeMarketFactoryMultiOutcomeMarketFactoryMarket
BinaryCPMM(템플릿)BinaryCPMMBinaryCPMM거래, 사용자, UserPosition
BinaryCPMM(템플릿)BinaryCPMMBinaryCPMM거래, 사용자, UserPosition
BinaryCPMM(템플릿)BinaryCPMMBinaryCPMM거래, 사용자, UserPosition

계약 주소(BSC 테스트넷)

ContractAddress
FactoryRegistryFactoryRegistry
DCPPFactoryDCPPFactory
MultiOutcomeMarketFactoryMultiOutcomeMarketFactory
ConditionalTokens (CTF)ConditionalTokens
담보 토큰(USDC)USDC

속도 제한

사용자 ID / 관리자 ID / IP별로 분류된 슬라이딩 1분 창을 갖춘 Redis 기반 토큰 버킷 전략입니다.

엔드포인트 카테고리LimitIdentifier
인증 임시값100/minIP
인증 로그인5/minIP
지갑 정보100/minUser
TX에 서명하세요20/minUser
Withdraw3/minUser
바인드 초대60/minUser
주문장 읽기120/minIP
주문서 작성30/minUser
관리자 인증30/minIP
관리 작전120/minAdmin
관리자 로그인30/minAdmin

초과하면 429 Too Many Requests + Retry-After 헤더를 반환합니다.