@waterx/predict-sdk
v0.5.1
Published
WaterX prediction market SDK
Readme
@waterx/predict-sdk
TypeScript SDK for waterx_prediction, the WaterX prediction market broker on Sui.
PredictClient— gRPC,simulate,signAndExecuteTransaction.- PTB builders — add Move calls to a
@mysten/suiTransaction(role-specific; see below). - View helpers — read on-chain state via
simulate+ BCS (getRegistry,getOrder,getAccountIds, …). - Generated Move wrappers —
src/generated(pnpm codegen). - Grouped imports —
@waterx/predict-sdk/user,@waterx/predict-sdk/user/keeper, etc.
Who uses what
Three integrator roles. Pick the import path that matches your service; all PTBs still need PredictClient + a signed Transaction.
| Role | You build | Import from |
| -------------------------------- | ----------------------------------------- | --------------------------------------------------- |
| Client (wallet / trading UI) | Accounts, deposit, orders, positions | @waterx/predict-sdk or @waterx/predict-sdk/user |
| Keeper (broker backend) | Fill, cancel, close, resolve, force-claim | @waterx/predict-sdk/user/keeper (or root package) |
| Admin (protocol ops) | Registry, pause, keeper list, settlement | @waterx/predict-sdk/user/admin (or root package) |
Anyone can use view helpers from the root package (no private key required for simulate reads).
Client (end user)
Typical flow: createAccount → deposit → placeOrder → requestClose / claim / self-cancel.
| Step | Functions |
| -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Account | createAccount, deposit (or requestDeposit + consumeDepositDirect), requestWithdraw, delegates |
| Trade | placeOrder, selfCancelOrder |
| Position | requestClose, selfCancelClose, claim, batchClaim |
| Read | getAccountIds, getAccountData, getAccountOrderIds, getAccountPositionIds, getOrder, getPosition, getMarketById |
import {
createAccount,
deposit,
getAccountIds,
placeOrder,
PredictClient,
} from "@waterx/predict-sdk";
// or: import * as user from "@waterx/predict-sdk/user";accountId in PTBs and views is the registry account id (0x2::object::ID from createAccount / AccountCreated event), not necessarily the Suiscan “Account object” address. Use getAccountIds(client, { owner }) to list ids for a wallet.
Keeper (broker)
Signer must be registered in global_config keepers (isKeeper, getKeeperAddresses). Uses bucket_framework::account::request as the keeper identity in each PTB.
| Action | Functions |
| -------------- | ------------------------------------------------------------------------------------ |
| Orders | fillOrder, cancelOrder |
| Close pipeline | confirmClose, cancelClose |
| Settlement | forceClaim, batchForceClaim, buildBatchForceClaimTransactions, resolveMarket |
| Read | getOrder, getOrderCursor, getRegistry, isKeeper |
import { cancelOrder, confirmClose, fillOrder, PredictClient } from "@waterx/predict-sdk";
// recommended namespace:
import * as keeper from "@waterx/predict-sdk/user/keeper";Keeper PTBs do not live under @waterx/predict-sdk/user (the user barrel is client-only).
After resolveMarket, batch-settle open positions with buildBatchForceClaimTransactions. It splits
positionIds into unsigned PTBs (default DEFAULT_FORCE_CLAIM_CHUNK_SIZE = 1000 claims per tx —
one account::request plus up to 1000 force_claim calls, under Sui’s ~1024 command limit). Override
with chunkSize when gas or tx size requires smaller batches. Use batchForceClaim instead when
appending claims to an existing Transaction (e.g. resolve + claim in one PTB).
import {
buildBatchForceClaimTransactions,
DEFAULT_FORCE_CLAIM_CHUNK_SIZE,
PredictClient,
} from "@waterx/predict-sdk";
const txs = buildBatchForceClaimTransactions(client, { positionIds });
// txs.length === 1 when positionIds.length <= DEFAULT_FORCE_CLAIM_CHUNK_SIZE
for (const tx of txs) {
await client.signAndExecuteTransaction({ signer: keeper, transaction: tx });
}Admin (protocol)
Requires AdminCap and deployment-specific object ids from config.
| Area | Functions |
| ----------------------- | ------------------------------------------------------------------------------------------------ |
| Registry | createMarketRegistry, depositSettlement, adminWithdraw |
| Markets | pauseMarket, unpauseMarket, setMinReserve, setOrderCancelCooldownMs |
| Keepers | addKeeper, removeKeeper |
| Account protocol access | whitelistPredictionProtocol, allowPredictionProtocolAsset, disallowPredictionProtocolAsset |
| Special | adminPlaceOrderFor |
import * as admin from "@waterx/predict-sdk/user/admin";Quick start (client placeOrder)
import { Transaction } from "@mysten/sui/transactions";
import { placeOrder, PredictClient } from "@waterx/predict-sdk";
const client = await PredictClient.create("TESTNET", { cache: true });
const tx = new Transaction();
placeOrder(client, tx, {
accountId: "0x...", // registry account id
maxSpend: 100_000_000n,
marketId: "0x...",
selection: "YES",
minShares: 50_000_000n,
priceCapBps: 7_000n,
expiryTs: 9_999_999_999_999n,
});Testnet package, shared object, registry, and settlement coin defaults are fetched from WaterX config:
WaterXProtocol/waterx-config.
client.config is the parsed canonical JSON from that repo. Use the client accessors when you need
the active IDs:
const client = await PredictClient.testnet({ cache: true });
client.packageId(); // packages.waterx_prediction.published_at
client.globalConfigId(); // packages.waterx_prediction.global_config
client.marketRegistry(); // packages.waterx_prediction.market_registries.USD
client.settlementCoinType(); // packages.waterx_prediction.settlement_coin_types.USDFor staging deployments, publish a branch or mirror of waterx-config and pass configUrl.
PredictClient.mainnet() is kept as an interface, but it requires an explicit compatible
configUrl until prediction mainnet config is published in waterx-config:
const client = await PredictClient.mainnet({
configUrl: "https://example.com/prediction-mainnet.json",
});Input validation
- PTB helpers use strict literals for
Selection/Outcome("YES","NO","INVALID"). On-chain view decoding accepts Move enum casing (for example"Yes"). normalizeMarketId()is for PTBvector<u8>arguments. Strings starting with0xare parsed as hex; other strings are UTF-8. View decoding expectsUint8Arrayornumber[]formarket_id, not strings.toBigInt()rejects empty strings, non-integers, negatives, and values aboveu64::MAX.
Development
pnpm codegen
pnpm typecheck
pnpm build
pnpm test:unitTesting
Tests live under tests/unit (offline, CI-default), tests/e2e (Sui testnet dry-run simulate — no signer required), and tests/integration (sign + execute, opt-in — SUI_PRIVATE_KEY in the process environment). Test files are split by indexer event family — see tests/COVERAGE.md for the full event ↔ test ↔ required-key matrix.
CI runs pnpm test:unit and pnpm test:e2e on every pull request (public testnet RPC only; no secrets).
pnpm test:unit # unit tests (same as `pnpm test`)
pnpm test:e2e # testnet simulate smoke (needs network)
pnpm test:integration # on-chain integration flows (needs SUI_PRIVATE_KEY + gas / assets)
pnpm test:all # run every Vitest project
pnpm test:unit --coverage # unit + v8 coverage (e2e: `pnpm test:e2e --coverage`)Optional: copy .env.example to .env for local integration (SUI_PRIVATE_KEY) or to override discovery via E2E_* env vars. E2E / CI do not require a .env file.
Seed testnet fixtures (optional)
pnpm seed:testnet builds on-chain pre-conditions so the E2E suite stops skipping order / position / claim tests. Each stage is idempotent — re-running reuses existing on-chain state when present.
sui keytool generate ed25519 # test-only key
cp .env.example .env # SUI_PRIVATE_KEY=suiprivkey1...
# Fund: testnet SUI (faucet). USD optional — seed/integration PSM-mint from MOCK_USDC when wallet has no settlement coin.
pnpm seed:testnet # preset=baseline (owner-only stages + fill/request-close if keeper)
pnpm seed:testnet -- --preset=with-claim # baseline + resolved claim market (needs keeper)
pnpm seed:testnet -- --preset=admin # admin round-trips (AdminCap holder only)
pnpm seed:testnet -- --stage=account,deposit,place-open
pnpm seed:testnet -- --dry-run # plan onlySee scripts/README.md for the full stage / preset table and tests/COVERAGE.md for which stages unblock which tests.
pnpm codegen regenerates src/generated from:
../waterx-contract/waterx_prediction../waterx-contract/waterx_account../waterx-contract/bucket_framework
