npm package: @markit.market/sdk
Description: TypeScript SDK for MarkIt prediction markets on Base
Latest version: 0.1.1

# @markit.market/sdk

TypeScript SDK for [MarkIt](https://markit.market) prediction markets on Base.

Builders earn **0.25%** of every bet placed through their integration, carved from the existing 2% protocol fee. Zero cost to users.

## Install

```bash
npm install @markit.market/sdk viem
```

## Quick Start

```ts
import { createMarkitClient } from '@markit.market/sdk'
import { createWalletClient, http } from 'viem'
import { base } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'

const markit = createMarkitClient({
  builderCode: 'your-builder-code',
  chain: 'base',
})

// Browse open markets
const markets = await markit.markets.list({ state: 'open' })
console.log(markets[0].question) // "Will the Lakers beat the Celtics?"
console.log(markets[0].yesPrice) // "0.65" (65 cents)

// Get a quote before betting
const quote = await markit.markets.getQuote({
  market: markets[0].address,
  side: 'yes',
  usdcAmount: 10_000_000n, // $10 USDC (6 decimals)
})
console.log(quote.sharesOut)      // shares you'd receive
console.log(quote.effectivePrice) // effective price per share
console.log(quote.totalFee)       // total fees in USDC

// Place the bet
const account = privateKeyToAccount('0x...')
const signer = createWalletClient({ account, chain: base, transport: http() })

const result = await markit.trade.placeBet({
  market: markets[0].address,
  side: 'yes',
  usdcAmount: 10_000_000n,
  signer,
})
console.log(result.txHash)    // transaction hash
console.log(result.attributed) // true if builder fee was credited
```

## Smart Wallet Integration

For Coinbase Smart Wallet, Safe, or other batching wallets, use `buildPlaceBetCalls()` to get raw calldata:

```ts
const calls = markit.trade.buildPlaceBetCalls({
  market: '0x...',
  side: 'yes',
  usdcAmount: 10_000_000n,
})
// calls = [
//   { to: USDC_ADDRESS, data: '0x...' },  // approve
//   { to: MARKET_ADDRESS, data: '0x...' }, // placeBet
// ]

// Send via your smart wallet's batch method
await smartWallet.sendCalls(calls)

// Then report attribution separately so you earn the builder fee
await markit.trade.reportAttribution({
  market: '0x...',
  txHash: '0x...',
  bettor: '0x...',
})
```

## Builder Codes

Register at [markit.market/builders](https://markit.market/builders) to get your builder code and API key.

```ts
const markit = createMarkitClient({
  builderCode: 'your-code',
  apiKey: 'mk_live_...', // from registration — needed for stats
})

// View your earnings
const stats = await markit.builder.getStats()
console.log(stats.totalVolume)  // total bet volume through your integration
console.log(stats.totalFees)    // total fees earned (USD)
console.log(stats.unpaidFees)   // fees pending payout
console.log(stats.uniqueUsers)  // unique bettors

// Per-bet fee history
const fees = await markit.builder.getFeeHistory({ limit: 20 })
```

## API Reference

### `createMarkitClient(config)`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `builderCode` | `string` | *required* | Your registered builder code |
| `chain` | `'base' \| 'baseSepolia'` | `'base'` | Target chain |
| `rpcUrl` | `string` | Public RPC | Custom RPC endpoint |
| `apiKey` | `string` | — | API key for `builder.getStats()` and `builder.getFeeHistory()` |

---

### `markit.markets`

#### `list(options?)`

List markets from the MarkIt indexer.

```ts
const markets = await markit.markets.list({
  state: 'open',       // 'open' | 'closeOnly' | 'resolved' | 'all'
  sport: 'nba',        // 'nba' | 'ncaab' | 'all'
  limit: 20,
})
```

Returns `MarketSummary[]`:

| Field | Type | Description |
|-------|------|-------------|
| `address` | `Address` | Market contract address |
| `question` | `string` | e.g. "Will the Lakers beat the Celtics?" |
| `slug` | `string \| null` | URL slug, e.g. `lakers-at-celtics-mar-17` |
| `state` | `number` | 0=Created, 1=Funded, 2=Open, 3=CloseOnly, 4=Resolved, 5=Withdrawable |
| `outcome` | `number` | 0=None, 1=Yes, 2=No |
| `sport` | `string` | `'nba'` or `'ncaab'` |
| `yesPrice` | `string` | Current YES price (0-1) |
| `noPrice` | `string` | Current NO price (0-1) |
| `yesLiability` | `string` | Total YES payouts owed (WAD) |
| `noLiability` | `string` | Total NO payouts owed (WAD) |
| `engineUsdc` | `string` | USDC balance in contract (6 decimals) |
| `bettingCloseTime` | `number` | Unix timestamp when betting closes |
| `resolveTime` | `number` | Unix timestamp when market resolves |
| `homeLogoUrl` | `string \| null` | ESPN team logo URL |
| `awayLogoUrl` | `string \| null` | ESPN team logo URL |

#### `get(slugOrAddress)`

Get full market details by address or slug.

```ts
const market = await markit.markets.get('lakers-at-celtics-mar-17')
// or
const market = await markit.markets.get('0x1234...')
```

Returns `MarketDetail` (extends `MarketSummary` with LP parameters, fees, seed imbalance).

#### `getPrice(market)`

Get live on-chain mark prices (reads directly from the contract via RPC).

```ts
const price = await markit.markets.getPrice('0x...')
console.log(price.yes) // 0.65
console.log(price.no)  // 0.35
```

#### `getQuote({ market, side, usdcAmount })`

Simulate a bet and get a detailed fee breakdown.

```ts
const quote = await markit.markets.getQuote({
  market: '0x...',
  side: 'yes',
  usdcAmount: 50_000_000n, // $50
})
```

Returns `BetQuote`:

| Field | Type | Description |
|-------|------|-------------|
| `sharesOut` | `string` | Shares you'd receive (WAD, 18 decimals) |
| `avgPrice` | `string` | Average price per share |
| `priceImpact` | `string` | Price impact as a decimal (0.01 = 1%) |
| `lpFee` | `string` | LP fee in USDC (6 decimals) |
| `protocolFee` | `string` | Protocol fee in USDC (6 decimals) |
| `builderFee` | `string` | Builder fee in USDC (6 decimals) |
| `totalFee` | `string` | Total fees in USDC (6 decimals) |
| `effectivePrice` | `string` | Effective price per share after fees |

#### `getLiveScores()`

Get ESPN live scores matched to MarkIt markets.

```ts
const scores = await markit.markets.getLiveScores()
// [{ homeTeam: "Lakers", awayTeam: "Celtics", homeScore: 98, awayScore: 95, status: "in_progress", period: "4th", clock: "2:15" }]
```

---

### `markit.trade`

#### `placeBet({ market, side, usdcAmount, signer, minSharesOut? })`

Full flow: checks USDC allowance, approves if needed, places the bet, reports builder attribution.

```ts
const result = await markit.trade.placeBet({
  market: '0x...',
  side: 'yes',
  usdcAmount: 10_000_000n,      // $10 USDC
  minSharesOut: 9_000_000_000n,  // optional: slippage protection
  signer: walletClient,
})
// { txHash: '0x...', attributed: true }
```

- `signer` must be a [viem WalletClient](https://viem.sh/docs/clients/wallet) with an `account`
- Attribution is fire-and-forget — the bet succeeds even if attribution fails
- USDC uses 6 decimals: `$1 = 1_000_000n`, `$10 = 10_000_000n`, `$100 = 100_000_000n`

#### `buildPlaceBetCalls({ market, side, usdcAmount, minSharesOut? })`

Returns raw `{ to, data }` call objects for smart wallet batching. Does not send any transactions.

```ts
const calls = markit.trade.buildPlaceBetCalls({
  market: '0x...',
  side: 'no',
  usdcAmount: 25_000_000n,
})
// Returns: [approveCall, placeBetCall]
```

After sending via your smart wallet, call `reportAttribution()` to credit the builder fee.

#### `reportAttribution({ market, txHash, bettor })`

Report a bet for builder fee attribution. Called automatically by `placeBet()`, but must be called manually when using `buildPlaceBetCalls()`.

```ts
const ok = await markit.trade.reportAttribution({
  market: '0x...',
  txHash: '0x...',
  bettor: '0x...',
})
// true if attribution was accepted
```

#### `getPositions(user)`

Get a user's open positions across all markets.

```ts
const positions = await markit.trade.getPositions('0x...')
// [{ market: '0x...', yesShares: 15000000000000000000n, noShares: 0n }]
```

---

### `markit.vault`

The LP vault underwrites all markets. Depositors earn fees from every bet.

#### `getState()`

```ts
const vault = await markit.vault.getState()
console.log(vault.nav)              // total NAV in USDC (6 decimals)
console.log(vault.sharePrice)       // price per share (WAD)
console.log(vault.float)            // undeployed USDC (6 decimals)
console.log(vault.activeMarketCount) // number of live markets
```

#### `getUserPosition(user)`

```ts
const pos = await markit.vault.getUserPosition('0x...')
console.log(pos.shares) // vault shares (WAD)
console.log(pos.value)  // current USDC value (6 decimals)
```

#### `deposit({ usdcAmount, signer })`

Deposit USDC into the vault. Handles approval automatically.

```ts
const txHash = await markit.vault.deposit({
  usdcAmount: 1_000_000_000n, // $1,000
  signer: walletClient,
})
```

#### `requestWithdrawal({ shares, signer })`

Request a withdrawal from the vault (enters FIFO queue).

```ts
const txHash = await markit.vault.requestWithdrawal({
  shares: 500_000_000_000_000_000_000n, // 500 shares (WAD)
  signer: walletClient,
})
```

---

### `markit.builder`

Requires `apiKey` in client config (received during registration at `/builders`).

#### `getStats()`

```ts
const stats = await markit.builder.getStats()
```

Returns `BuilderStats`:

| Field | Type | Description |
|-------|------|-------------|
| `totalVolume` | `number` | All-time bet volume (USD) |
| `totalFees` | `number` | All-time builder fees earned (USD) |
| `totalBets` | `number` | Total bets attributed |
| `uniqueUsers` | `number` | Unique bettors |
| `unpaidFees` | `number` | Fees pending payout (USD) |
| `last30dVolume` | `number` | 30-day rolling volume |
| `last30dFees` | `number` | 30-day rolling fees |

#### `getFeeHistory({ limit? })`

```ts
const fees = await markit.builder.getFeeHistory({ limit: 50 })
// [{ market, bettor, usdcBet, builderFee, txHash, createdAt, paidAt }]
```

---

## Error Handling

All SDK errors throw `MarkitError` with a typed `code`:

```ts
import { MarkitError, ErrorCode } from '@markit.market/sdk'

try {
  await markit.trade.placeBet({ ... })
} catch (err) {
  if (err instanceof MarkitError) {
    switch (err.code) {
      case ErrorCode.TX_REJECTED:        // user rejected in wallet
      case ErrorCode.INSUFFICIENT_USDC:  // not enough USDC balance
      case ErrorCode.BET_TOO_SMALL:      // below minimum bet
      case ErrorCode.SKEW_CAP_EXCEEDED:  // market too skewed
      case ErrorCode.MARKET_NOT_OPEN:    // market not accepting bets
      case ErrorCode.TX_REVERTED:        // on-chain revert
      case ErrorCode.NETWORK_ERROR:      // RPC/fetch failure
      case ErrorCode.INVALID_PARAMS:     // bad input
    }
  }
}
```

## USDC Amounts

USDC uses 6 decimals. Common values:

| Amount | BigInt |
|--------|--------|
| $0.01 | `10_000n` |
| $1 | `1_000_000n` |
| $10 | `10_000_000n` |
| $100 | `100_000_000n` |
| $1,000 | `1_000_000_000n` |

## Testnet

Use Base Sepolia for development:

```ts
const markit = createMarkitClient({
  builderCode: 'your-code',
  chain: 'baseSepolia',
})
```

Testnet USDC can be minted freely via the MockUSDC contract.

## Requirements

- Node.js 18+
- `viem` >= 2.0.0 (peer dependency)

## License

MIT