TypeScript SDK
The target structure for @sdk.markets/sdk.
@sdk.markets/sdk is the core package. It owns:
- domain types
- platform client configuration
- viem client integration
- direct onchain market actions
- hosted flow helpers
- async job helpers
- typed event watching
It should be usable from:
- browser apps
- React Native Expo apps
- backend jobs
- scripts
- agents
- tests
Integration modes
The SDK is meant to support multiple integration styles over time:
- hosted platform sessions for CLI, apps, and managed execution
- browser and mobile wallet clients for direct user-owned execution
- backend wallet providers
- server-to-server integrations where an app backend owns auth, policy, and transaction execution
Current hosted package surface
The current package exposes a hosted API client that mirrors the CLI's hosted mode. React Native Expo apps should use the hosted-only subpath so they do not pull direct onchain helpers into the mobile bundle:
import { createHostedClient } from "@sdk.markets/sdk/hosted";
import { toHostedCreateMarketInput } from "@sdk.markets/sdk/inputs";
const client = createHostedClient({
baseUrl: "https://platform.markets",
projectKey,
actorToken,
});@sdk.markets/sdk/hosted includes:
createHostedClient- hosted auth helpers:
startLogin,pollLogin,whoami,bootstrap - hosted account helpers:
wallet,balance - hosted market helpers:
create,createAndWait,parseCloseTime - hosted job helpers:
jobs.get
@sdk.markets/sdk/inputs includes the shared input conversion used by the CLI:
const input = toHostedCreateMarketInput({
question: "Will our Expo app create a market?",
options: ["Yes", "No"],
closeTime: "tomorrow 8 pm",
resolutionMode: "AiOracle",
resolutionSources: ["https://example.com/source"],
});
const market = await client.markets.createAndWait(input);The input helpers validate question/options, close time, admins, quorum, creator fees, and AI oracle sources before producing the hosted API payload.
Backend wallet execution
Apps that already route user actions through their own backend can keep wallet execution there.
This is the server-to-server integration path. In this setup the client app does not need a
platform actorToken; the backend authenticates the user, applies product policy, chooses the
wallet, and sends the transaction with its own wallet provider.
Use the direct Markets client to prepare the transaction and parse the receipt while your
backend wallet provider performs the send:
import { Markets, toCreateMarketInput } from "@sdk.markets/sdk";
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";
const markets = new Markets({
chain: baseSepolia,
publicClient: createPublicClient({
chain: baseSepolia,
transport: http(process.env.BASE_SEPOLIA_RPC_URL),
}),
});
const input = toCreateMarketInput(payload, userWalletAddress);
const request = markets.prepareCreateTransaction(input, userWalletAddress);
const hash = await sendWithBackendWallet(request);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
const marketAddress = markets.parseCreateReceipt(receipt);If the backend exposes a generic sender, createWithExecutor can wrap the full flow:
const result = await markets.createWithExecutor(input, {
account: userWalletAddress,
sendTransaction: sendWithBackendWallet,
});This mode is separate from hosted platform sessions. Hosted mode uses sdk.markets auth and hosted execution. Server-to-server mode lets an existing backend keep its own auth, wallet selection, policy checks, signing provider, and transaction submission while still using the SDK for market types, calldata, and receipt parsing.
Top-level client
import { MarketsClient } from "@sdk.markets/sdk";
const client = new MarketsClient({
environment: "sandbox",
apiKey: process.env.NEXT_PUBLIC_MARKETS_API_KEY!,
});MarketsClient is the top-level facade over the lower-level hosted and direct clients. Hosted
methods use the project API key plus the actor session. Direct methods use viem clients when you
opt into explicit chain access. Server-to-server methods can prepare calldata or use an executor
provided by your backend wallet provider.
For direct reads and writes, the same client can also be configured with viem clients:
const directClient = new MarketsClient({
environment: "sandbox",
apiKey: process.env.NEXT_PUBLIC_MARKETS_API_KEY!,
publicClient,
walletClient,
});Modules
client.markets
Hosted market creation plus parimutuel reads and lifecycle actions.
Current surface:
client.markets.get(address)
client.markets.getMany(addresses)
client.markets.getStatus({ marketAddress })
client.markets.getUserPosition({ marketAddress, user })
client.markets.canFinalize({ marketAddress })
client.markets.isChallengeWindowActive({ marketAddress })
client.markets.isResolved({ marketAddress })
client.markets.create(hostedCreateInput)
client.markets.createAndWait(hostedCreateInput)
client.markets.createDirect(input)
client.markets.createWithExecutor(input, executor)
client.markets.prepareCreateTransaction(input, account)
client.markets.parseCreateReceipt(receipt)
client.markets.buyIn({ marketAddress, option, amount, approveIfNeeded })
client.markets.addToWhitelist({ marketAddress, users })
client.markets.removeFromWhitelist({ marketAddress, user })
client.markets.submitResolution({ marketAddress, option, evidenceUrl })
client.markets.submitAdminVote({ marketAddress, option })
client.markets.submitDispute({ marketAddress })
client.markets.finalize({ marketAddress })
client.markets.redeem({ marketAddress })
client.markets.refund({ marketAddress })
client.markets.voidMarket({ marketAddress })client.drafts
Hosted validation and draft generation.
Planned surface:
client.drafts.validate(input)
client.drafts.generate(input)
client.drafts.get(id)
client.drafts.getValidation(id)This is the main product entry point: generate or validate a draft first, then create the market
through client.markets.create(...) or client.markets.createAndWait(...).
The client.drafts namespace exists in the current package, but its methods throw explicit
"not available" errors until the hosted draft endpoints are published.
client.account
Wallet- and token-aware helpers.
Current surface:
client.account.getUsdcBalance({ address })
client.account.getUsdcAllowance({ owner, spender })
client.account.approveUsdc({ spender, amount })
client.account.getWalletState()client.jobs
Async job helpers for hosted execution.
client.jobs.get(jobId)Read-only usage
The core SDK should work without a wallet client when you only need reads:
const client = new MarketsClient({ publicClient });
const market = await client.markets.get(marketAddress);Write methods should fail with explicit errors if no wallet client is available.
Direct versus hosted flows
The SDK supports both:
- hosted flows as the default product path
- direct flows for explicit contract reads and writes when you need lower-level control
For parimutuel hosted creation, the normal auth model is:
- project API key
- authenticated end-user session
- managed wallet execution behind the scenes
Explicit wallet signatures are still relevant later for advanced signed-hosted flows such as CLOB orders, but they should not be the default hosted path here.
Event model
Typed event watchers should remain part of the core package:
const stop = client.markets.watchMarketEvent(
marketAddress,
"ResolutionSubmitted",
(logs) => {
console.log(logs);
},
);
stop();Why the SDK stays Privy-agnostic
Privy is our intended wallet UX, but the core package should only depend on viem-compatible clients. That keeps it portable:
- browser app with Privy
- CLI or script with a raw wallet client
- backend worker with a service signer
- tests with local wallets
Privy-specific behavior belongs in app/provider composition and the React package, not the core SDK itself.