@ab-org/predicate-market-sdk
v2.0.0
Published
Prediction-market specific helpers built on top of `@ab-org/sdk-core`.
Downloads
791
Readme
@ab-org/predicate-market-sdk
Prediction-market specific helpers built on top of @ab-org/sdk-core.
Key features
- Built-in wallet connection modal (social via OIDC relay +
WalletAccount, injected wallets viaWalletConnector— no FedCM inSignInModalfor Google/X) - Connection state via
createWalletConnectController()andcreateAccountController()from@ab-org/sdk-core - Unified smart-wallet session metadata with capability policy support
- High-level execution helpers via
createWalletExecutionController() - Deposit / withdraw controllers with USDT-first defaults
- Dynamic token & chain lists via
MarketDataProvider(backend-driven, mock included) - Quote / slippage API — fetch real-time pricing before confirming deposit or withdraw
- Predicate-market policy adapter for deposit / withdraw / trade flows
Installation
npm install @ab-org/sdk-core @ab-org/predicate-market-sdkQuick start
import {
WalletConnector,
MetaMaskProvider,
createAccountController,
createWalletConnectController,
createWalletExecutionController,
} from "@ab-org/sdk-core";
import {
createDepositController,
createWithdrawController,
createMarketDataProvider,
createPredicateMarketPolicyAdapter,
type CustodyAdapter,
} from "@ab-org/predicate-market-sdk";
declare const custodyAdapter: CustodyAdapter;
const connector = new WalletConnector([
new MetaMaskProvider(),
// new CubistSocialProvider(cubistClient)
]);
const walletConnect = createWalletConnectController({ connector, defaultAdapterId: "metamask" });
const account = createAccountController();
const execution = createWalletExecutionController();
// Use the mock provider until real backend endpoints are ready
const marketData = createMarketDataProvider();
const deposit = createDepositController(custodyAdapter, marketData);
const withdraw = createWithdrawController(custodyAdapter, marketData);
const policy = createPredicateMarketPolicyAdapter({
appId: "prediction-market",
origin: window.location.origin,
});Connect wallet
<button onClick={() => (walletConnect.isConnected ? walletConnect.disconnect() : walletConnect.openModal())}>
{walletConnect.isConnected ? "Disconnect" : "Connect Wallet"}
</button>Configure SignInModal
The SDK ships with bundled auth config (Google client id, Twitter client id, CubeSigner env/org) so you can call initSDK with only signIn. Override via env (NEXT_PUBLIC_*) or by passing options to initSDK.
Google / X in SignInModal use the SDK's bundled OIDC relay auth: a popup opens your NEXT_PUBLIC_RELAY_ORIGIN routes /relay/google and /relay/x, then the SDK's built-in WalletAccount bridge turns the returned OIDC token into the session provider.
import { initSDK, SignInModal } from "@ab-org/predicate-market-sdk";
initSDK({
// Optional: only used by other Twitter flows; SignInModal X login uses /relay/x on RELAY_ORIGIN
twitterRedirectUri: typeof window !== "undefined" ? `${window.location.origin}/auth/twitter-callback` : undefined,
signIn: {
socialProviders: [
{ id: "google", label: "Continue with Google" },
{ id: "x", label: "Continue with X" },
],
wallets: [
{ id: "metamask", name: "MetaMask" },
{ id: "okx", name: "OKX Wallet" },
{ id: "phantom", name: "Phantom" },
],
initialVisibleCount: 4,
},
});
<SignInModal />;Rules:
NEXT_PUBLIC_RELAY_ORIGIN(orRELAY_ORIGIN) must match where you serve/relay/googleand/relay/x
Funding chain & funding token (withdraw / balance)
getChainInfo(chainId?)— resolves RPC, explorer metadata, native currency fields, anddefaultFundingTokenAddress(per-chain default ERC-20 for the funding leg). IfchainIdis omitted or empty, defaults to3131(Tenderly BSC vnet). Built-in ids are3131and56(BSC mainnet); unknown ids throw.getFundingTokenAddress(chainId?)— same optionalchainIdasgetChainInfo. IfNEXT_PUBLIC_FUNDING_TOKEN_ADDRESS/FUNDING_TOKEN_ADDRESSor legacy defaultFundingTokenAddress`**.DEFAULT_FUNDING_TOKEN_ADDRESS— shorthand forgetChainInfo().defaultFundingTokenAddress(default funding chain3131).fetchFundingTokenBalance(address, { chainId, rpcUrl?, tokenAddress?, decimals?, displaySymbol? })— usesgetChainInfo(chainId)for RPC whenrpcUrlis omitted,getFundingTokenAddress(chainId)whentokenAddressis omitted, anddecimalsdefaultchain.nativeCurrencyDecimalswhen omitted (override if your funding token uses different decimals).createFundingWithdrawExecutor({ chainId?, rpcUrl?, tokenAddress?, … })— uses the samechainIdforgetChainInfo, default token address (getFundingTokenAddress(chainId)), and order payload (default3131whenchainIdomitted).
Type note: EvmChainInfo includes defaultFundingTokenAddress. If you construct chain objects manually in TypeScript, add that field or use getChainInfo instead of literals.
socialProviders: undefineduses built-in defaults (google,x)socialProviders: []hides all social buttons- known social ids like
googleandxautomatically reuse built-in icons unless you overrideicon wallets: undefineduses the built-in wallet registrywallets: []hides all wallet buttons- known wallet ids automatically reuse built-in metadata like
installUrland detectedinstalledstate unless you override them - component props still override
initSDK({ signIn })on a per-modal basis
The package still exports signInWithGoogle (Google Identity Services, optional FedCM) for custom integrations; the built-in SignInModal does not use it for Google.
Address & balance
if (!account.isConnected) return <p>Please connect wallet</p>;
return (
<div>
Address: {account.address?.slice(0, 6)}...{account.address?.slice(-4)}
<br />
Balance: {account.balance?.formatted} {account.balance?.symbol}
</div>
);Market data — token / chain lists & quotes
Both deposit and withdraw controllers expose market-data helpers
that delegate to the injected MarketDataProvider:
// 1. Fetch available tokens
const tokens = await deposit.fetchTokens();
// → [{ symbol: "USDT", name: "Tether", decimals: 6, … }, …]
// 2. After the user picks a token, fetch the chains it supports
const chains = await deposit.fetchChains("USDT");
// → [{ id: "ETH", name: "Ethereum", estimatedTime: "~15 min" }, …]
// 3. After user picks token + chain, fetch the deposit address (backend-provided)
const { address, minimumDeposit } = await deposit.fetchDepositAddress("USDT", "ETH");
// QR code is generated client-side from the address — no backend QR needed
// 4. After user enters amount, fetch a quote
const quote = await deposit.fetchQuote("USDT", "ETH", "100");
// → { quoteId, estimatedAmount, slippage, fee, feeToken, exchangeRate, expiresAt }The same token / chain / quote API is available on the withdraw controller:
const tokens = await withdraw.fetchTokens();
const chains = await withdraw.fetchChains("USDT");
const quote = await withdraw.fetchQuote("USDT", "ETH", "200");Implementing a real MarketDataProvider
Replace createMarketDataProvider() with your own implementation:
import type { MarketDataProvider } from "@ab-org/predicate-market-sdk";
const realMarketData: MarketDataProvider = {
async getSupportedTokens(direction) {
const res = await fetch(`/api/market/tokens?direction=${direction}`);
return res.json();
},
async getSupportedChains(token, direction) {
const res = await fetch(`/api/market/chains?token=${token}&direction=${direction}`);
return res.json();
},
async getQuote(request) {
const res = await fetch("/api/market/quote", {
method: "POST",
body: JSON.stringify(request),
});
return res.json();
},
async getDepositAddress(token, chain) {
const res = await fetch(`/api/market/deposit-address?token=${token}&chain=${chain}`);
return res.json();
},
};Deposit modal
<button
onClick={() =>
deposit.open({
preferredToken: "USDC",
preferredChain: "ETH",
onStatusChange: (status) => console.log("deposit status", status),
})
}
>
Deposit
</button>Smart-wallet session model
Social login and injected wallets now converge on the same WalletSession shape.
const account = createAccountController();
console.log(account.session?.walletType); // "injected" | "smart"
console.log(account.chainContext?.walletChainDescriptor.label);
console.log(account.capabilities); // provider capability matrix
console.log(account.capabilityPolicy); // optional scoped session policyTrading helpers
import { encodeFunctionData, erc20Abi } from "viem";
async function approveSpender(spender: string, value: bigint) {
if (!account.address) return;
const callData = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [spender, value],
});
await execution.sendTransaction({
from: account.address,
to: tokenAddress,
data: callData,
});
}Use execution.signTransaction(), execution.signMessage(), and execution.signTypedData() instead of building raw EIP-1193 requests in app code.
Capability policies
const depositPolicy = policy.deposit("USDC", "ETH", "1000000");
const withdrawPolicy = policy.withdraw("USD1", "AB_CORE", "500000");
const tradePolicy = policy.trade("ETH", ["eth_sendTransaction", "eth_signTypedData_v4"]);These policies can be passed into your smart-wallet authorization flow so app actions stay scoped by chain, method, token, and amount.
Withdraw modal
<button
onClick={() =>
withdraw.open({
defaultToken: "USDT",
defaultChain: "AB_CORE",
targetAddress: "0x...",
defaultAmount: "100",
onStatusChange: (status) => console.log("withdraw", status),
})
}
>
Withdraw
</button>