API 參考
CloudBank 預測市場的全面 API 文檔,涵蓋 REST APIs、智能合約介面、GraphQL 查詢和 WebSocket 通道。
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 requestsJWT 與 HS256 簽署。聲明包括 JWT(錢包位址)、HS256 和 Subject。
管理員認證
- JWT 承載:JWT 取得存取權限 + 刷新令牌
- 基本驗證:在引導階段
Authorization: Basic base64(user:pass)期間使用 - RBAC:RBAC(完全權限)/
superadmin(有限權限)
REST API
基礎 URL: URL
快速 cURL 範例(身份驗證/市場/交易/錢包)
export HOST="https://docs-test.cloudbank.to"
export API="$HOST/api/v1"
export ADDRESS="0xYourWalletAddress"1)驗證:隨機數+登入
curl -sS -X POST "$API/auth/nonce" \
-H "Content-Type: application/json" \
-d "{\"address\":\"$ADDRESS\"}"{
"nonce": "7fa3f1d9",
"message": "CloudBank login nonce: 7fa3f1d9",
"expiresAt": "2026-03-06T00:00:00Z"
}簽署後,提交登入($SIGNATURE和$MESSAGE必須替換為實際的錢包簽署資料):
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\"}"{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"custodialWalletAddress": "0x4D7E...A9f2",
"expiresAt": "2026-03-06T12:00:00Z"
}export TOKEN="eyJhbGciOi..."2)市場:查詢市場列表
curl -sS "$API/markets?state=TRADING&limit=20&offset=0" \
-H "Authorization: Bearer $TOKEN"{
"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)交易:提交訂單
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"
}'{
"orderHash": "0x8f7b...c31d",
"status": "open",
"filledAmount": "0",
"createdAt": "2026-03-05T10:00:00Z"
}4)皮夾:獲取資訊+提現
curl -sS "$API/wallet/info" \
-H "Authorization: Bearer $TOKEN"{
"walletAddress": "0x4D7E...A9f2",
"balances": {
"BNB": "0.132",
"USDC": "1250.50"
},
"chainId": 97
}curl -sS -X POST "$API/wallet/withdraw" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"asset":"USDC",
"toAddress":"0xReceiverAddress",
"amount":"25.0"
}'{
"txHash": "0x4b17...9f25",
"asset": "USDC",
"amount": "25.0",
"status": "pending"
}Auth
| Method | Path | Auth | 速率限制 | Description |
|---|---|---|---|---|
| POST | POST | - | 100/min/IP | 產生 EIP-191 挑戰(位址 -> 隨機數 + 訊息) |
| POST | POST | - | 5/min/IP | 驗證簽名並傳回 JWT + custodialWalletAddress |
POST POST
// Request
{ "address": "0x..." }
// Response
{ "nonce": "abc123", "message": "Sign this message...", "expiresAt": "2026-01-01T00:00:00Z" }POST POST
// Request
{ "address": "0x...", "signature": "0x...", "message": "Sign this message..." }
// Response
{ "token": "eyJ...", "custodialWalletAddress": "0x...", "expiresAt": "2026-01-01T00:00:00Z" }Wallet
| Method | Path | Auth | 速率限制 | Description |
|---|---|---|---|---|
| GET | GET | JWT | 100/min | 獲取託管錢包信息 |
| DELETE | DELETE | JWT | 100/min | 刪除託管錢包(需要非功能狀態) |
| POST | POST | JWT | 20/min | 代簽交易(白名單合約) |
| POST | POST | JWT | 3/min | 提現BNB/USDC(每日限額) |
POST POST
// Request
{ "to": "0x...", "data": "0x...", "value": "0", "product": "predict" }
// Response
{ "txHash": "0x...", "gasMode": "paymaster", "status": "pending", "createdAt": "..." }POST POST
// Request
{ "asset": "USDC", "toAddress": "0x...", "amount": "100.5" }
// Response
{ "txHash": "0x...", "asset": "USDC", "amount": "100.5", "fee": "0", "gasMode": "user", "status": "pending" }Orderbook
| Method | Path | Auth | 速率限制 | Description |
|---|---|---|---|---|
| GET | GET | - | 120/min/IP | 取得訂單深度 |
| POST | POST | JWT | 30/min | 提交訂單 |
| POST | POST | JWT | 30/min | 混合路線(AMM + 訂單簿) |
| DELETE | DELETE | JWT | 30/min | 取消訂單 |
| GET | GET | JWT | 30/min | 取得心跳狀態 |
| GET | GET | JWT | - | WebSocket 心跳通道 |
GET GET
?marketId={id}&outcomeTokenId={tokenId}&depth=20// Response
{ "market": "0x...", "bids": [{"price": "0.65", "size": "100"}], "asks": [...] }邀請關係
| Method | Path | Auth | 速率限制 | Description |
|---|---|---|---|---|
| GET | GET | - | 120/min/IP | 按邀請人查詢關係 |
| POST | POST | JWT | 60/min | 綁定邀請關係 |
| GET | GET | JWT | 60/min | 獲得自己的邀請關係 |
管理員——授權
| Method | Path | Auth | 速率限制 | Description |
|---|---|---|---|---|
| POST | POST | - | 30/min/IP | 管理員登入 |
| POST | POST | - | 30/min/IP | 刷新令牌 |
管理員 — 使用者(超級管理員)
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | GET | 管理員+SA | 列出所有管理員 |
| GET | GET | 管理員+SA | 獲取管理員詳細信息 |
| POST | POST | 管理員+SA | 建立管理員 |
| PATCH | PATCH | 管理員+SA | 更新管理員 |
| POST | POST | 管理員+SA | 重設密碼 |
| POST | POST | 管理員+SA | 綁定操作錢包 |
| POST | POST | 管理員+SA | 解綁操作錢包 |
管理員 — 保管用戶
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | GET | Admin | 託管使用者的分頁查詢 |
| GET | GET | Admin | 取得用戶詳細資訊+錢包 |
| DELETE | DELETE | SA | 永久刪除用戶錢包 |
管理員 — 操作錢包(超級管理員)
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | GET | SA | 列出操作錢包 |
| GET | GET | SA | 獲取錢包詳細信息 |
| POST | POST | SA | 創建操作錢包 |
| PATCH | PATCH | SA | 更新操作錢包 |
| POST | POST | Admin | 操作錢包代簽交易 |
管理員 — 白名單
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | POST | Admin | 新增合約白名單 |
| GET | GET | Admin | 查詢白名單(可依產品過濾) |
| PATCH | PATCH | Admin | 更新白名單條目 |
| DELETE | DELETE | Admin | 刪除白名單條目 |
管理員 — 邀請關係(超級管理員)
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | GET | SA | 分頁查詢(支援CSV匯出) |
| POST | POST | SA | 批次匯入(每批 <=500 行) |
System
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | GET | - | 健康檢查(SQL/Redis/BSC 狀態) |
| GET | GET | - | 普羅米修斯指標 |
錯誤代碼
所有錯誤回應都遵循以下格式:
{ "code": "error_code", "message": "Human-readable message", "details": {} }| Code | HTTP 狀態 | Description |
|---|---|---|
invalid_request | 400 | 請求參數無效 |
invalid_signature | 401 | EIP-191 簽章驗證失敗 |
invalid_nonce | 401 | Nonce 無效或已過期 |
unauthorized | 401 | 未經過身份驗證或令牌已過期 |
forbidden | 403 | Forbidden |
contract_not_whitelisted | 403 | 合約未列入白名單 |
insufficient_bnb | 400 | BNB 餘額不足 |
insufficient_balance | 400 | 餘額不足 |
daily_limit_exceeded | 429 | 超出每日提款限額 |
not_found | 404 | 找不到資源 |
rate_limited | 429 | 超出速率限制 |
invite_binding_conflict | 409 | 邀請關係衝突 |
invite_request_replayed | 409 | 重複的綁定請求 |
invite_self_not_allowed | 400 | 不允許自行邀請 |
internal_error | 500 | 伺服器內部錯誤 |
service_unavailable | 503 | 暫停服務 |
WebSocket
訂單心跳
URL:URL Auth:承載 JWT (查詢參數或標頭)
伺服器→客戶端
{ "type": "heartbeat_connected", "status": { ... } }
{ "type": "heartbeat_ack", "status": { ... } }
{ "type": "error", "message": "Error description" }客戶端→伺服器
- 任何訊息都可以觸發心跳保活
- 支持標準 WebSocket ping/pong
智能合約介面
OptimisticController
管理生命週期狀態的二元預測市場控制器:TRADING -> PROPOSED -> DISPUTED -> RESOLVED / CANCELLED。
// 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),0% 費用,基於 Gnosis CTF ERC-1155 持倉代幣。
// 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 的市場工廠。
// 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 個獨立二元市場的多選項市場包裝器(一對一模式)。
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 預言機,由指定提供者應答。
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 具有簽名驗證、每日配額和寄件者白名單。
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
工廠註冊表,提供指向目前工廠地址的穩定指針。
function factory() external view returns (address);
function setFactory(address newFactory) external; // onlyOwner關鍵介面
// 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(圖表/Goldsky)
端點:https://api.goldsky.com/api/public/project_{id}/subgraphs/cloudbank-bsc-testnet/{version}/gn
模式實體
Market
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
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
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
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
type MultiOutcomeMarket @entity {
id: ID!
question: String!
metadataUri: String
creator: Bytes!
optionCount: Int!
options: [Market!]! @derivedFrom(field: "multiOutcomeMarket")
createdAtTimestamp: BigInt!
}常見查詢
取得所有市場
query GetMarkets {
markets(orderBy: createdAtTimestamp, orderDirection: desc) {
id, question, state, amm, yesTokenId, noTokenId, totalVolume, category
trades(first: 1, orderBy: timestamp, orderDirection: desc) { price, side }
}
}取得市場交易
query GetMarketTrades($marketId: ID!) {
trades(where: { market: $marketId }, orderBy: timestamp, orderDirection: desc, first: 100) {
id, trader, type, side, amountIn, amountOut, price, timestamp, txHash
}
}取得使用者位置
query GetUserPositions($user: ID!) {
user(id: $user) {
positions(where: { isOpen: true }) {
side, shares, costBasis, proceeds
market { id, question, state }
}
}
}排行榜(按數量)
query GetTopTraders($first: Int!) {
users(orderBy: totalVolume, orderDirection: desc, first: $first, where: { totalTrades_gt: 0 }) {
id, totalVolume, realizedPnL, totalTrades, totalProfitableSells
}
}獲得多結果市場
query GetMultiOutcomeMarket($id: ID!) {
multiOutcomeMarket(id: $id) {
id, question, optionCount
options(orderBy: optionIndex) {
id, optionLabel, state, amm, yesTokenId, noTokenId, totalVolume
}
}
}索引事件
Graph 子圖監聽以下鏈上事件:
| Contract | Event | Handler | Entity |
|---|---|---|---|
| DCPPFactory | DCPPFactory | DCPPFactory | Market |
| DCPPFactory | DCPPFactory | DCPPFactory | Market |
| MultiOutcomeMarketFactory | MultiOutcomeMarketFactory | MultiOutcomeMarketFactory | MultiOutcomeMarket |
| MultiOutcomeMarketFactory | MultiOutcomeMarketFactory | MultiOutcomeMarketFactory | Market |
| BinaryCPMM(模板) | BinaryCPMM | BinaryCPMM | 貿易,用戶,UserPosition |
| BinaryCPMM(模板) | BinaryCPMM | BinaryCPMM | 貿易,用戶,UserPosition |
| BinaryCPMM(模板) | BinaryCPMM | BinaryCPMM | 貿易,用戶,UserPosition |
合約位址(BSC 測試網)
| Contract | Address |
|---|---|
| FactoryRegistry | FactoryRegistry |
| DCPPFactory | DCPPFactory |
| MultiOutcomeMarketFactory | MultiOutcomeMarketFactory |
| ConditionalTokens (CTF) | ConditionalTokens |
| 抵押代幣 (USDC) | USDC |
速率限制
基於 Redis 的令牌桶策略,具有滑動 1 分鐘窗口,按用戶 ID / 管理員 ID / IP 進行分段。
| 端點類別 | Limit | Identifier |
|---|---|---|
| 驗證隨機數 | 100/min | IP |
| 授權登入 | 5/min | IP |
| 錢包資訊 | 100/min | User |
| 簽署交易 | 20/min | User |
| Withdraw | 3/min | User |
| 邀請綁定 | 60/min | User |
| 已讀訂單簿 | 120/min | IP |
| 訂單簿寫入 | 30/min | User |
| 管理員授權 | 30/min | IP |
| 管理員操作 | 120/min | Admin |
| 管理標誌 | 30/min | Admin |
超出時,返回 429 Too Many Requests + Retry-After 標頭。