SDK Reference

rain-sdk-v2 — TypeScript SDK for the Rain prediction markets protocol on Arbitrum One. Transaction builders, on-chain reads, REST API wrapper, and Socket.IO realtime events.

Conventions used everywhere: option indexes are 1-based · prices use 1e18 scale (parseEther('0.5') = 50%) · token amounts use parseUnits(x, 6) for USDT and parseUnits(x, 18) for RAIN · OptionSide.Yes = 1, OptionSide.No = 2.

Core classes

Rain

Stateless class for building transactions and querying on-chain data. Never holds keys — it returns RawTransaction objects you sign yourself.

TypeScript
const rain = new Rain({
  environment: 'production',  // 'development' | 'stage' | 'production' (default: development)
  rpcUrl: 'https://arb1.arbitrum.io/rpc', // optional
  apiUrl: 'https://prod2-api.rain.one',   // optional, derived from environment
});
Constructor paramTypeDefaultDescription
environment'development' | 'stage' | 'production'developmentTarget environment
rpcUrlstringrandom public RPCCustom Arbitrum RPC
apiUrlstringfrom environmentCustom backend API URL

All builders return a RawTransaction or RawTransaction[] (approval first when needed):

TypeScript
interface RawTransaction {
  to: `0x${string}`;
  data: `0x${string}`;
  value?: bigint;
}

RainAA — smart accounts (Account Abstraction)

Stateful class for smart account management with Alchemy gas sponsorship. Wraps any RawTransaction from the builders into a sponsored user operation.

TypeScript
import { RainAA } from 'rain-sdk-v2';

const rainAA = new RainAA({
  walletClient,             // viem WalletClient (required)
  alchemyApiKey: '...',     // required
  paymasterPolicyId: '...', // required
  chain: arbitrum,          // required
  rpcUrl: '...',            // optional
});

const smartAccountAddress = await rainAA.connect(); // init smart account
const hash = await rainAA.sendTransaction(rawTx);   // gas-sponsored send
rainAA.address;            // getter — smart account address (throws if not connected)
rainAA.client;             // getter — smart wallet client
rainAA.disconnect();       // reset connection state

Market creation

buildCreateMarketTx(params) write

Creates a new prediction market. Returns [approveTx?, createPoolTx].

TypeScript
const txs = await rain.buildCreateMarketTx({
  marketQuestion: 'Will ETH reach $5000 by end of year?',
  marketOptions: ['Yes', 'No'],
  marketTags: ['crypto'],
  marketDescription: 'Prediction on ETH price target',
  isPublic: true,
  isPublicPoolResolverAi: true,
  creator: '0x...',
  startTime: BigInt(Math.floor(Date.now() / 1000) + 120),
  endTime: BigInt(Math.floor(Date.now() / 1000) + 86400),
  no_of_options: 2n,
  disputeTimer: 259200,
  inputAmountWei: parseUnits('10', 6),
  barValues: [50, 50],
  initialYesPrices: [parseEther('0.5'), parseEther('0.5')], // optional
  baseToken: config.tokens.usdt.address,
  tradingModel: TradingModel.AMM, // AMM = 0, OrderBook = 1
  marketImage: 'https://cdn.example.com/market-image.png',
});
ParameterTypeDescription
marketQuestionstringThe market question
marketOptionsstring[]Option labels (2–26 options)
marketTagsstring[]Tags (1–3)
marketDescriptionstringDescription
isPublicbooleanPublic market
isPublicPoolResolverAibooleantrue for AI resolver, false for manual
creator0x${string}Creator / pool owner address
startTimebigintUnix timestamp (seconds) when trading opens
endTimebigintUnix timestamp (seconds) when trading ends
no_of_optionsbigintNumber of options
inputAmountWeibigintInitial liquidity in base token wei
disputeTimernumberOracle end-time duration in seconds (e.g. 259200 = 3 days). Auto-set from environment config
barValuesnumber[]Probability distribution, 0–100 scale, sums to 100
initialYesPricesbigint[]Optional. Initial Yes prices per option in 1e18 scale. Defaults to 50%
baseToken0x${string}Base token address (USDT or RAIN)
tradingModelTradingModelAMM (0) or OrderBook (1)
marketImagestringMarket image URL (required)
Approval amounts — USDT: liquidity + oracleFixedFeePerOption × nOptions. RAIN: liquidity + nOptions × 20% of liquidity (e.g. 1000 RAIN with 5 options → 2000 RAIN approval).

Trading

buildEnterOptionTx(params) write

Buy shares of an option (AMM trade). Reads the market's base token, checks allowance, includes an approval TX if needed, and auto-calculates slippage protection via on-chain getEntryShares. Returns [approveTx?, enterOptionTx].

TypeScript
const txs = await rain.buildEnterOptionTx({
  marketContractAddress: '0x...',
  selectedOption: 1n,                 // 1-based
  optionSide: OptionSide.Yes,         // Yes = 1, No = 2
  buyAmountInWei: parseUnits('5', 6), // 5 USDT
  walletAddress: '0x...',
  slippageTolerance: 5n,              // optional, default 5%
  deadline: 600n,                     // optional, default 600s
});
ParameterTypeDescription
marketContractAddress0x${string}Market contract
selectedOptionbigintOption index (1-based)
optionSideOptionSideYes (1) or No (2)
buyAmountInWeibigintAmount in base token wei
walletAddress0x${string}For allowance check
minSharesOutbigintOptional. Auto-calculated from getEntryShares with slippage
slippageTolerancebigintOptional. Percent, default 5n
deadlinebigintOptional. Duration in seconds, default 600n

buildSellOptionTx(params) write

Market-sell shares into the resting buy order book. No approval needed (you sell shares, not tokens). Slippage protection auto-calculated from on-chain getCurrentPrice. Returns a single RawTransaction.

TypeScript
const tx = await rain.buildSellOptionTx({
  marketContractAddress: '0x...',
  selectedOption: 1n,
  optionSide: OptionSide.Yes,
  sharesAmount: parseUnits('10', 6),  // shares to sell
  slippageTolerance: 5n,              // optional
  deadline: 600n,                     // optional
});

Split & merge

buildSplitTx(params) write

Split base tokens into equal Yes + No shares for an option (e.g. 5 USDT → 5 Yes + 5 No). Allowance handled automatically. Returns [approveTx?, splitTx].

TypeScript
const txs = await rain.buildSplitTx({
  marketContractAddress: '0x...',
  option: 1n,
  amount: parseUnits('5', 6),
  walletAddress: '0x...',
});

buildMergeTx(params) write

Merge equal Yes + No share pairs back into base tokens. Burns shares, returns base token; no approval needed.

TypeScript
const tx = rain.buildMergeTx({
  marketContractAddress: '0x...',
  option: 1n,
  amount: 5000000n, // raw shares (from getUserOptionShares)
});

Liquidity

buildAddLiquidityTx(params) write

Add liquidity to a specific option. Slippage protection auto-calculated proportionally from on-chain ammYesReserve / ammNoReserve. Returns [approveTx?, addLiquidityTx].

TypeScript
const txs = await rain.buildAddLiquidityTx({
  marketContractAddress: '0x...',
  option: 1n,
  totalAmountInWei: parseUnits('10', 6),
  walletAddress: '0x...',
  slippageTolerance: 5n, // optional
  deadline: 600n,        // optional
});

buildRemoveLiquidityTx(params) write

Remove liquidity by burning LP shares. Min outputs auto-calculated via on-chain getRemovedLiquidity.

TypeScript
const lpShares = await rain.getUserOptionLPShares({
  marketContractAddress: '0x...', option: 1n, userAddress: '0x...',
});
const tx = await rain.buildRemoveLiquidityTx({
  marketContractAddress: '0x...',
  option: 1n,
  lpShares,
  slippageTolerance: 5n, // optional
});

Order book

Each market option/side has an on-chain limit order book with price-level FIFO queues (price-time priority). Order IDs are emitted in the PlaceBuyOrder event.

buildPlaceBuyOrderTx(params) write

Place a limit buy order. Allowance checked automatically; available when the AMM pool is closed. Returns [approveTx?, placeBuyOrderTx].

TypeScript
const txs = await rain.buildPlaceBuyOrderTx({
  marketContractAddress: '0x...',
  option: 1n,
  optionSide: OptionSide.Yes,
  price: parseEther('0.5'),       // 50% in 1e18 scale
  amount: parseUnits('10', 6),    // 10 USDT
  walletAddress: '0x...',
});

buildPlaceSellOrderTx(params) write

Place a limit sell order. Shares are escrowed by the contract; no token approval needed.

TypeScript
const tx = rain.buildPlaceSellOrderTx({
  marketContractAddress: '0x...',
  option: 1n,
  optionSide: OptionSide.Yes,
  price: parseEther('0.7'),  // 70%
  shares: 5000000n,          // from getUserOptionShares
});

buildCancelBuyOrdersTx / buildCancelSellOrdersTx write

Cancel one or more orders in a single transaction. Both share the same interface.

TypeScript
const tx = rain.buildCancelBuyOrdersTx({
  marketContractAddress: '0x...',
  option: 1n,
  optionSides: [1, 1],                              // Yes, Yes
  prices: [parseEther('0.5'), parseEther('0.6')],
  orderIDs: [1n, 2n],                               // from PlaceBuyOrder event
});

Order book reads read

MethodReturnsDescription
getFirstBuyOrderPrice({ marketContractAddress, option, optionSide })bigintBest bid price (1e18 scale)
getFirstSellOrderPrice({ ... })bigintBest ask price (1e18 scale)
getBuyOrdersAtPrice({ ..., price })OrderLevelInfoLevel info: { headIndex, tailIndex, count, isInitialized } — FIFO queue per price level
getSellOrdersAtPrice({ ..., price })OrderLevelInfoSame, for sells
checkOrderExists({ ..., price, orderID }){ exists, index }Check a specific resting order
getUserActiveBuyOrders({ marketContractAddress, userAddress })bigintCount of user's active buy orders
getUserActiveSellOrders({ ... })bigintCount of user's active sell orders

Market resolution

MethodDescription
buildClosePoolAITx({ marketContractAddress, option })Close with AI resolver. Returns [approveTx (resolver bond), closePoolTx]
buildClosePoolManualTx({ ..., proposedWinner })Close with manual resolver, proposing a winner (1 = Yes, 2 = No)
buildChooseWinnerTx({ ..., optionSide })Finalize winner after pool is closed
buildCalculateWinnerTx({ marketContractAddress, option })Calculate winner via the oracle resolver. Must be called before claim. Reads optionResolver(option) and calls calculateWinner() on it
buildExtendTimeTx({ ..., newEndTime, signature })Extend resolution time. Requires a backend signature from signOraclesExtendTime
TypeScript
// Close with AI resolver (after endTime)
const closeTxs = await rain.buildClosePoolAITx({ marketContractAddress: '0x...', option: 1n });

// Or close manually, proposing the winner
const closeManual = await rain.buildClosePoolManualTx({
  marketContractAddress: '0x...', option: 1n, proposedWinner: 1, // 1 = Yes
});

// After the dispute window: calculate the winner, then claim
const calcTx = await rain.buildCalculateWinnerTx({ marketContractAddress: '0x...', option: 1n });

Claiming

buildClaimTx(params) write

Claim winnings after resolution. Call buildCalculateWinnerTx first; optionally check getOptionClaimed to avoid reverts.

TypeScript
const tx = rain.buildClaimTx({ marketContractAddress: '0x...', option: 1n });

Disputes & appeals

buildOpenDisputeTx(params) write

Open a dispute — or an appeal, if a dispute already exists. The fee is read from getDisputeAppealFee(option). Returns [approveTx (dispute fee), openDisputeTx].

TypeScript
// Call once to open a dispute; call again after the dispute window to appeal
const txs = await rain.buildOpenDisputeTx({ marketContractAddress: '0x...', option: 1n });

Evidence and dispute messaging (text, images, video, PDFs, YouTube links) goes through the REST API — see createDisputeMessage / getPoolDisputeConvo in the REST overview.

Token approvals

buildApprovalTx(params) write

Standalone ERC-20 approve (most builders handle approvals automatically — use this for manual control).

TypeScript
const tx = rain.buildApprovalTx({
  tokenAddress: config.tokens.usdt.address,
  spender: '0x...',                 // market contract
  amount: parseUnits('100', 6),
});

Read methods (on-chain queries)

MethodReturnsDescription
getTokenAllowance({ tokenAddress, owner, spender })bigintCurrent ERC-20 allowance
getUserOptionShares({ ..., option, optionSide, userAddress })bigintUser's Yes or No shares for an option
getUserOptionLPShares({ ..., option, userAddress })bigintUser's LP shares for an option
getEntryShares({ ..., option, optionSide, amount }){ returnedShares, expectedReward }Quote: shares received for a buy amount
getOptionClaimed({ ..., option, userAddress })booleanWhether the user already claimed winnings
getDynamicPayout({ ..., userAddress, option })bigint[]Payout amounts per side: [yesPayoutWei, noPayoutWei]
getDisputeWindow({ marketContractAddress })bigintDispute window duration in seconds
Plus the order book reads listed above.

Authentication

Wallet-based login to the Rain backend: sign the lowercased wallet address with personal_sign, then exchange the signature for a JWT.

TypeScript
import { signLoginMessage } from 'rain-sdk-v2';

const signature = await signLoginMessage(walletClient, walletAddress);

const result = await rain.login({
  signature,
  walletAddress,                      // EOA address
  smartWalletAddress,                 // smart account address
  referredBy: 'CODE',                 // optional referral code
});
// result.accessToken — JWT for authenticated REST routes
// result.userId      — backend user ID

// Later: check token validity
const status = await rain.checkTokenExpiration(result.accessToken);

REST API overview

The SDK wraps the full Rain backend REST API. Authenticated routes take an accessToken (from login) as the last argument. Highlights by domain:

DomainKey methods
PoolsgetPublicPools (filter/sort, e.g. status: 'Live', sortBy: 'trending'), getPrivatePools, getPoolById, getPoolByContractAddress, searchPool, getFeaturedPools, getRelatedPools, getPoolResolutionHistory, accessPool / verifyAccessCode (private pools), getPoolTotalParticipants, updatePoolResolutionTime, signOraclesExtendTime
InvestmentsgetUserPositions (per-side net shares, avg buy price, projected payout), getOpenPositions, getUserTotalInvestment, getOptionsTotalVolume, getPoolActivity, getTopHolders, getUserInvestedPools, calculateUserPnl, getUserPnlGraph, getPlatformTvl, getTvlGraph, getPlatformVolume, getTopWinnersLosers, calculateUserOpenInterest
OrdersgetOrderBook({ pool }), getUserOrders({ filter: 'pending' }), getUserOrderByPoolId, getOrdersListingByPool, getOrderById
UsersfindUserByWalletAddress, getUserProfile, viewUserProfile, updateUserProfile, getUserHistory, getUsersTotalCount, checkTokenExpiration
PointsgetUserPoints, getUserPointsGraph, addUserPoints, userSuccessfulOnboarding
NotificationsgetNotifications, markAllNotificationsAsRead, markNotificationAsRead
Price datagetPriceData({ contractAddress, side, filter }) — filters '1H' | '6H' | '1D' | '1W' | '1M' | 'ALL'
Comments & reviewscreateComment (with replies), getCommentsListing, likeComment / unlikeComment, addReview, getUserReviews
FollowtoggleFollow, checkFollow, getFollowers, getFollowing, getFollowStats
RAIN burngetTotalBurned, getBurnPerPool
DisputescreateDisputeMessage (text / image / video / file / YouTube evidence, max 25MB per file), getPoolDisputeConvo
TokensgetTokenPrice(tokenAddress) — USD price

WebSocket events (RainSocket)

Realtime event subscriptions via Socket.IO. Every on* helper returns an unsubscribe function.

TypeScript
import { RainSocket } from 'rain-sdk-v2';

const socket = new RainSocket('https://dev-api.rain.one');
socket.connect();

const unsub = socket.onSyncPrice(poolId, (data) => {
  // data.prices = [{ side: 1, price: 0.62, percentage: 62, subPoolIndex: 1 }, ...]
});

socket.onChannel('custom-event/123', (data) => { /* generic subscription */ });

unsub();
socket.disconnect();

Pool-scoped events

ChannelHelperFires when
enter-option/{poolId}onEnterOptionShares bought (AMM or order match)
exit-option/{poolId}onExitOptionShares sold (order match)
sync-price/{poolId}onSyncPricePrice update
order-created/{poolId}onOrderCreatedNew order placed
order-cancelled/{poolId}onOrderCancelledOrder cancelled
order-filled/{poolId}onOrderFilledOrder filled (full or partial; payload has filledOrder + optional pendingOrder)
liquidity/{poolId}onLiquidityLiquidity added
remove-liquidity/{poolId}onRemoveLiquidityLiquidity removed
split/{poolId} / merge/{poolId}onSplit / onMergeTokens split / merged
pool-closed/{poolId}onPoolClosedPool or sub-pool finalized
pool-reverted/{poolId}onPoolRevertedResolution reverted
pool-token-set/{poolId}onPoolTokenSetToken set for pool
streamingStatusChanged/{poolId}onStreamingStatusChangedStreaming toggled
winner/{poolId} / winner-proposer/{poolId}onWinner / onWinnerProposerWinner proposed / proposer recorded
reveal-winner-available/{poolId}onRevealWinnerAvailableAppeal winner ready
dispute-opened/{poolId}onDisputeOpenedDispute opened
oracle-created/{poolId}onOracleCreatedDispute oracle deployed
dispute-time-extented/{poolId}onDisputeTimeExtendedDeadline extended
appeal-opened/{poolId}onAppealOpenedAppeal filed
appeal-winner-calculated/{poolId}onAppealWinnerCalculatedOracle calculated winner
dispute-winner/{poolId} / appeal-winner/{poolId}onDisputeWinner / onAppealWinnerDispute / final appeal decided
claim-reward/{poolId}onClaimRewardReward claimed (broadcast)

User-scoped events

ChannelHelperFires when
claim-reward/{poolId}/{userId}onUserClaimRewardYour reward claimed
dispute-refund/{poolId}/{userId}onDisputeRefundDispute bond refunded (disputer won)
appeal-refund/{poolId}/{userId}onAppealRefundAppeal fee refunded (appealer won)
resolution-refund/{poolId}/{userId}onResolutionRefundResolution bond refunded (proposer correct)
resolver-reward/{poolId}/{userId}onResolverRewardResolver closing-share reward
notifications/{userId}onNotificationsPersonal notification

Global events

ChannelHelperFires when
poolonNewPoolNew pool created anywhere on the protocol

Enums

TypeScript
enum TradingModel {
  AMM = 0,
  OrderBook = 1,
}

enum OptionSide {
  Yes = 1,
  No = 2,
}

Environments

EnvironmentAPIUSDTRAIN
developmenthttps://dev2-api.rain.one6 decimals18 decimals
stagehttps://stg2-api.rain.one6 decimals18 decimals
productionhttps://prod2-api.rain.one6 decimals18 decimals

Factory contract addresses and token addresses per environment are exposed by rain.getEnvironmentConfig() — always read them from the SDK rather than hard-coding.

TypeScript
const config = rain.getEnvironmentConfig();
// config.apiUrl
// config.market_factory_address
// config.dispute_initial_timer
// config.tokens.usdt — { address, symbol, decimals, oracle_fixed_fee_per_option }
// config.tokens.rain — { address, symbol, decimals, oracle_fixed_fee_per_option }

const tokenInfo = rain.getTokenConfig('0x...'); // lookup by address, or null

Typical flow

text
1. Create Market        (buildCreateMarketTx)
2. Wait for startTime
3. Enter Option         (buildEnterOptionTx) / Split (buildSplitTx)
4. Place Orders         (buildPlaceBuyOrderTx / buildPlaceSellOrderTx)
5. Wait for endTime
6. Close Pool           (buildClosePoolAITx / buildClosePoolManualTx)
7. Calculate Winner     (buildCalculateWinnerTx)
8. Check claimed        (getOptionClaimed) — optional
9. Claim                (buildClaimTx)

If disputed:   6b. Open Dispute (buildOpenDisputeTx)  6c. Appeal (call again)
If extending:  5b. signOraclesExtendTime API → buildExtendTimeTx

Ready to write code? Head over to the complete examples.