Appearance
Portfolio & history
Balances, chain distribution and a per-product transaction record.
Routes: /defi/overview, /defi/swap-history, /defi/bridge-history, /defi/perpetual-transaction, /defi/liquidity-history
Portfolio overview
Source: defi-dex/src/pages/defi/overview.tsx · Data: Zerion
Portfolio data comes from Zerion's wallet API, not from Blazpay's own indexing:
GET https://api.zerion.io/v1/wallets/{wallet}/positions/
?filter[positions]=only_simple
¤cy={currency}
&filter[trash]=only_non_trash
&sort=valuefilter[trash]=only_non_trash matters — without it a wallet's view is dominated by airdropped spam tokens.
bz-backend/services/portfolio.js exposes two shapes:
| Function | Returns |
|---|---|
getSummary(wallet) | Total value, chain distribution, headline numbers |
getPortfolio(wallet) | Full position list |
Both are consumed by the wallet reputation card as well as the portfolio page, and by the PORTFOLIO AI intent.
In BlazAI
The PORTFOLIO intent renders portfolio/UserPortfolio (action portfolio) with charts and distribution inline in chat. CHECK_BALANCE is a narrower intent for a specific token or chain, and is also used for follow-up questions about the worth or price of tokens already discussed.
Transaction history
Blazpay keeps its own record of every trade it originated, in bz-backend/models/defiTransactions.js. This is separate from the chain — it exists so that a user can see a bridge that is still in flight, a trade whose hash has not been mined yet, and a human-readable route description.
The UUID-first pattern
A transaction record is created before the on-chain hash exists:
sendTransactionRawgenerates a UUID and writes a record withtxHash = <uuid>.- It pushes
?hash=<uuid>into the browser URL, so a reload recovers the in-flight trade. - Once the wallet returns the real hash, it
PATCHes/defi/transaction/hash/<uuid>to replace it.
If you find records whose txHash looks like a UUID rather than 0x…, those are trades that never got a hash — the user rejected the wallet prompt, or the tab closed.
Endpoints
| Purpose | Endpoint |
|---|---|
| List a wallet's transactions | GET /defi/transaction/:walletAddress |
| One transaction by id | GET /defi/transaction/:walletAddress/:id |
| By hash | GET /defi/tx/:hash, GET /defi/txHash/:txHash |
| Most recent for a wallet | GET /defi/tx/wallet/:address |
| Refresh pending statuses | GET /defi/txn/:walletAddress |
| Create | POST /defi/transaction |
| Update by uuid or hash | PATCH /defi/transaction/hash/:hash |
| Bridge status update | PATCH /defi/transaction/update |
Full detail: trading API reference.
Status refresh
GET /defi/txn/:walletAddress walks that wallet's pending records and asks each provider's status endpoint (getTxStatus(chainId, hash) on the relevant aggregator, using the routers map in swap-sdk/src/utils/constants.ts). bridgeStatusCron.js does the same in the background so a user who closes the tab still gets a resolved record.
Per-product history surfaces
| Route | Records |
|---|---|
/defi/swap-history | Same-chain swaps |
/defi/bridge-history | Cross-chain transfers, including in-flight |
/defi/perpetual-transaction | Orderly orders and fills |
/defi/liquidity-history | Reserved; liquidity is not shipped |
/defi/transactiondetails | Single-transaction detail view |
CSIP fills are separate — models/DcaFill.js, surfaced in the CSIP position detail rather than the generic history. Autopilot activity is separate again — models/autopilotTradeModel.js and autopilotEventModel.js, surfaced via GET /defi/autopilot/:chainId/:agentId/activity.
Other data sources
| Data | Source |
|---|---|
| Balances (portfolio) | Zerion |
| Wallet token balances (init) | GET /defi/init/:address → controllers/defi/init.js |
| Price charts | getGraphPrice in defi-dex/src/apis/defi.api.ts |
| Token prices for automation | CoinMarketCap, batched into a Redis rolling series (services/autopilotPrices.js) |
| Fee treasury totals | CoinGecko, via GET /defi/fees/summary |
| Per-chain first/last activity | Etherscan v2 txlist |
Three different price sources, deliberately
CoinMarketCap feeds automation (it needs a consistent series for dip-buy and take-profit triggers). CoinGecko feeds the fee summary (one-off, batch, free tier is fine). Zerion feeds portfolio display (it already prices positions, so re-pricing would be duplicated work). Do not consolidate them without checking that the replacement supports rolling series.
Known limits
- Portfolio totals exclude anything not in a wallet. Perp collateral inside Orderly, CSIP vault balances and Autopilot vault balances do not appear. A user with most of their capital in automation sees a misleadingly small portfolio.
- Zerion coverage defines chain coverage. A chain Blazpay supports for swaps but Zerion does not index shows a zero balance.
- "Oldest hold" on the wallet card defaults to wallet age, not to per-token entry time — that would need an ERC-20
Transferlog index per chain. - No unified cross-product PnL. Each surface reports its own.
- UUID-hash records can accumulate. There is no cleanup job for records whose hash never resolved.