Closed Beta — Demo trading only. No real funds. Help us find bugs!
    BETA
    FeaturesPricingAbout
    Sign InJoin Beta

    Developer Docs

    Getting StartedBot EnginesExchange IntegrationsArchitectureSecurityDatabaseDeploymentTesting

    User Guide

    Getting StartedTrading BotsAI BotDCA BotSignal BotArbitrage BotPump BotScalping BotGrid BotCombo BotTradingDeFiPortfolioMarketplaceSubscriptionsSettingsAdmin

    BlockbotX Database Schema Reference

    Overview

    • Database: PostgreSQL 16
    • ORM: Drizzle ORM 0.45 with pg (node-postgres) driver
    • Connection Pooling: PgBouncer in transaction mode
    • Financial Precision: All monetary and percentage fields use Decimal(65,30) to prevent floating-point rounding errors
    • Migration Strategy: Schema files in drizzle/schema/*.ts, migrations generated via pnpm db:generate and applied via pnpm db:push or pnpm db:migrate
    • ID Strategy: CUID strings ($defaultFn(() => createId())) on all models except SystemSettings (singleton "singleton" ID)
    • Timestamps: createdAt with .$defaultFn(() => new Date()), updatedAt with .$onUpdate(() => new Date()) where applicable
    • Cascade Deletes: All child records cascade on parent deletion (except SecurityLog which uses SetNull for optional userId, and Trade which uses SetNull for optional botId)
    • Table Naming: All tables use snake_case names as the first argument to pgTable() (e.g., pgTable('users', { ... }))
    • Custom Types: drizzle/custom-types.ts defines a custom decimalNumber type using customType that converts between PostgreSQL Decimal(65,30) and decimal.js Decimal objects

    Data Models by Domain

    Users and Authentication

    User

    The core user model. All other domain models relate back to User.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    emailString.unique()Login email
    passwordStringHashed password
    firstNameString?
    lastNameString?
    avatarString?Avatar URL
    bioString?User biography
    phoneString?Phone number
    locationString?
    websiteString?
    companyString?
    positionString?Job title
    emailVerifiedBoolean.default(false)Whether email is verified
    emailVerifiedAtDateTime?Verification timestamp
    emailVerificationTokenString?Pending verification token
    emailVerificationExpiryDateTime?Token expiry
    resetPasswordTokenString?Password reset token
    resetPasswordExpiryDateTime?Reset token expiry
    twoFactorEnabledBoolean.default(false)2FA toggle
    twoFactorSecretString?TOTP secret
    twoFactorBackupCodesString[]Encrypted backup codes array
    failedLoginAttemptsInt.default(0)Brute-force counter
    lockedUntilDateTime?Account lockout expiry
    statusString.default('active')active, suspended, banned, disabled
    bannedAtDateTime?When banned
    bannedReasonString?Ban reason
    bannedByString?Admin who banned
    suspendedUntilDateTime?Suspension expiry
    suspendedReasonString?Suspension reason
    forcePasswordResetBoolean.default(false)Force reset on next login
    roleString.default('user')user, admin, superadmin
    timezoneString?User timezone
    languageString.default('en')UI language
    themeString.default('light')light, dark
    currencyString.default('USD')Display currency (USD, EUR, GBP, etc.)
    referralCodeString?.unique()Unique referral code
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: email, status Table: users

    Session

    Refresh token sessions for JWT authentication. Expiry is 7 days.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> UserOwning user
    refreshTokenString.unique()JWT refresh token
    expiresAtDateTimeSession expiry (7 days)
    ipAddressString?Client IP at login
    userAgentString?Browser/device user agent
    createdAtDateTime.defaultNow()

    Indexes: userId, refreshToken, expiresAt Cascade: Deletes when User is deleted Table: sessions

    TwoFactorAuth

    TOTP-based two-factor authentication configuration. One-to-one with User.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdString.unique(), FK -> UserOne per user
    secretStringTOTP secret key
    backupCodesString[]Encrypted backup codes
    enabledBoolean.default(false)Whether 2FA is active
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Table: two_factor_auth

    LoginHistory

    Audit trail of all login attempts (successful and failed).

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    ipAddressStringClient IP
    userAgentString?Browser user agent
    successBooleanWhether login succeeded
    failureReasonString?Reason for failure
    createdAtDateTime.defaultNow()

    Indexes: userId, createdAt Table: login_history

    KnownDevice

    Trusted device fingerprints for detecting new-device logins.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    deviceFingerprintStringSHA-256 hash of userAgent
    deviceNameStringParsed browser + OS description
    ipAddressStringIP at time of registration
    lastUsedAtDateTime.defaultNow()Last login from this device
    trustedBoolean.default(true)Trust status
    createdAtDateTime.defaultNow()

    Unique: unique([userId, deviceFingerprint]) Indexes: userId Table: known_devices

    PrivacySettings

    GDPR consent flags and profile visibility preferences. One-to-one with User.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdString.unique(), FK -> UserOne per user
    profileVisibilityString.default('private')public, private, friends
    showEmailBoolean.default(false)
    showPhoneBoolean.default(false)
    allowMessagesFromString.default('contacts')everyone, contacts, nobody
    dataUsageConsentBoolean.default(false)GDPR data usage consent
    marketingEmailsConsentBoolean.default(false)Marketing opt-in
    thirdPartyDataSharingBoolean.default(false)Third-party sharing consent
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Table: privacy_settings

    DataExport

    GDPR data export requests and their processing status.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> UserRequesting user
    exportTypeStringtrades, bots, settings, profile, all
    statusStringpending, processing, completed, failed
    fileUrlString?Download URL when completed
    expiresAtDateTime?Link expiry
    createdAtDateTime.defaultNow()

    Indexes: userId, status Table: data_exports


    Exchanges and Trading

    Exchange

    Metadata about supported exchange platforms.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    nameString.unique()Exchange name
    apiUrlStringREST API base URL
    websocketUrlString?WebSocket endpoint
    supportedPairsJsonArray of trading pairs
    feesJsonFee structure
    activeBoolean.default(true)Whether exchange is enabled
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Relations: tradingPairs, trades, orders Table: exchanges

    ExchangeConnection

    Encrypted API key storage for user exchange connections. Supports Binance and OKX.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> UserOwning user
    exchangeStringbinance, okx, etc.
    encryptedApiKeyStringAES-256-GCM encrypted API key
    encryptedSecretKeyStringAES-256-GCM encrypted secret
    encryptedPassphraseString?OKX passphrase (null for Binance)
    isActiveBoolean.default(true)Connection enabled
    isTestnetBoolean.default(false)true = demo/testnet, false = production
    permissionsString[].default(['read'])read, trade (never withdraw)
    labelString?User-friendly label
    lastUsedDateTime?Last API call timestamp
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Unique: unique([userId, exchange]) -- one connection per exchange per user Indexes: userId, exchange Table: exchange_connections

    TradingPair

    Exchange-specific trading pair configuration with precision rules.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    exchangeIdStringFK -> Exchange
    symbolStringe.g., BTCUSDT
    baseStringBase asset, e.g., BTC
    quoteStringQuote asset, e.g., USDT
    minOrderSizeDecimalMinimum order quantity
    maxOrderSizeDecimal?Maximum order quantity
    pricePrecisionIntDecimal places for price
    quantityPrecisionIntDecimal places for quantity
    activeBoolean.default(true)
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Unique: unique([exchangeId, symbol]) Indexes: symbol Table: trading_pairs

    Trade

    Executed trades -- both paper and live. Central to P&L tracking.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    botIdString?FK -> Bot (SetNull)Bot that executed (null for manual)
    exchangeIdString?FK -> Exchange
    pairIdString?FK -> TradingPair
    positionIdString?FK -> Position
    orderIdString?FK -> Order
    symbolStringTrading pair symbol
    sideStringBUY, SELL
    typeString.default('MARKET')MARKET, LIMIT
    priceDecimalIntended price
    quantityDecimalTrade quantity
    executedPriceDecimalActual execution price
    feeDecimal.default(0)Trading fee
    feeCurrencyString.default('USDT')Fee denomination
    totalDecimal.default(0)Total trade value
    profitLossDecimal?Realized P&L
    balanceDecimal.default(0)Account balance after trade
    statusString.default('FILLED')Trade status
    isPaperTradeBoolean.default(true)Paper vs. live
    executedAtDateTime.defaultNow()Execution timestamp

    Indexes: userId, botId, exchangeId, executedAt, symbol, profitLoss Composite Indexes: [botId, executedAt DESC], [userId, executedAt DESC], [userId, symbol, executedAt DESC], [userId, status, executedAt DESC] Table: trades

    Order

    Order lifecycle management from placement through fill or cancellation.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    botIdString?FK -> Bot (SetNull)
    exchangeIdStringFK -> Exchange
    pairIdStringFK -> TradingPair
    typeStringmarket, limit, stop_loss, take_profit
    sideStringbuy, sell
    priceDecimal?Limit price (null for market)
    quantityDecimalOrder quantity
    filledDecimal.default(0)Filled quantity
    statusStringpending, open, filled, cancelled, failed
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())
    filledAtDateTime?Fill timestamp

    Indexes: userId, botId, status, createdAt Table: orders

    Position

    Open and closed trading positions with full P&L tracking.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    botIdStringFK -> Bot
    symbolStringTrading pair
    sideStringBUY
    quantityDecimalPosition size
    entryPriceDecimalEntry price
    currentPriceDecimalLatest price
    stopLossDecimalStop loss level
    takeProfitDecimalTake profit level
    unrealizedPnLDecimal.default(0)Current unrealized P&L
    unrealizedPnLPercentDecimal.default(0)Unrealized P&L as percentage
    statusString.default('open')open, closed
    exitPriceDecimal?Price at close
    profitLossDecimal?Realized P&L
    profitLossPercentDecimal?Realized P&L percentage
    closeReasonString?STOP_LOSS, TAKE_PROFIT, SIGNAL, BOT_STOPPED
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())
    closedAtDateTime?Close timestamp

    Indexes: botId, status, symbol Composite Indexes: [botId, status] Table: positions

    PortfolioHistory

    Periodic snapshots of portfolio value for charting.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    totalValueDecimalPortfolio value in USD
    assetsJsonArray of assets with amounts and USD values
    timestampDateTime.defaultNow()Snapshot time

    Indexes: userId, timestamp Table: portfolio_history


    Trading Bots

    Bot

    Core bot configuration and lifecycle management.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> UserOwner
    nameStringBot name
    descriptionString?
    typeString.default('ai')ai, signal, dca, arbitrage, pump_screener
    statusString.default('inactive')active, inactive, paused, error
    configJsonBot-type-specific configuration
    riskSettingsJsonRisk management parameters
    paperTradingBoolean.default(true)Paper trading mode
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())
    startedAtDateTime?Last start time
    stoppedAtDateTime?Last stop time
    lastExecutionAtDateTime?Most recent execution

    Indexes: userId, type, status Composite Indexes: [userId, status], [status, lastExecutionAt], [userId, type, status] Relations: executions, trades, orders, positions, performanceSnapshots, backtests, pumpDetections Table: bots

    Execution

    Individual bot execution records -- each decision cycle.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    botIdStringFK -> Bot
    executionIdString.unique()Idempotency key
    signalStringBUY, SELL, HOLD
    confidenceDecimalSignal confidence (0-100)
    reasonStringHuman-readable reason
    priceDecimalPrice at execution time
    indicatorsJsonTechnical indicator values
    statusStringsuccess, error
    errorString?Error message if failed
    timestampDateTime.defaultNow()

    Indexes: botId, timestamp, signal Table: executions

    PerformanceSnapshot

    Periodic bot performance metrics for tracking and display.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    botIdStringFK -> Bot
    timestampDateTime.defaultNow()Snapshot time
    metricsJsonDetailed metrics blob
    riskScoreDecimalComputed risk score
    ratingStringexcellent, good, fair, poor
    totalTradesIntTotal trade count
    winRateDecimalWin percentage
    totalReturnDecimalAbsolute return
    totalReturnPercentDecimalReturn as percentage
    maxDrawdownPercentDecimalMaximum drawdown percentage
    sharpeRatioDecimalRisk-adjusted return metric
    profitFactorDecimalGross profit / gross loss

    Indexes: botId, timestamp Composite Indexes: [botId, timestamp DESC] Table: performance_snapshots

    Backtest

    Backtesting results against historical data.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    botIdStringFK -> Bot
    userIdStringFK -> User
    configJsonBacktestConfig parameters
    resultJsonBacktestReport results
    executionTimeIntDuration in milliseconds
    createdAtDateTime.defaultNow()

    Indexes: botId, userId, createdAt Table: backtests

    HistoricalDataset

    OHLCV candle cache for backtesting, avoiding repeated API fetches.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    symbolStringTrading pair symbol
    timeframeString1m, 5m, 15m, 1h, 4h, 1d
    startDateDateTimeDataset start
    endDateDateTimeDataset end
    candleCountIntNumber of candles
    candlesJsonArray of Candle objects (OHLCV)
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Unique: unique([symbol, timeframe, startDate, endDate]) Indexes: symbol, timeframe, updatedAt Table: historical_datasets

    BotExecution

    Execution tracking specifically for the arbitrage-bot engine.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    botIdStringBot ID (no FK relation)
    statusStringrunning, completed, failed
    logsJson?Execution log entries
    profitLossDecimal?Realized P&L from this execution
    tradesCountInt.default(0)Number of trades executed
    startedAtDateTime.defaultNow()
    endedAtDateTime?Completion time

    Indexes: botId, startedAt Table: bot_executions

    PumpDetection

    Pump screener bot alerts for unusual price/volume activity.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    botIdStringFK -> Bot
    userIdStringFK -> User
    symbolStringDetected symbol
    priceChangeDecimalPercentage price change
    volumeChangeDecimalPercentage volume change
    currentPriceDecimalPrice at detection
    marketCapDecimal?Market capitalization
    volume24hDecimal?24-hour trading volume
    socialMentionsInt.default(0)Social media mention count
    confidenceStringhigh, medium, low
    detectedAtDateTime.defaultNow()

    Indexes: userId, botId, detectedAt, symbol Table: pump_detections

    Analytics

    General analytics snapshots across multiple domains.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    typeStringportfolio, bot_performance, trading, defi, market_analysis
    periodString24h, 7d, 30d, 90d, 1y, all
    dataJsonAnalytics payload
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: userId, type, period, createdAt Composite Indexes: [userId, type, period] Table: analytics


    Signal Providers

    SignalProvider

    External signal sources (webhooks, APIs, Telegram bots).

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    nameStringProvider name
    typeStringwebhook, api, telegram
    urlString?Webhook/API endpoint
    apiKeyString?Encrypted API key
    configJsonProvider-specific configuration
    isActiveBoolean.default(true)
    reliabilityDecimal.default(0)0-100 based on historical accuracy
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: name, type, isActive Relations: signals Table: signal_providers

    Signal

    Individual trading signals from providers.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    providerIdStringFK -> SignalProvider
    symbolStringTrading pair
    actionStringBUY, SELL, HOLD
    priceDecimal?Target price
    stopLossDecimal?Suggested stop loss
    takeProfitDecimal?Suggested take profit
    confidenceDecimal?0-100 confidence score
    reasonString?Signal rationale
    metadataJson?Additional signal data
    processedBoolean.default(false)Whether signal has been acted on
    createdAtDateTime.defaultNow()

    Indexes: providerId, symbol, action, processed, createdAt Composite Indexes: [processed, createdAt] Table: signals


    DeFi and Wallets

    Wallet

    User blockchain wallets for DeFi interactions.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    addressStringBlockchain address
    chainStringethereum, bsc, polygon
    nameString?User-friendly name
    isActiveBoolean.default(true)
    encryptedKeyString?For managed wallets only
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Unique: unique([userId, address, chain]) Indexes: userId, address, chain Relations: stakingPositions, farmPositions, liquidityPositions Table: wallets

    StakingPosition

    Token staking positions across DeFi protocols.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    walletIdStringFK -> Wallet
    protocolStringaave, compound, custom
    chainStringethereum, bsc, polygon
    tokenAddressStringStaked token contract address
    tokenSymbolStringStaked token symbol
    amountDecimalStaked amount
    valueUSDDecimalCurrent USD value
    apyDecimalAnnual percentage yield
    rewardsDecimal.default(0)Accumulated rewards
    stakedAtDateTimeWhen staking began
    lastUpdatedDateTime.defaultNow()Last price/reward update
    statusString.default('active')active, unstaking, completed
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: walletId, protocol, chain, status Composite Indexes: [walletId, status], [chain, status] Table: staking_positions

    FarmPosition

    Yield farming positions with LP token and reward tracking.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    walletIdStringFK -> Wallet
    protocolStringuniswap, pancakeswap, sushiswap
    chainStringBlockchain network
    farmAddressStringFarm contract address
    lpTokenAddressStringLP token contract address
    lpTokenAmountDecimalLP token quantity
    token0SymbolStringFirst token symbol
    token1SymbolStringSecond token symbol
    token0AmountDecimalFirst token amount
    token1AmountDecimalSecond token amount
    valueUSDDecimalCurrent USD value
    apyDecimalAnnual percentage yield
    rewardsEarnedDecimal.default(0)Accumulated rewards
    rewardTokenSymbolString?Reward token symbol
    depositedAtDateTimeDeposit timestamp
    lastHarvestDateTime?Last reward harvest
    lastUpdatedDateTime.defaultNow()Last update
    statusString.default('active')active, withdrawn
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: walletId, protocol, chain, status Composite Indexes: [walletId, status], [chain, status] Table: farm_positions

    LiquidityPosition

    Liquidity pool positions with impermanent loss tracking.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    walletIdStringFK -> Wallet
    protocolStringuniswap-v2, uniswap-v3, pancakeswap
    chainStringBlockchain network
    poolAddressStringPool contract address
    lpTokenAddressStringLP token contract address
    lpTokenAmountDecimalLP token quantity
    token0SymbolStringFirst token symbol
    token1SymbolStringSecond token symbol
    token0AmountDecimalCurrent first token amount
    token1AmountDecimalCurrent second token amount
    initialToken0AmountDecimalInitial first token amount
    initialToken1AmountDecimalInitial second token amount
    initialValueUSDDecimalValue at deposit
    currentValueUSDDecimalCurrent USD value
    impermanentLossDecimal.default(0)IL amount
    feesEarnedDecimal.default(0)Trading fees earned
    addedAtDateTimeLiquidity add timestamp
    lastUpdatedDateTime.defaultNow()
    statusString.default('active')active, removed
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: walletId, protocol, chain, status Composite Indexes: [walletId, status], [chain, status] Table: liquidity_positions

    DeFiProtocol

    Registry of DeFi protocols with TVL and volume metrics.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    nameString.unique()Protocol name
    chainStringPrimary blockchain
    categoryStringdex, lending, staking, yield
    tvlDecimal.default(0)Total value locked
    volume24hDecimal.default(0)24-hour trading volume
    websiteString?Protocol website
    logoString?Logo URL
    descriptionString?Protocol description
    isActiveBoolean.default(true)
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: chain, category, isActive Table: defi_protocols

    LiquidityPool

    Metadata about available liquidity pools.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    protocolStringProtocol name
    chainStringBlockchain network
    pairStringe.g., ETH/USDT
    token0StringFirst token
    token1StringSecond token
    tvlDecimalTotal value locked
    apyDecimalAnnual percentage yield
    volume24hDecimal24-hour volume
    activeBoolean.default(true)
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Unique: unique([protocol, chain, pair]) Indexes: protocol, chain Table: liquidity_pools


    Marketplace

    Strategy

    Trading strategies and bot configurations sold in the marketplace.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    creatorIdStringFK -> UserStrategy author
    nameStringStrategy name
    descriptionStringDescription
    priceDecimalPrice in USD
    typeStringbot_config, trading_strategy
    categoryString?trend_following, arbitrage, etc.
    configJsonStrategy configuration
    performanceJson?Performance metrics
    ratingDecimal.default(0)Average rating
    downloadsInt.default(0)Download/purchase count
    activeBoolean.default(true)Listed status
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: creatorId, category, rating Relations: reviews, purchases Table: strategies

    StrategyReview

    User reviews and ratings for marketplace strategies.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    strategyIdStringFK -> Strategy
    userIdStringReviewer (no FK relation)
    ratingInt1-5 star rating
    commentString?Review text
    createdAtDateTime.defaultNow()

    Unique: unique([strategyId, userId]) -- one review per user per strategy Indexes: strategyId Table: strategy_reviews

    StrategyPurchase

    Purchase records for marketplace strategies.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringBuyer (no FK relation)
    strategyIdStringFK -> Strategy
    priceDecimalPrice paid
    statusString.default('completed')completed, refunded
    createdAtDateTime.defaultNow()

    Unique: unique([userId, strategyId]) -- one purchase per user per strategy Indexes: userId, strategyId Table: strategy_purchases


    Subscriptions and Payments

    Subscription

    User subscription plans with Stripe integration.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    planStringfree, basic, pro, enterprise
    statusStringactive, cancelled, expired
    billingCycleStringmonthly, yearly
    amountDecimalSubscription price
    stripeCustomerIdString?.unique()Stripe customer ID
    stripeSubscriptionIdString?.unique()Stripe subscription ID
    startedAtDateTime.defaultNow()
    expiresAtDateTimeSubscription expiry
    cancelledAtDateTime?Cancellation timestamp
    grantedByString?Admin who granted (for complimentary)
    grantReasonString?Reason for complimentary grant
    isComplimentaryBoolean.default(false)Admin-granted subscription

    Indexes: userId, status Relations: history Table: subscriptions

    SubscriptionHistory

    Audit trail of subscription plan changes.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    subscriptionIdStringFK -> Subscription
    planStringPlan at time of change
    statusStringStatus at time of change
    billingCycleStringBilling cycle at time of change
    amountDecimalAmount at time of change
    changeReasonString?Reason for change
    createdAtDateTime.defaultNow()

    Indexes: subscriptionId Table: subscription_history

    Payment

    Payment transactions for subscriptions and strategy purchases.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    amountDecimalPayment amount
    currencyString.default('USD')Payment currency
    typeStringsubscription, strategy_purchase, etc.
    statusStringpending, completed, failed, refunded
    stripePaymentIdString?.unique()Stripe payment intent ID
    metadataJson?Additional payment data
    createdAtDateTime.defaultNow()
    completedAtDateTime?Completion timestamp

    Indexes: userId, status Table: payments

    Referral

    Referral tracking between users with reward management.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    referrerIdStringFK -> User ("ReferrerReferrals")User who referred
    referredIdString.unique(), FK -> User ("ReferredReferrals")User who was referred
    codeString.unique()Referral code used
    rewardAmountDecimal.default(0)Reward value
    statusString.default('pending')pending, active, rewarded
    createdAtDateTime.defaultNow()
    rewardedAtDateTime?When reward was paid

    Indexes: referrerId, code Table: referrals


    Bot Templates

    BotTemplate

    Pre-built or user-created bot configuration templates.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdString?FK -> UserNull for system templates
    nameStringTemplate name
    descriptionStringTemplate description
    categoryStringtrading, defi, arbitrage
    typeString?dca, grid, ai, signal, etc.
    complexityStringbeginner, intermediate, advanced
    visibilityString.default('private')public, private
    configJsonTemplate configuration
    defaultSettingsJson?Default settings to apply
    tagsString[]Searchable tags
    usageCountInt.default(0)Deployment count
    featuredBoolean.default(false)Homepage featured flag
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: category, complexity, featured, userId, visibility Relations: deployments Table: bot_templates

    TemplateDeployment

    Records of templates deployed as actual bots by users.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    templateIdStringFK -> BotTemplate
    userIdStringDeploying user (no FK relation)
    botIdString?Deployed bot ID (no FK relation)
    customizationJsonUser customizations applied
    deployedAtDateTime.defaultNow()

    Indexes: templateId, userId Table: template_deployments


    Notifications

    Notification

    Multi-channel notification delivery with scheduling and tracking.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    typeNotificationTypeenumSee Key Enums section
    channelNotificationChannelenumDelivery channel
    priorityNotificationPriority.default('NORMAL')LOW, NORMAL, HIGH, CRITICAL
    titleStringNotification title
    messageString.text()Notification body (unlimited length)
    metadataJson?Additional data
    readBoolean.default(false)Read status
    deliveredBoolean.default(false)Delivery status
    deliveredAtDateTime?Delivery timestamp
    failureReasonString?Delivery failure reason
    scheduledForDateTime?Future delivery time
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: userId, read, delivered, createdAt, type, channel, priority Table: notifications

    NotificationPreference

    Per-user, per-channel notification preferences with quiet hours. One-to-one with User.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdString.unique(), FK -> UserOne per user
    emailEnabledBoolean.default(true)Master email toggle
    emailBotAlertsBoolean.default(true)Bot alert emails
    emailTradesBoolean.default(true)Trade notification emails
    emailPaymentsBoolean.default(true)Payment emails
    emailSupportBoolean.default(true)Support ticket emails
    emailMarketingBoolean.default(false)Marketing emails (opt-in)
    inAppEnabledBoolean.default(true)Master in-app toggle
    inAppBotAlertsBoolean.default(true)In-app bot alerts
    inAppTradesBoolean.default(true)In-app trade notifications
    inAppPaymentsBoolean.default(true)In-app payment notifications
    inAppSupportBoolean.default(true)In-app support notifications
    telegramEnabledBoolean.default(false)Telegram toggle
    telegramChatIdString?Telegram chat ID
    telegramBotAlertsBoolean.default(true)Telegram bot alerts
    telegramTradesBoolean.default(true)Telegram trade notifications
    telegramCriticalOnlyBoolean.default(false)Only CRITICAL priority
    digestModeBoolean.default(false)Batch notifications
    digestFrequencyString.default('realtime')realtime, hourly, daily
    quietHoursEnabledBoolean.default(false)Quiet hours toggle
    quietHoursStartInt?Start hour (0-23)
    quietHoursEndInt?End hour (0-23)
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Table: notification_preferences


    Admin and System

    AuditLog

    Audit trail of user actions across the platform.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> UserActing user
    actionStringlogin, api_key_created, trade_executed, etc.
    detailsJson?Action-specific data
    ipAddressString?Client IP
    createdAtDateTime.defaultNow()

    Indexes: userId, action, createdAt Table: audit_logs

    AdminNote

    CRM-style notes that admins attach to user accounts.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User ("UserAdminNotes")Subject user
    authorIdStringFK -> User ("AuthorAdminNotes")Admin who wrote the note
    contentString.text()Note content (unlimited length)
    categoryString.default('general')general, billing, security, compliance
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: userId Table: admin_notes

    SecurityLog

    Security event tracking -- authentication failures, rate limits, suspicious activity.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    typeStringauth_failure, rate_limit, csrf_failure, suspicious_activity
    userIdString?FK -> User (SetNull)Associated user (optional)
    ipAddressStringClient IP
    userAgentStringBrowser user agent
    metadataJson?Event-specific data
    createdAtDateTime.defaultNow()

    Indexes: type, ipAddress, userId, createdAt Cascade: Uses SetNull on User deletion (preserves security logs) Table: security_logs

    BlockedIP

    IP blocklist for automated and manual blocking.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    ipAddressString.unique()Blocked IP address
    reasonStringBlock reason
    blockedAtDateTime.defaultNow()When blocked
    expiresAtDateTime?Block expiry (null = permanent)
    createdByString?Admin user ID or system
    metadataJson?Additional context

    Indexes: ipAddress, expiresAt Table: blocked_ips

    SystemSettings

    Global system configuration. Singleton model (fixed ID "singleton").

    FieldTypeConstraintsDescription
    idStringprimaryKey, .default('singleton')Always "singleton"
    maintenanceModeBoolean.default(false)Maintenance mode toggle
    maintenanceMessageString?User-facing maintenance message
    registrationsOpenBoolean.default(true)Allow new registrations
    updatedAtDateTime.$onUpdate(() => new Date())
    updatedByString?Admin who last updated

    Table: system_settings


    Help and Support

    HelpArticle

    Knowledge base articles for the help center.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    titleStringArticle title
    slugString.unique()URL slug
    contentStringArticle body
    categoryIdStringFK -> HelpCategory
    tagsString[]Searchable tags
    viewsInt.default(0)View count
    publishedBoolean.default(false)Published status
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Indexes: categoryId, published, slug Table: help_articles

    HelpCategory

    Categories for organizing help articles.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    nameString.unique()Category name
    slugString.unique()URL slug
    descriptionString?Category description
    iconString?Icon identifier
    orderInt.default(0)Display order
    createdAtDateTime.defaultNow()

    Relations: articles Table: help_categories

    FAQ

    Frequently asked questions, independent of articles.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    questionStringFAQ question
    answerStringFAQ answer
    categoryIdString?Optional category (no FK relation)
    orderInt.default(0)Display order
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())

    Table: faqs

    SupportTicket

    User support tickets with status and priority tracking.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    userIdStringFK -> User
    subjectStringTicket subject
    categoryStringtechnical, billing, general
    statusString.default('open')open, in_progress, closed
    priorityString.default('normal')low, normal, high, urgent
    assignedToString?Staff member ID
    createdAtDateTime.defaultNow()
    updatedAtDateTime.$onUpdate(() => new Date())
    closedAtDateTime?Close timestamp

    Indexes: userId, status, priority, category Relations: messages Table: support_tickets

    TicketMessage

    Individual messages within a support ticket thread.

    FieldTypeConstraintsDescription
    idStringprimaryKey, $defaultFn(() => createId())Primary key
    ticketIdStringFK -> SupportTicket
    userIdStringFK -> UserMessage author
    contentStringMessage body
    isStaffReplyBoolean.default(false)Staff vs. user message
    createdAtDateTime.defaultNow()

    Indexes: ticketId, createdAt Table: ticket_messages


    Key Enums

    NotificationType (28 values)

    CategoryValues
    Bot AlertsBOT_CREATED, BOT_STARTED, BOT_STOPPED, BOT_ERROR, TRADE_EXECUTED, STOP_LOSS_HIT, TAKE_PROFIT_HIT, DAILY_REPORT, WEEKLY_REPORT
    Payment & SubscriptionPAYMENT_SUCCESS, PAYMENT_FAILED, SUBSCRIPTION_CREATED, SUBSCRIPTION_UPGRADED, SUBSCRIPTION_CANCELLED, SUBSCRIPTION_RENEWED
    SupportTICKET_CREATED, TICKET_MESSAGE, TICKET_STATUS_CHANGED, TICKET_CLOSED
    MarketplaceSTRATEGY_PURCHASED, STRATEGY_REVIEW
    ReferralREFERRAL_JOINED, REFERRAL_ACTIVATED, REWARD_EARNED
    SecurityLOGIN_NEW_DEVICE, PASSWORD_CHANGED, TWO_FA_ENABLED, ACCOUNT_LOCKED

    NotificationChannel (6 values)

    EMAIL, IN_APP, TELEGRAM, DISCORD, PUSH, SMS

    NotificationPriority (4 values)

    LOW, NORMAL, HIGH, CRITICAL

    String-Based Enums (used as String fields with convention)

    FieldModel(s)Allowed Values
    roleUseruser, admin, superadmin
    status (user)Useractive, suspended, banned, disabled
    type (bot)Botai, signal, dca, arbitrage, pump_screener
    status (bot)Botactive, inactive, paused, error
    side (trade)TradeBUY, SELL
    type (trade)TradeMARKET, LIMIT
    type (order)Ordermarket, limit, stop_loss, take_profit
    side (order)Orderbuy, sell
    status (order)Orderpending, open, filled, cancelled, failed
    status (position)Positionopen, closed
    closeReasonPositionSTOP_LOSS, TAKE_PROFIT, SIGNAL, BOT_STOPPED
    signalExecutionBUY, SELL, HOLD
    actionSignalBUY, SELL, HOLD
    type (signal provider)SignalProviderwebhook, api, telegram
    ratingPerformanceSnapshotexcellent, good, fair, poor
    planSubscriptionfree, basic, pro, enterprise
    status (subscription)Subscriptionactive, cancelled, expired
    billingCycleSubscriptionmonthly, yearly
    status (ticket)SupportTicketopen, in_progress, closed
    prioritySupportTicketlow, normal, high, urgent
    category (ticket)SupportTickettechnical, billing, general
    type (analytics)Analyticsportfolio, bot_performance, trading, defi, market_analysis
    periodAnalytics24h, 7d, 30d, 90d, 1y, all
    status (referral)Referralpending, active, rewarded
    status (defi)StakingPositionactive, unstaking, completed
    status (farm)FarmPositionactive, withdrawn
    status (liquidity)LiquidityPositionactive, removed
    confidencePumpDetectionhigh, medium, low
    exportTypeDataExporttrades, bots, settings, profile, all
    category (protocol)DeFiProtocoldex, lending, staking, yield
    type (strategy)Strategybot_config, trading_strategy
    complexityBotTemplatebeginner, intermediate, advanced
    visibilityBotTemplatepublic, private
    profileVisibilityPrivacySettingspublic, private, friends

    Financial Precision

    Why Decimal(65,30) Instead of Float

    Float (IEEE 754 double-precision) introduces rounding errors in financial calculations. For example:

    0.1 + 0.2 = 0.30000000000000004 (Float)
    0.1 + 0.2 = 0.3                 (Decimal)
    

    In a trading platform, these errors accumulate across thousands of trades, leading to:

    • Incorrect P&L calculations
    • Wrong balance displays
    • Flawed trading decisions based on inaccurate data
    • Audit discrepancies

    Fields Using Decimal

    ModelDecimal Fields
    Position (10 fields)quantity, entryPrice, currentPrice, stopLoss, takeProfit, unrealizedPnL, unrealizedPnLPercent, exitPrice, profitLoss, profitLossPercent
    PerformanceSnapshot (7 fields)riskScore, winRate, totalReturn, totalReturnPercent, maxDrawdownPercent, sharpeRatio, profitFactor
    Trade (7 fields)price, quantity, executedPrice, fee, total, profitLoss, balance
    Signal (4 fields)price, stopLoss, takeProfit, confidence
    Execution (2 fields)confidence, price
    SignalProvider (1 field)reliability
    StakingPosition (4 fields)amount, valueUSD, apy, rewards
    FarmPosition (6 fields)lpTokenAmount, token0Amount, token1Amount, valueUSD, apy, rewardsEarned
    LiquidityPosition (9 fields)lpTokenAmount, token0Amount, token1Amount, initialToken0Amount, initialToken1Amount, initialValueUSD, currentValueUSD, impermanentLoss, feesEarned
    DeFiProtocol (2 fields)tvl, volume24h
    LiquidityPool (3 fields)tvl, apy, volume24h
    PortfolioHistory (1 field)totalValue
    Order (3 fields)price, quantity, filled
    TradingPair (2 fields)minOrderSize, maxOrderSize
    Strategy (2 fields)price, rating
    StrategyPurchase (1 field)price
    Subscription (1 field)amount
    SubscriptionHistory (1 field)amount
    Payment (1 field)amount
    Referral (1 field)rewardAmount
    PumpDetection (5 fields)priceChange, volumeChange, currentPrice, marketCap, volume24h
    BotExecution (1 field)profitLoss

    Using Decimal in Application Code

    Decimal columns return Decimal objects (from decimal.js), not JavaScript number values. The custom type in drizzle/custom-types.ts handles automatic conversion between PostgreSQL Decimal(65,30) and decimal.js Decimal objects. Key patterns:

    import Decimal from 'decimal.js';
    
    // Display: convert to number
    const price = position.entryPrice.toNumber();
    
    // Arithmetic: use Decimal methods
    const pnl = position.currentPrice.sub(position.entryPrice).mul(position.quantity);
    
    // Comparison
    if (position.unrealizedPnL.greaterThan(0)) { /* profit */ }
    
    // Creating new Decimal values for writes
    const newPrice = new Decimal("123.456789");
    

    Key Constraints and Indexes

    Unique Constraints

    ModelConstraintPurpose
    UseremailOne account per email
    UserreferralCodeUnique referral codes
    ExchangeConnectionunique([userId, exchange])One connection per exchange per user
    KnownDeviceunique([userId, deviceFingerprint])One entry per device per user
    TradingPairunique([exchangeId, symbol])No duplicate pairs per exchange
    Walletunique([userId, address, chain])No duplicate wallets
    HistoricalDatasetunique([symbol, timeframe, startDate, endDate])No duplicate datasets
    LiquidityPoolunique([protocol, chain, pair])No duplicate pools
    StrategyReviewunique([strategyId, userId])One review per user per strategy
    StrategyPurchaseunique([userId, strategyId])One purchase per user per strategy
    ReferralreferredId (unique)Each user can only be referred once
    Referralcode (unique)Unique referral codes
    SessionrefreshToken (unique)Unique refresh tokens
    ExecutionexecutionId (unique)Idempotency on executions
    SubscriptionstripeCustomerId (unique)One Stripe customer per subscription
    SubscriptionstripeSubscriptionId (unique)One Stripe subscription per record
    PaymentstripePaymentId (unique)One Stripe payment per record
    BlockedIPipAddress (unique)One block entry per IP

    Notable Composite Indexes

    These indexes are designed for the most common query patterns:

    ModelIndexQuery Pattern
    Trade[botId, executedAt DESC]Bot trade history, newest first
    Trade[userId, executedAt DESC]User trade history
    Trade[userId, symbol, executedAt DESC]User trades for a specific symbol
    Trade[userId, status, executedAt DESC]User trades filtered by status
    Bot[userId, status]User's bots by status
    Bot[status, lastExecutionAt]Active bots needing execution
    Bot[userId, type, status]User's bots by type and status
    Position[botId, status]Bot's open/closed positions
    PerformanceSnapshot[botId, timestamp DESC]Latest performance for a bot
    Analytics[userId, type, period]User analytics by type and period
    Signal[processed, createdAt]Unprocessed signals queue
    StakingPosition[walletId, status], [chain, status]Active staking by wallet/chain
    FarmPosition[walletId, status], [chain, status]Active farming by wallet/chain
    LiquidityPosition[walletId, status], [chain, status]Active liquidity by wallet/chain

    Relationships Diagram

    User (users)
     |
     |-- 1:many --> Session (sessions)
     |-- 1:1   --> TwoFactorAuth (two_factor_auth)
     |-- 1:many --> LoginHistory (login_history)
     |-- 1:many --> KnownDevice (known_devices)
     |-- 1:1   --> PrivacySettings (privacy_settings)
     |-- 1:many --> DataExport (data_exports)
     |
     |-- 1:many --> ExchangeConnection (exchange_connections)
     |-- 1:many --> PortfolioHistory (portfolio_history)
     |
     |-- 1:many --> Bot (bots)
     |                |-- 1:many --> Execution (executions)
     |                |-- 1:many --> Trade (trades)
     |                |-- 1:many --> Order (orders)
     |                |-- 1:many --> Position (positions)
     |                |               |-- 1:many --> Trade (trades)
     |                |-- 1:many --> PerformanceSnapshot (performance_snapshots)
     |                |-- 1:many --> Backtest (backtests)
     |                |-- 1:many --> PumpDetection (pump_detections)
     |
     |-- 1:many --> Trade (trades)     [direct user trades]
     |-- 1:many --> Order (orders)     [direct user orders]
     |
     |-- 1:many --> Wallet (wallets)
     |                |-- 1:many --> StakingPosition (staking_positions)
     |                |-- 1:many --> FarmPosition (farm_positions)
     |                |-- 1:many --> LiquidityPosition (liquidity_positions)
     |
     |-- 1:many --> Strategy (strategies)
     |                |-- 1:many --> StrategyReview (strategy_reviews)
     |                |-- 1:many --> StrategyPurchase (strategy_purchases)
     |
     |-- 1:many --> Subscription (subscriptions)
     |                |-- 1:many --> SubscriptionHistory (subscription_history)
     |-- 1:many --> Payment (payments)
     |
     |-- 1:many --> Referral (referrals)  [as referrer]
     |-- 1:1   --> Referral (referrals)  [as referred, unique]
     |
     |-- 1:many --> Notification (notifications)
     |-- 1:1   --> NotificationPreference (notification_preferences)
     |
     |-- 1:many --> AuditLog (audit_logs)
     |-- 1:many --> AdminNote (admin_notes)  [as subject]
     |-- 1:many --> AdminNote (admin_notes)  [as author]
     |-- 1:many --> SecurityLog (security_logs)
     |
     |-- 1:many --> SupportTicket (support_tickets)
     |                |-- 1:many --> TicketMessage (ticket_messages)
     |-- 1:many --> TicketMessage (ticket_messages)  [as message author]
     |
     |-- 1:many --> Backtest (backtests)
     |-- 1:many --> BotTemplate (bot_templates)
     |                |-- 1:many --> TemplateDeployment (template_deployments)
     |-- 1:many --> PumpDetection (pump_detections)
     |-- 1:many --> Analytics (analytics)
    
    
    Exchange (exchanges)
     |-- 1:many --> TradingPair (trading_pairs)
     |-- 1:many --> Trade (trades)
     |-- 1:many --> Order (orders)
    
    
    SignalProvider (signal_providers)
     |-- 1:many --> Signal (signals)
    
    
    HelpCategory (help_categories)
     |-- 1:many --> HelpArticle (help_articles)
    
    
    Standalone Models (no parent relations):
     - BotExecution (bot_executions)   [botId is plain String, no FK]
     - DeFiProtocol (defi_protocols)
     - LiquidityPool (liquidity_pools)
     - FAQ (faqs)
     - BlockedIP (blocked_ips)
     - SystemSettings (system_settings)  [singleton]
    

    Migration Strategy

    Schema is defined in TypeScript files under drizzle/schema/*.ts (e.g., users.ts, trading.ts, bots.ts, defi.ts). Each file exports pgTable() definitions. Migrations are stored in drizzle/migrations/.

    Workflow:

    1. Edit schema -- modify the relevant drizzle/schema/*.ts file
    2. Generate migration -- pnpm db:generate (runs drizzle-kit generate)
    3. Apply to database -- pnpm db:push (runs drizzle-kit push) for development, or pnpm db:migrate (runs drizzle-kit migrate) for production
    4. Inspect visually -- pnpm db:studio (runs drizzle-kit studio)
    5. Seed data -- pnpm db:seed (runs tsx drizzle/seed.ts)

    No generate step needed -- unlike Prisma, Drizzle uses TypeScript schema files directly. There is no client generation step.

    AI-powered crypto trading platform in closed beta. 8 bot engines, DeFi integration, and demo trading.

    Product

    • Features
    • Pricing
    • About
    • Join Beta

    Resources

    • Documentation
    • API Reference
    • Support
    • Blog
    • Status

    Legal

    • Terms of Service
    • Privacy Policy
    • Disclaimer

    © 2026 BlockbotX. All rights reserved.

    Built with AI · Powered by Next.js