Skip to content

Data model

MongoDB, roughly 130 collections. This page covers the ones that matter for the trading and AI cores, plus a map of the rest.

The two that everything depends on

Networkmodels/networkModal.js

js
{
  chainType: String,          // 'evm' | 'solana' | 'tron'
  coin: String,
  id: Number,                 // ⚠ the canonical chain key across the platform
  key: String,
  logoURI: String,
  mainnet: Boolean,
  metamask: Object,           // { rpcUrls: [...], chainName, nativeCurrency, ... }
  multicallAddress: String,
  name: String,
  nativeToken: Object,
  tokenlistUrl: String,
  order: Number,              // display ordering
  aggregators: [String],      // which providers to try on this chain
  show: { type: Boolean, default: true },
}
index: { id: 1 }

id is the platform's chain key, and it is not always a real chain id

For EVM chains it equals the EVM chain id. For Solana it is the synthetic 102; for TRON it is 728126428. Anything treating id as EVM-shaped breaks on the non-EVM two. See non-EVM paths.

metamask.rpcUrls[0] is the RPC the backend uses for everything — signing (signTx loads it per chainId), the fee summary, and vault interaction. A bad RPC in this record takes down that chain silently.

aggregators: [String] is a per-chain provider allow-list, distinct from the global excludeSwap/excludeBridge defaults.

show: false hides a chain from the UI without deleting the record — the correct way to disable a chain.

Tokenmodels/TokenModal.js

js
{
  address: String,
  chainId: Number,            // matches Network.id
  symbol: String,
  decimals: Number,
  name: String,
  coinKey: String,
  logoURI: String,
  coingeckoId: String,        // used by the fee summary
  cmcId: Number,              // used by Autopilot prices
  ath, atl, high24h, low24h: Number,
  lastStatsUpdate: Date,
  order: Number,              // ⚠ drives picker visibility
  changellySymbol: String,
  transacCryptoId: ObjectId → TransacCrypto,
}
indexes: { address: 1 }, { chainId: 1 }

order is why a token "does not exist"

The token picker slices the top 200 by order. A token present in the collection but ranked below 200 appears absent. "USDT is missing on chain X" is almost always a ranking problem. Fix: scripts/curate-token-order.js. See token curation.

Two price-source ids coexist: coingeckoId (fee summary) and cmcId (Autopilot). Both are needed; neither is a superset.

Native assets are resolved through multiple sentinel addresses — resolveTokenAddress in the swap controller handles 0x0, 0xeee… and Polygon's 0x…1010.

Trading

defiTransactionsmodels/defiTransactions.js

Blazpay's own record of every trade it originated. Separate from the chain so an in-flight bridge is visible.

txHash starts as a UUID

sendTransactionRaw creates the record with a UUID, then PATCH /defi/transaction/hash/<uuid> swaps in the real hash. Records whose txHash still looks like a UUID are trades that never got one — the user rejected the wallet prompt or closed the tab. There is no cleanup job.

CollectionPurpose
limitOrderShared between perp orders and the unshipped spot product — filter by source
orderlyAccountOrderly account id plus the KMS-encrypted ed25519 key
liquidityTransactionsLiquidity history (product unshipped)
buySellOrdersFiat ramp orders
transactionModel, transactionV2Model, tokenTransactionModel, tokenPaymentModelEcosystem-layer payments
historyLegacy

Provider catalogue caches

Refreshed by administrative endpoints, not by a cron:

alchemyCrypto, alchemyFiat, changellyCrypto, changellyFiat, onRampCrypto, transacCrypto, transacFiat, geckoLists, exchangeRateModel.

Automation

CollectionContents
DcaInvestmentOne CSIP position. Carries lastError / lastAttemptAt (cleared on success, shown as amber "retrying"), plus on-chain-mirrored processed, status, depositTime, interval
DcaFillOne executed order. Unique (txHash, positionId)
autopilotAgentModelAgent doc — policy, balances, lastFillAt per strategy id, synced on-chain state
autopilotTradeModelExecuted Autopilot trades
autopilotEventModelActivity feed events
priceAlertModelPrice alerts
monitoredTransactionModelTransactions under watch

autopilotAgentModel.lastFillAt is a map keyed by strategy id

That is how the trigger engine knows a dca cadence is due and how the take-profit anti-hammer guard works. A failed attempt deliberately does not advance it.

BlazAI

CollectionContents
blazSession{ wallet, name, pending, activeDappProjectId }
blazMessage{ session, senderType, text, action, data }
blazNotificationModelIn-app notifications; includes the autopilot type and expiryWarnedAt
trainingDataModelReview pipeline for prompt refinement
feedbackModelUser feedback

blazMessage.data is a bag of named optional fields — one per action shape. See chat request path.

Three unrelated chat data models

blazSession/blazMessage is BlazAI. chatModel/chattModel/messageModel is user-to-user and support chat. AIChat/AIMessages is the deprecated swap-ai path. Querying the wrong one is an easy mistake.

Safety & audit

CollectionContents
contractAuditModelauditId, certificateId, wallet, score, findings[], severityCounts, certificateEligible, status
tokenSafetyScanModelRug-check scans, reused within 6 hours per (address, chainId)
walletCardModelCached reputation cards
approvalConfigModelApproval-scan configuration

dApp builder

dappProjectModel (versions, iterationsUsed/maxIterations), dappSubscriptionModel, generatedDappModel, dappModel, managedAccountModel (ZeroDev smart accounts), caSca and caActivity (smart-contract-account records).

Platform

CollectionPurpose
systemSettingModelFeature flags and limits. Categories: bridge, security, network, fees, limits, general. Statics: getValue(key), setValue(key, value), getByCategory(cat)
userModelUsers, Blazpay usernames, referrals
adminUser, adminActionModel, adminNotificationModelAdmin
networkStatusModelPer-chain health
loginRequestModel, authorizedCodeAuth
notificationModel, pointsModelCross-cutting

Use systemSettingModel for new flags and limits

It already backs the audit quota (audit_free_limit_per_wallet, default 3) and four Autopilot settings. Adding a bespoke config collection duplicates it.

Ecosystem layer

Grouped rather than enumerated — full list in ecosystem map.

GroupCollections
NFT & stakingrocketModel, rocketUpgradeCostModel, blazBobModel, nftRewardsModel, userNFTInventoryModel, marketplaceNFTModel, stakingData, stakingInfo, {bnb,polygon,manta,hekla,holesky}Stakings
MarketplaceMarketplaceListing, MarketplaceOffer, cartModel, productModel
RewardsrewardModel, ClaimRewards, claimModel, autoClaimModel, autoClaimLogModel, weeklyDropModel, ClaimMonadModel
QuestsdailyTask, missionModel, userMissionProgressModel, userStreakModel, attendanceModel, dailyRankingModel, leaderboardModel, dailyUpdateModel
Gamesgame, gameWalletModel, MinesGame, RouletteSpin, predictionMarketModel, predictionBetModel, userPredictionStatsModel, pokerModel, pokerHandHistory
PresalepresaleWallet, presaleDeposit, presaleAudit, whitelistModel, depositModel
DAOdaoQuestionModel, daoVoteModel, daoCommentModel
ContentblogModel, authorModel, newsModel, threadModel, faqModel, carouselModel, trendingTopicsModel
SupportsupportTicket, support, questionModel, messageModel
CreatorsclippersCampaign, clippersSubmission, smmModel
Emailemail, sentEmailModel, sesSchedule, unsubscribeModel
Internalkanban{Board,Column,Card,Activity}Model, migrationModel
Chain-specificqubeticsModel, lightLinkModel, lightLinkRewardModel, lightLinkBuyNFT, chainModel
MiscentryPassModel, paymentModel, sendPatternModel, AIChat, AIMessages

Indexing

Explicit indexes are sparse — Network { id }, Token { address } and { chainId }, DcaFill { txHash, positionId } unique. Most collections rely on the default _id index plus whatever Atlas has auto-suggested.

Query patterns that deserve an index and may not have one

defiTransactions by walletAddress, blazMessage by session, blazSession by wallet, autopilotAgentModel by wallet and by (chainId, agentId). These are the hot read paths for the portfolio, chat history and Autopilot list endpoints. Check db.collection.getIndexes() before assuming.

Cross-repo type mirrors

Three places where a schema is duplicated by hand and must be changed together:

MongoMirror
blazMessage.action enum (BLAZ_ACTIONS)IAIAction in defi-dex/src/types/ai-chat.type.ts
blazMessage.data subdocumentIChatMessage['data'] in the same file
utils/policy.js policy shapedefi-dex/src/utils/autopilotPolicy.ts — must be byte-identical, the hash depends on it

Local development

Local dev points at production Atlas by default. Network Access includes 223.233.0.0/16 for rotating developer ISP IPs.

You are one query away from production data

bz-backend's local .env uses the production connection string, and there are dozens of loose maintenance scripts at the repo root (fix_admin_and_txs.js, delete_remaining_anomalies.js, migrate_exempt.js, …) that write. Read local development before running any of them.

Internal + developer documentation. Usage figures are point-in-time snapshots — re-verify before publishing them.