Appearance
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
Network — models/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.
Token — models/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
defiTransactions — models/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.
Related
| Collection | Purpose |
|---|---|
limitOrder | Shared between perp orders and the unshipped spot product — filter by source |
orderlyAccount | Orderly account id plus the KMS-encrypted ed25519 key |
liquidityTransactions | Liquidity history (product unshipped) |
buySellOrders | Fiat ramp orders |
transactionModel, transactionV2Model, tokenTransactionModel, tokenPaymentModel | Ecosystem-layer payments |
history | Legacy |
Provider catalogue caches
Refreshed by administrative endpoints, not by a cron:
alchemyCrypto, alchemyFiat, changellyCrypto, changellyFiat, onRampCrypto, transacCrypto, transacFiat, geckoLists, exchangeRateModel.
Automation
| Collection | Contents |
|---|---|
DcaInvestment | One CSIP position. Carries lastError / lastAttemptAt (cleared on success, shown as amber "retrying"), plus on-chain-mirrored processed, status, depositTime, interval |
DcaFill | One executed order. Unique (txHash, positionId) |
autopilotAgentModel | Agent doc — policy, balances, lastFillAt per strategy id, synced on-chain state |
autopilotTradeModel | Executed Autopilot trades |
autopilotEventModel | Activity feed events |
priceAlertModel | Price alerts |
monitoredTransactionModel | Transactions 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
| Collection | Contents |
|---|---|
blazSession | { wallet, name, pending, activeDappProjectId } |
blazMessage | { session, senderType, text, action, data } |
blazNotificationModel | In-app notifications; includes the autopilot type and expiryWarnedAt |
trainingDataModel | Review pipeline for prompt refinement |
feedbackModel | User 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
| Collection | Contents |
|---|---|
contractAuditModel | auditId, certificateId, wallet, score, findings[], severityCounts, certificateEligible, status |
tokenSafetyScanModel | Rug-check scans, reused within 6 hours per (address, chainId) |
walletCardModel | Cached reputation cards |
approvalConfigModel | Approval-scan configuration |
dApp builder
dappProjectModel (versions, iterationsUsed/maxIterations), dappSubscriptionModel, generatedDappModel, dappModel, managedAccountModel (ZeroDev smart accounts), caSca and caActivity (smart-contract-account records).
Platform
| Collection | Purpose |
|---|---|
systemSettingModel | Feature flags and limits. Categories: bridge, security, network, fees, limits, general. Statics: getValue(key), setValue(key, value), getByCategory(cat) |
userModel | Users, Blazpay usernames, referrals |
adminUser, adminActionModel, adminNotificationModel | Admin |
networkStatusModel | Per-chain health |
loginRequestModel, authorizedCode | Auth |
notificationModel, pointsModel | Cross-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.
| Group | Collections |
|---|---|
| NFT & staking | rocketModel, rocketUpgradeCostModel, blazBobModel, nftRewardsModel, userNFTInventoryModel, marketplaceNFTModel, stakingData, stakingInfo, {bnb,polygon,manta,hekla,holesky}Stakings |
| Marketplace | MarketplaceListing, MarketplaceOffer, cartModel, productModel |
| Rewards | rewardModel, ClaimRewards, claimModel, autoClaimModel, autoClaimLogModel, weeklyDropModel, ClaimMonadModel |
| Quests | dailyTask, missionModel, userMissionProgressModel, userStreakModel, attendanceModel, dailyRankingModel, leaderboardModel, dailyUpdateModel |
| Games | game, gameWalletModel, MinesGame, RouletteSpin, predictionMarketModel, predictionBetModel, userPredictionStatsModel, pokerModel, pokerHandHistory |
| Presale | presaleWallet, presaleDeposit, presaleAudit, whitelistModel, depositModel |
| DAO | daoQuestionModel, daoVoteModel, daoCommentModel |
| Content | blogModel, authorModel, newsModel, threadModel, faqModel, carouselModel, trendingTopicsModel |
| Support | supportTicket, support, questionModel, messageModel |
| Creators | clippersCampaign, clippersSubmission, smmModel |
email, sentEmailModel, sesSchedule, unsubscribeModel | |
| Internal | kanban{Board,Column,Card,Activity}Model, migrationModel |
| Chain-specific | qubeticsModel, lightLinkModel, lightLinkRewardModel, lightLinkBuyNFT, chainModel |
| Misc | entryPassModel, 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:
| Mongo | Mirror |
|---|---|
blazMessage.action enum (BLAZ_ACTIONS) | IAIAction in defi-dex/src/types/ai-chat.type.ts |
blazMessage.data subdocument | IChatMessage['data'] in the same file |
utils/policy.js policy shape | defi-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.