content


Key Takeaways
A tracking plan is the shared schema between product, engineering, and analytics teams. Without one, event names drift between sprints, properties go missing, and funnels break silently.
Use the Object Action naming convention throughout: a noun followed by a past-tense verb. For example: Swap Initiated, Deposit Completed, Liquidation Occurred. This keeps events consistent as the team grows.
Always instrument P0 events first. For a DEX, that means Swap Initiated and Swap Completed. For lending, it is Supply Initiated and Borrow Initiated. For vaults, it is Deposit Initiated and Deposit Completed. Validate on staging before adding P1 and P2 events.
Pass the reserved volume property on every transaction event. It surfaces directly in the Formo dashboard as protocol volume without any additional configuration.
Fire intent events (e.g. Swap Initiated, Deposit Initiated) before the wallet confirmation prompt appears. This lets you measure drop-off between user intent and actual submission, which is one of the highest-leverage funnel insights in DeFi.
Never manually fire events that the Formo SDK auto-tracks (wallet connects, chain switches, transactions). Doing so doubles event counts and makes retention curves unreliable.
Capture liquidation events from your indexer or contract event listener, not the frontend. The user may not have the app open when a liquidation occurs.
A tracking plan is the analytics contract between your product team, marketing, engineers, and data analysts. It defines every event your app emits: what it is called, when it fires, and which properties are in it.
Without a clear plan, event names drift between sprints, properties go missing, and funnels break the moment someone renames an event. With one, every team member ships to the same schema and your product analytics stay reliable as the app grows.
This guide covers production-ready tracking plans for the 3 most common DeFi app types:
DEX: swap execution, limit orders, cross-chain swaps, and liquidity provision
Lending: market discovery, supply, borrow, repay, and liquidation
Yield Vaults: vault discovery, deposit, withdraw, yield claims, and strategy migrations
Each plan uses the Formo SDK (@formo/analytics) and the Object Action naming convention. Every event includes a working code snippet and a full property schema.
Generate your own tracking plan at Formo.so/tracking-plan-generator
What This Guide Covers
This comprehensive guide shows you how to build a tracking plan, how to set up analytics for your DeFi app, and best practices for DeFi founders, product, and growth teams.
Section | What it covers |
What is a tracking plan | Definition, structure, and how it differs from a spec doc |
Why analytics matters in DeFi | The cost of skipping it and what it unlocks in DeFi |
Install Formo | Package install, provider setup, and firing custom events |
Part 1: DEX | 26 events across swap execution, limit orders, cross-chain, and liquidity |
Part 2: Lending | 14 events across market discovery, supply, borrow, repay, and liquidation |
Part 3: Yield Vaults | 12 events across vault discovery, deposit, withdraw, yield, and strategy |
Best practices | 8 implementation rules that apply across all 3 app types |
Common mistakes | 7 mistakes that corrupt funnels and how to avoid them |
FAQs | Answers to the most common implementation questions |
What Is a Tracking Plan?
A tracking plan is a shared document that defines every event your app emits: what it is called, when it fires, and which properties travel with it. It is the contract between your product team, engineers, and analysts.
Without one, event names drift between sprints, properties go missing, and funnels break the moment someone renames an event. With one, every team member ships to the same schema and your product analytics stay reliable as the app grows.
What Is in the Tracking Plan
Element | Description |
Event name | The standardised name following the Object Action convention (e.g. Swap Initiated) |
Trigger | The exact user action or system condition that fires the event |
Priority | P0 (core funnel), P1 (supporting signals), P2 (discovery and UX) |
Properties | Every key-value pair sent with the event, with type and description |
Notes | Implementation guidance for engineers (debounce rules, source of truth, etc.) |
How a Tracking Plan Differs From a Spec Doc
A spec doc describes what a feature should do. A tracking plan describes what data your feature should emit. The two are complementary: the spec drives the build, the tracking plan drives the measurement. In DeFi, where onchain and offchain data need to stay in sync, having both is essential.
Why Do You Need a Tracking Plan?
DeFi apps face a measurement problem that traditional apps do not. Every user action has two layers: the offchain interaction (clicking Swap, entering an amount, opening a review modal) and the onchain outcome (the transaction hash, the confirmed volume, the gas used). A tracking plan is what keeps those two layers connected and queryable.
The cost of shipping without one
Teams that skip the tracking plan phase typically hit the same set of problems within the first few sprints:
Inconsistent event names. One engineer fires swap_completed, another fires Swap Complete, and a third fires SwapDone. All 3 refer to the same event. None of them joins cleanly in your analytics dashboard.
Missing properties. Swap Initiated fires without volume, so you have transaction counts but no USD value. You cannot measure protocol revenue from the event stream alone.
Funnel breaks. A renamed event silently drops out of a saved funnel. The conversion rate appears to collapse. The team spends a sprint investigating a data problem, not a product problem.
Duplicate events. Auto-tracked events get manually re-fired. Counts double. Retention curves become unreliable.
The earlier in the product lifecycle you write the tracking plan, the less cleanup you do later. Retrofitting event schemas onto a live app with existing users is significantly harder than shipping them correctly from the start.
What Analytics Unlocks For DeFi Teams
Building without analytics is like wandering a dark forest. You might feel like you‘re moving, but the path is unclear. Moving with a light source illuminates the path so you can reach where you want to go, faster.
Analytics helps you understand your users, where they come from, and what they do so you can build better products and grow with confidence.
Here are the benefits of having proper analytics and attribution for crypto teams:
Conversion measurement. With a tracking plan in place, you can measure the full funnel from Page Viewed to Swap Review Opened to Swap Initiated to Swap Completed in a single funnel report. Every step is named consistently, and every property is present.
Volume and revenue tracking: The Formo SDK reserves the volume property for USD transaction value. A tracking plan ensures every transaction event passes volume correctly, so your charts show protocol volume alongside event counts without any post-hoc joins.
Wallet-level user profiles: When events carry consistent properties like wallet_address, chain, and asset, Formo can stitch them into wallet profiles that show the full history of each user across sessions and chains.
Reliable retention analysis: Retention cohorts depend on a consistent definition of what counts as an active session. A tracking plan locks that definition down so your week-over-week numbers are comparable.
DeFi Tracking Plan and Analytics Best Practices
These practices apply across all 3 DeFi app types covered in this guide.
1. Use Object Action naming throughout
Every event in this guide follows the Object Action pattern: a noun describing what was acted on, followed by a past-tense verb describing what happened.
Object | Action | Full Event Name |
Swap | Initiated |
|
Limit Order | Placed |
|
Supply | Completed |
|
Deposit | Initiated |
|
Vault | Viewed |
|
This convention keeps events consistent across your team, makes funnels readable at a glance, and ensures your analytics dashboard stays interpretable as the team grows. Formo's custom events documentation recommends this [Noun] + [Past-Tense Verb] standard for all implementations.
Reserved properties: volume, revenue, and points
The Formo SDK has 3 reserved properties that surface directly in your dashboard:
Property | Type | Use in DeFi |
| number | USD value of the swap, deposit, supply, or borrow. Use on every transaction event. |
| number | Protocol fee earned from the transaction. Must be non-negative. |
| string | Abstract points or XP value, if your app runs a points program. |
See the full reference.
2. Prioritise P0 events first
Instrument your core conversion funnel before anything else. P0 events are the ones that directly measure whether users complete the primary action in your app. For a DEX, that is, Swap Initiated and Swap Completed. For a lending app, that is Supply Initiated and Borrow Completed. For a yield vault, that is Deposit Initiated and Deposit Completed.
P1 and P2 events add signal but do not change the core funnel picture. Ship P0 first, validate the data, then expand.
3. Pass volume on every transaction event
The Formo SDK reserves volume for USD transaction value. It surfaces directly in your dashboard without any additional configuration. Pass it on to every event that represents a volume-generating action.
4. Fire intent events before the wallet confirmation
For events like Swap Review Opened, Supply Initiated, and Deposit Initiated, fire the event before the wallet confirmation prompt appears. This lets you measure drop-off between user intent and actual submission, which is one of the highest-leverage funnel insights in DeFi.
5. Apply a 500ms debounce to input events
Events triggered by typing, such as Amount Entered and Pool Searched, should use a 500ms debounce. Firing on every keystroke inflates event counts and makes the data noisy.
6. Capture server-side events from your indexer
Some events, such as Liquidation events, should be captured from the backend or smart contract indexer, not from the frontend. Frontend events are unreliable for liquidations because the user may not have the app open when the liquidation happens.
See the Formo custom events docs for server-side tracking options and contract docs for smart contract events.
7. Test on staging before deploying to production
Before going live, verify each event fires with the correct name and properties using your browser's network tab or the Formo dashboard. A single misspelt property name on a P0 event can corrupt weeks of funnel data.
Common Tracking Plan Mistakes
These are the most frequent implementation errors across DeFi teams, and the ones that cause the most downstream pain.
Manually firing auto-tracked events
The Formo SDK auto-tracks Page Viewed and wallet activity, such as connecting a wallet, switching chains, and transactions. Calling formo.track() on any of these creates duplicates. Your event counts double, your retention curves become unreliable, and your funnels produce inflated numbers. The auto-tracked events in this guide are documented for reference only. Do not re-fire them.
Skipping volume on volume-generating events
The volume property is reserved by Formo and surfaces directly in your dashboard as protocol volume. Teams that omit it end up with accurate event counts but no USD value attached to those events. Track it from day one on every volume-generating event.
Firing events only after the wallet confirmation
Teams commonly fire Swap Completed or Deposit Completed only after the transaction confirms, but miss the Swap Initiated or Deposit Initiated events. This collapses two separate funnel steps into one and makes it impossible to measure how many users reach the review stage but drop off before signing. Fire intent events before the signature prompt, completion events after confirmation.
Tracking too many P2 events before P0 is stable
P2 events like Token Pair Reversed and Vault Filter Applied are useful for UX optimisation, but add no signal to your core conversion funnel. Teams that instrument P2 events first often end up with rich discovery data and no reliable measure of whether users complete a swap or deposit. Always stabilise P0 before expanding.
Not reviewing the plan with stakeholders before implementation
A tracking plan that only engineers have seen tends to miss the questions that product and growth teams need answered. Review the plan with all stakeholders before implementation. The Formo tracking plan generator produces a shareable document that makes this review straightforward.
Tracking Plan Best Practices
The Object Action naming convention
Every event in this guide follows the Object Action pattern: a noun describing what was acted on, followed by a past-tense verb describing what happened.
Object | Action | Full Event Name |
Swap | Initiated |
|
Limit Order | Placed |
|
Supply | Completed |
|
Deposit | Initiated |
|
Vault | Viewed |
|
This convention keeps events consistent across your team, makes funnels readable at a glance, and ensures your analytics dashboard stays interpretable as the team grows. Formo's custom events documentation recommends this [Noun] + [Past-Tense Verb] standard for all implementations.
Reserved properties: volume, revenue, and points
The Formo SDK has 3 reserved properties that surface directly in your dashboard:
Property | Type | Use in DeFi |
| number | USD value of the swap, deposit, supply, or borrow. Use on every transaction event. |
| number | Protocol fee earned from the transaction. Must be non-negative. |
| string | Abstract points or XP value, if your app runs a points program. |
See the full reference.
How to Install Formo
The Formo SDK auto-tracks Page Viewed, wallet activity, chain switches, and transactions automatically. The custom events in this guide are the only ones that require a manual formo.track(...) call.
1. Install the package
npm install @formo/analytics
2. Wrap your app
For Next.js (app router), edit app/layout.tsx:
import { FormoAnalyticsProvider } from "@formo/analytics";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<FormoAnalyticsProvider writeKey={process.env.NEXT_PUBLIC_FORMO_WRITE_KEY!}>
{children}
</FormoAnalyticsProvider>
</body>
</html>
);
}
For Vite or React, wrap your root component in the same provider.
Get your project’s writeKey from your Formo workspace.
3. Fire custom events
Use the useFormo() hook anywhere in your component tree:
"use client";
import { useFormo } from "@formo/analytics";
export function SwapButton() {
const formo = useFormo();
return (
<button
onClick={() =>
formo.track("Swap Initiated", {
from_token: "ETH",
to_token: "USDC",
volume: 1000,
})
}
>
Swap
</button>
);
}
For full install docs covering React Native, plain JS, and server-side tracking, see our Docs.
Notes for engineers
Fire custom events via
Formo.track("Event Name", { properties }).Use the reserved
volumeproperty to capture USD value on all transaction events.Apply a 500ms debounce to events triggered by typing (
Amount Entered,Pool Searched).Fire review or confirmation events before the wallet signature so you can measure drop-off between intent and submission.
On failure events, use a normalised
error_typeenum rather than raw error strings to keep funnels analysable.
How To Read This
Each app type section is independent. Pick the one that matches your app and implement in stages, starting with P0 events first.
Event Priority Levels
Every event in this guide carries a priority label. Here is what each level means:
Priority | What it means | When to implement |
P0 | Core conversion events. These directly measure whether users complete the primary action in your app: a swap, a supply, or a deposit. Without P0 events, you have no funnel. | Ship before launch or as your first instrumentation sprint. |
P1 | Supporting signals. These add context to your P0 funnel: approval flows, input behaviour, collateral actions, and health factor warnings. Useful once P0 is stable. | Ship in your second instrumentation sprint. |
P2 | Discovery and UX events. These cover search, filters, settings, and secondary interactions. Valuable for UX optimisation, but adds no signal to the core conversion funnel. | Ship after P0 and P1 are verified and stable. |
Start with P0. Validate the data in your Formo dashboard before moving to P1 and P2.
App Type | Events | P0 Conversion Events |
DEX | 26 | Swap Initiated, Swap Completed, Limit Order Placed, Cross-Chain Swap Completed, Liquidity Add Completed |
Lending | 14 | Supply Initiated, Supply Completed, Borrow Initiated, Borrow Completed, Liquidation Occurred |
Yield Vaults | 12 | Deposit Initiated, Deposit Completed, Withdraw Initiated, Withdraw Completed |
Part 1: DEX Tracking Plan
This section covers a DEX with swap execution, limit orders, cross-chain swaps, and liquidity provision. It maps to 26 custom events across 6 product areas.
Event priority framework
Stage | Events | What it unlocks |
P0: Core funnel | Swap Review Opened, Swap Initiated, Swap Completed, Swap Failed, Limit Order Placed, Limit Order Filled, Cross-Chain Swap Initiated, Cross-Chain Swap Completed, Liquidity Added, Liquidity Add Completed | Conversion rates, volume, drop-off analysis |
P1: Supporting signals | Token Approval Initiated, Token Approval Completed, Token Approval Failed, Token Selected, Amount Entered, Max Slippage Changed, Limit Order Cancelled | Approval funnel, input behaviour, slippage correlation |
P2: Discovery and settings | Token Pair Reversed, Max Balance Clicked, Swap Settings Opened, Pool Searched, Pool Filter Applied | UX optimisation, pool discovery patterns |
Recommendation
Prioritise the P0 conversion events first: Swap Review Opened, Swap Initiated, Swap Completed, Swap Failed, Limit Order Placed, Limit Order Filled, Cross-Chain Swap Initiated, Cross-Chain Swap Completed, Liquidity Added, Liquidity Add Completed. Add the remaining events in stages as you continue to use Formo.
Reserved volume events for DEX
Pass volume on: Swap Initiated, Swap Completed, Limit Order Placed, Limit Order Filled, Cross-Chain Swap Initiated, Cross-Chain Swap Completed, Liquidity Added, and Liquidity Add Completed.
DEX: Trade — Swap Execution
The core swap funnel: review, approval, submission, confirmation. Instrument all 6 before adding anything else.
Swap Review Opened
Trigger: User clicks the Swap button, and the confirmation modal appears. Priority: P0
Fire this before the wallet signature request so you can measure drop-off between intent and submission.
Property | Type | Description | Example |
| string | Input token symbol |
|
| address | Input token contract address |
|
| string | Output token symbol |
|
| address | Output token contract address |
|
| string | Input amount |
|
| number | Input value in USD |
|
| string | Expected output amount |
|
| number | Expected output value in USD |
|
| string | Display rate string |
|
| string | Minimum output after slippage |
|
| string | Price impact percentage |
|
| number | Slippage tolerance (%) |
|
| string[] | DEXes in the route |
|
| chain | Active chain |
|
Token Approval Initiated
Trigger: Approval transaction submitted to the wallet. Priority: P1
Property | Type | Description | Example |
| string | Token being approved |
|
| address | Token contract address |
|
| address | The contract is being approved to spend |
|
| string |
|
|
| string | Amount approved or |
|
| chain | Active chain |
|
Token Approval Completed
Trigger: Approval transaction confirmed onchain Priority: P1
Property | Type | Description | Example |
| string | The token that was approved |
|
| address | Token contract address |
|
| address | Approved spender |
|
| string |
|
|
| string | Confirmation transaction hash |
|
| chain | Active chain |
|
Token Approval Failed
Trigger: Approval was rejected or reverted. Priority: P1
Property | Type | Description | Example |
| string | Token that failed approval |
|
| address | Token contract address |
|
| string |
|
|
| string | Normalised enum: |
|
| string | Raw error message for debugging |
|
| chain | Active chain |
|
Swap Initiated
Trigger: Swap transaction submitted to the chain. Priority: P0
Primary swap event. Include all accumulated settings and route info. Use the reserved volume property for USD trade size.
Property | Type | Description | Example |
| string | Input token symbol |
|
| address | Input token contract address |
|
| string | Output token symbol |
|
| address | Output token contract address |
|
| string | Token pair string |
|
| string | Input amount |
|
| number | Input value in USD |
|
| string | Estimated output amount |
|
| number | Estimated output value in USD |
|
| string | Minimum output after slippage |
|
| string | Rate at submission |
|
| string | Price impact percentage |
|
| number | Slippage tolerance (%) |
|
| number | Deadline in minutes |
|
| string | Token used for gas |
|
| string[] | DEXes in the route |
|
| number | Number of hops in the route |
|
| boolean | Whether the route is split across pools |
|
| chain | Active chain |
|
| number | USD trade size (Formo reserved) |
|
Swap Completed
Trigger: Swap transaction confirmed onchain Priority: P0
Property | Type | Description | Example |
| string | Input token symbol |
|
| string | Output token symbol |
|
| string | Token pair string |
|
| string | Input amount |
|
| number | Input value in USD |
|
| string | Actual output amount received |
|
| number | Actual output value in USD |
|
| number | Realised slippage (%) |
|
| number | Slippage tolerance set by user (%) |
|
| string | Confirmation transaction hash |
|
| string | Gas units consumed |
|
| string | Gas price at confirmation |
|
| chain | Active chain |
|
| number | USD trade size (Formo reserved) |
|
Swap Failed
Trigger: Swap rejected or reverted. Priority: P0
Use a normalised error_type enum over raw error strings to keep funnels analysable.
Property | Type | Description | Example |
| string | Input token symbol |
|
| string | Output token symbol |
|
| string | Token pair string |
|
| string | Attempted input amount |
|
| number | Attempted input value in USD |
|
| string | Normalised enum: |
|
| string | Raw error message for debugging |
|
| number | Slippage tolerance set by user (%) |
|
| chain | Active chain |
|
DEX: Trade — Swap Form Interactions
Top-of-funnel actions on the swap form. Useful for understanding which inputs lead to a completed swap.
Token Selected
Trigger: User selects a token from the token picker. Priority: P1
Property | Type | Description | Example |
| string | Selected token symbol |
|
| address | Token contract address |
|
| string |
|
|
| string | The other token in the pair |
|
| chain | Active chain |
|
Amount Entered
Trigger: User enters or changes the swap amount (debounce 500ms). Priority: P1
Property | Type | Description | Example |
| string | Token symbol for the entered amount |
|
| string | Amount entered |
|
| number | USD value at time of entry |
|
| string |
|
|
| chain | Active chain |
|
Token Pair Reversed
Trigger: User clicks the reverse arrow between input and output. Priority: P2
Property | Type | Description | Example |
| string | The token that was the input before reversal |
|
| string | The token that was the output before reversal |
|
| chain | Active chain |
|
Max Balance Clicked
Trigger: User clicks the Max button on the input token. Priority: P2
Property | Type | Description | Example |
| string | Token whose max balance was used |
|
| string | Full balance amount |
|
| number | USD value of the full balance |
|
| chain | Active chain |
|
DEX: Trade — Swap Settings
User-configurable settings that affect every downstream swap event. Track these to understand how slippage preferences correlate with swap success and failure rates.
Swap Settings Opened
Trigger: User opens the swap settings panel (gear icon). Priority: P2
Property | Type | Description |
| number | Slippage tolerance at time of opening (%) |
| number | Current deadline setting in minutes |
| string | Current gas token selection |
Max Slippage Changed
Trigger: User adjusts slippage tolerance.e Priority: P1
Pair with Swap Failed where error_type is slippage_exceeded to identify users who may benefit from a slippage recommendation.
Property | Type | Description | Example |
| number | Slippage before the change (%) |
|
| number | Slippage after the change (%) |
|
| string |
|
|
DEX: Trade — Limit Orders
Limit order lifecycle. Treat Limit Order Placed and Limit Order Filled as P0 conversion events alongside the core swap funnel.
Limit Order Placed
Trigger: Limit order submitted. Priority: P0
Property | Type | Description | Example |
| string |
|
|
| string | Input token symbol |
|
| string | Output token symbol |
|
| string | Input amount |
|
| number | Input value in USD |
|
| string | Target price for execution |
|
| number | Order expiry duration in seconds |
|
| chain | Active chain |
|
| number | USD order size (Formo reserved) |
|
Limit Order Filled
Trigger: Limit order filled onchain. Priority: P0
Property | Type | Description | Example |
| string | Onchain order identifier |
|
| string | Input token symbol |
|
| string | Output token symbol |
|
| string | Input amount filled |
|
| string | Output amount received |
|
| string | Actual execution price |
|
| string | Fill transaction hash |
|
| chain | Active chain |
|
| number | USD fill size (Formo reserved) |
|
Limit Order Cancelled
Trigger: User cancels a limit order. Priority: P1
Property | Type | Description | Example |
| string | Onchain order identifier |
|
| string | Input token symbol |
|
| string | Output token symbol |
|
| string | Target price at cancellation |
|
| number | Time the order was open before cancellation |
|
| chain | Active chain |
|
DEX: Trade — Cross-Chain
Cross-chain swap lifecycle. Pair Cross-Chain Swap Initiated and Cross-Chain Swap Completed to measure bridge success rate and settlement latency.
Cross-Chain Swap Initiated
Trigger: Cross-chain transaction submitted. Priority: P0
Property | Type | Description | Example |
| string | Input token symbol |
|
| string | Output token symbol |
|
| chain | Origin chain |
|
| chain | Destination chain |
|
| string | Input amount |
|
| number | Input value in USD |
|
| string | Estimated output on the destination chain |
|
| string | Bridge or messaging protocol used |
|
| number | Estimated settlement time |
|
| number | USD swap size (Formo reserved) |
|
Cross-Chain Swap Completed
Trigger: Funds delivered on the destination chain. Priority: P0
Use the delta between estimated_duration_seconds and actual_duration_seconds to track bridge latency over time.
Property | Type | Description | Example |
| chain | Origin chain |
|
| chain | Destination chain |
|
| string | Input token symbol |
|
| string | Output token symbol |
|
| string | Actual output received on the destination chain |
|
| number | Actual settlement time |
|
| string | Bridge or messaging protocol used |
|
| string | Source chain transaction hash |
|
| string | Destination chain transaction hash |
|
| number | USD swap size (Formo reserved) |
|
DEX: Earn — Liquidity and Pool Discovery
Liquidity provision flow and pool discovery. Track Liquidity Added and Liquidity Add Completed to measure LP funnel drop-off between intent and onchain settlement.
Liquidity Added
Trigger: Add liquidity transaction submitted. Priority: P0
Property | Type | Description | Example |
| string | Pool identifier |
|
| string | First token symbol |
|
| string | Second token symbol |
|
| string | Amount of token A deposited |
|
| string | Amount of token B deposited |
|
| number | Total deposit value in USD |
|
| string | Lower bound of the LP price range |
|
| string | Upper bound of the LP price range |
|
| string | Pool fee tier |
|
| chain | Active chain |
|
| number | USD deposit size (Formo reserved) |
|
Liquidity Add Completed
Trigger: Add-liquidity transaction confirmed onchain. Priority: P0
Property | Type | Description | Example |
| string | Pool identifier |
|
| string | Onchain position ID |
|
| string | Confirmation transaction hash |
|
| number | Total deposit value confirmed |
|
| chain | Active chain |
|
| number | USD deposit size (Formo reserved) |
|
Pool Searched
Trigger: User types in the pool search box (debounce 500ms) Priority: P2
Property | Type | Description | Example |
| string | Search term entered |
|
| number | Number of results returned |
|
Pool Filter Applied
Trigger: User applies a filter on the pools list. Priority: P2
Property | Type | Description | Example |
| string | Filter category: |
|
| string | Value selected for the filter |
|
DEX Event Summary
Event Name | Trigger | Priority |
Swap Review Opened | Swap confirmation modal appears | P0 |
Token Approval Initiated | Approval transaction submitted | P1 |
Token Approval Completed | Approval transaction confirmed onchain | P1 |
Token Approval Failed | Approval rejected or reverted | P1 |
Swap Initiated | Swap transaction submitted to the chain | P0 |
Swap Completed | Swap transaction confirmed onchain | P0 |
Swap Failed | Swap rejected or reverted | P0 |
Token Selected | User selects a token from the token picker | P1 |
Amount Entered | User enters or changes the swap amount | P1 |
Token Pair Reversed | User clicks the reverse arrow | P2 |
Max Balance Clicked | User clicks the Max button on the input token | P2 |
Swap Settings Opened | User opens the swap settings panel | P2 |
Max Slippage Changed | User adjusts slippage tolerance | P1 |
Limit Order Placed | Limit order submitted | P0 |
Limit Order Filled | Limit order filled onchain | P0 |
Limit Order Cancelled | User cancels a limit order | P1 |
Cross-Chain Swap Initiated | Cross-chain transaction submitted | P0 |
Cross-Chain Swap Completed | Funds delivered on the destination chain | P0 |
Liquidity Added | Add liquidity transaction submitted | P0 |
Liquidity Add Completed | Add-liquidity transaction confirmed onchain | P0 |
Pool Searched | User types in the pool search box | P2 |
Pool Filter Applied | User applies a filter on the pools list | P2 |
Part 2: Lending App Tracking Plan
This section covers a lending and borrowing app. It captures the full lifecycle from market discovery through supply, borrow, repay, withdraw, and liquidation, the core conversion and risk events for any money market.
Recommendation
Prioritise the P0 conversion events first: Supply Initiated, Supply Completed, Withdraw Completed, Borrow Initiated, Borrow Completed, Repay Completed, Liquidation Occurred. Add the remaining events in stages.
Notes for engineers
Use the reserved
volumeproperty for USD value on supply, borrow, repay, withdraw, and liquidation events.Fire
Health Factor Warning is shownwhenever the UI surfaces a liquidation risk, so you can correlate warnings with actual liquidations.Capture
Liquidation Occurredfrom your indexer or contract event listener, not from the frontend.Include
health_factor_afteronBorrow Initiatedso you can analyse how aggressively users leverage up.
Lending event summary
Event Name | Trigger | Priority |
Market Viewed | User opens a market detail page | P1 |
Supply Initiated | Supply transaction submitted | P0 |
Supply Completed | Supply transaction confirmed | P0 |
Withdraw Completed | Withdraw transaction confirmed | P0 |
Collateral Enabled | User enables an asset as collateral | P1 |
Borrow Initiated | Borrow transaction submitted | P0 |
Borrow Completed | Borrow transaction confirmed | P0 |
Repay Completed | Repay transaction confirmed | P0 |
Health Factor Warning Shown | UI shows a low health factor warning | P1 |
Liquidation Occurred | User position was liquidated | P0 |
Lending: Discover Markets
Top-of-funnel events. Useful for understanding which markets attract the most attention before a deposit or borrow.
Market Viewed
Trigger: User opens a market detail page. Priority: P1
Fired when a specific market (e.g., the ETH supply market) is viewed.
Property | Type | Description | Example |
| string | Market identifier |
|
| string | Asset symbol |
|
| number | Current supply APY (%) |
|
| number | Current borrow APY (%) |
|
| chain | Active chain |
|
Lending: Supply and Withdraw
Deposit-side lifecycle events.
Supply Initiated
Trigger: Supply transaction submitted. Priority: P0
Property | Type | Description | Example |
| string | Asset symbol |
|
| address | Asset contract address |
|
| string | Amount supplied |
|
| number | USD value at time of supply |
|
| number | APY at time of supply (%) |
|
| string | Market identifier |
|
| chain | Active chain |
|
| number | USD supply size (Formo reserved) |
|
Supply Completed
Trigger: Supply transaction confirmed. Priority: P0
Property | Type | Description | Example |
| string | Asset symbol |
|
| string | Amount confirmed |
|
| number | USD value confirmed |
|
| string | Confirmation transaction hash |
|
| chain | Active chain |
|
| number | USD supply size (Formo reserved) |
|
Withdraw Completed
Trigger: Withdraw transaction confirmed. Priority: P0
Property | Type | Description | Example |
| string | Asset symbol |
|
| string | Amount withdrawn |
|
| number | USD value withdrawn |
|
| string | "partial" or "full" |
|
| string | Confirmation transaction hash |
|
| chain | Active chain |
|
| number | USD withdrawal size (Formo reserved) |
|
Collateral Enabled
Trigger: User enables an asset as collateral. Priority: P1
Fired when a supplied asset is marked as collateral.
Property | Type | Description | Example |
| string | Asset enabled as collateral |
|
| number | Loan-to-value ratio for this asset |
|
| chain | Active chain |
|
Lending: Borrow and Repay
Debt-side lifecycle is the highest-revenue event in a money market.
Borrow Initiated
Trigger: Borrow transaction submitted. Priority: P0
Include health_factor_after so you can analyse how aggressively users leverage and correlate with downstream liquidation risk.
Property | Type | Description | Example |
| string | Asset being borrowed |
|
| string | Amount borrowed |
|
| number | USD value at time of borrow |
|
| number | APY at time of borrow (%) |
|
| string |
|
|
| number | Health factor after the borrow |
|
| chain | Active chain |
|
| number | USD borrow size (Formo reserved) |
|
Borrow Completed
Trigger: Borrow transaction confirmed. Priority: P0
Property | Type | Description | Example |
| string | Asset borrowed |
|
| string | Amount confirmed |
|
| number | USD value confirmed |
|
| string | Confirmation transaction hash |
|
| chain | Active chain |
|
| number | USD borrow size (Formo reserved) |
|
Repay Completed
Trigger: Repay transaction confirmed. Priority: P0
Property | Type | Description | Example |
| string | Asset repaid |
|
| string | Amount repaid |
|
| number | USD value repaid |
|
| string | "partial" or "full" |
|
| string | Confirmation transaction hash |
|
| chain | Active chain |
|
| number | USD repayment size (Formo reserved) |
|
Lending: Risk and Liquidation
Position health and liquidation events. These are critical for understanding app risk exposure and correlating UI warnings with onchain outcomes.
Health Factor Warning Shown
Trigger: UI shows a low health factor warning. Priority: P1
Fired when the UI surfaces a liquidation risk warning. Pair with Liquidation Occurred to measure how many warned users take action before liquidation.
Property | Type | Description | Example |
| number | Current health factor at time of warning |
|
| string | "medium" or "high" |
|
| chain | Active chain |
|
Liquidation Occurred
Trigger: User position was liquidated. Priority: P0
Capture this from your indexer or contract event listener, not from the frontend. This event is the most important risk signal in a lending app.
Property | Type | Description | Example |
| string | Asset seized as collateral |
|
| string | Asset repaid by the liquidator |
|
| number | USD value of collateral seized |
|
| number | USD value of debt repaid |
|
| number | USD value of the liquidation bonus |
|
| string | Liquidation transaction hash |
|
| chain | Active chain |
|
Part 3: Yield Vault Tracking Plan
This section covers a yield vault app. It captures vault discovery, deposit, withdraw, yield claims, and strategy migrations, the core lifecycle for analysing TVL growth, retention, and realised yield.
Recommendation
Prioritise the P0 conversion events first: Deposit Initiated, Deposit Completed, Withdraw Initiated, Withdraw Completed. Add the remaining events in stages.
Notes for engineers
Use the reserved
volumeproperty for USD value on deposit and withdraw events.On
Withdraw Completed, computeyield_earned_usdandholding_duration_secondsserver-side from the original deposit, so you can analyse realised APY and retention.If your vault auto-compounds, you can skip
Yield Claimedand rely onWithdraw Completed.yield_earned_usd.
Yield vault event summary
Event Name | Trigger | Priority |
Vault Viewed | User opens a vault detail page | P1 |
Vault Filter Applied | User filters the vault list | P2 |
Deposit Initiated | Deposit transaction submitted | P0 |
Deposit Completed | Deposit transaction confirmed | P0 |
Withdraw Initiated | Withdraw transaction submitted | P0 |
Withdraw Completed | Withdraw transaction confirmed | P0 |
Yield Claimed | User claims yield or rewards | P1 |
Strategy Migrated | User migrates between vault strategies | P2 |
Yield Vaults: Vault Discovery
How users find and evaluate vaults before depositing.
Vault Viewed
Trigger: User opens a vault detail page. Priority: P1
Property | Type | Description | Example |
| string | Vault identifier |
|
| string | Display name of the vault |
|
| string | Underlying asset symbol |
|
| number | Current APY (%) |
|
| number | Current TVL in USD |
|
| chain | Active chain |
|
Vault Filter Applied
Trigger: User filters the vault list.t Priority: P2
Fired when the user filters by asset, chain, APY, or other criteria.
Property | Type | Description | Example |
| string | Filter category: "apy", "chain", "asset" |
|
| string | Value selected for the filter |
|
Yield Vaults: Deposit
Inflow lifecycle: the primary conversion funnel for a yield vault.
Deposit Initiated
Trigger: Deposit transaction submitted. Priority: P0
Property | Type | Description | Example |
| string | Vault identifier |
|
| string | Asset being deposited |
|
| string | Amount deposited |
|
| number | USD value at time of deposit |
|
| number | APY displayed to the user at deposit time (%) |
|
| chain | Active chain |
|
| number | USD deposit size (Formo reserved) |
|
Deposit Completed
Trigger: Deposit transaction confirmed Priority: P0
Property | Type | Description | Example |
| string | Vault identifier |
|
| string | Asset deposited |
|
| string | Amount confirmed |
|
| number | USD value confirmed |
|
| string | Vault shares are minted to the user |
|
| string | Confirmation transaction hash |
|
| chain | Active chain |
|
| number | USD deposit size (Formo reserved) |
|
Yield Vaults: Withdraw and Yield
Outflow and reward lifecycle. On Withdraw Completed, compute yield_earned_usd and holding_duration_seconds server-side from the original deposit record, so you can analyse realised APY and retention.
Withdraw Initiated
Trigger: Withdraw transaction submitted. Priority: P0
Property | Type | Description | Example |
| string | Vault identifier |
|
| string | Asset being withdrawn |
|
| string | Amount requested |
|
| number | USD value at time of request |
|
| string | Vault shares redeemed |
|
| chain | Active chain |
|
| number | USD withdrawal size (Formo reserved) |
|
Withdraw Completed
Trigger: Withdraw transaction confirmed.d Priority: P0
Property | Type | Description | Example |
| string | Vault identifier |
|
| string | Asset withdrawn |
|
| string | Amount confirmed |
|
| number | USD value confirmed |
|
| string | Confirmation transaction hash |
|
| number | Yield earned over the holding period in USD |
|
| number | Time between deposit and withdrawal in seconds |
|
| chain | Active chain |
|
| number | USD withdrawal size (Formo reserved) |
|
Yield Claimed
Trigger: User claims yield or rewards (if separate from withdraw) Priority: P1
If your vault auto-compounds, skip this event and rely on Withdraw Completed.yield_earned_usd instead.
Property | Type | Description | Example |
| string | Vault identifier |
|
| string | Reward token symbol |
|
| string | Amount claimed |
|
| number | USD value of the claim |
|
| string | Confirmation transaction hash |
|
| chain | Active chain |
|
Yield Vaults: Strategy
Strategy migration events. Useful for understanding how users move between vault versions and tracking migration adoption rates.
Strategy Migrated
Trigger: User migrates funds between vault strategies. Priority: P2
Property | Type | Description | Example |
| string | Source vault identifier |
|
| string | Destination vault identifier |
|
| number | USD value migrated |
|
| chain | Active chain |
|
Yield Vault Event Summary
Event Name | Trigger | Priority |
Vault Viewed | User opens a vault detail page | P1 |
Vault Filter Applied | User filters the vault list | P2 |
Deposit Initiated | Deposit transaction submitted | P0 |
Deposit Completed | Deposit transaction confirmed | P0 |
Withdraw Initiated | Withdraw transaction submitted | P0 |
Withdraw Completed | Withdraw transaction confirmed | P0 |
Yield Claimed | User claims yield or rewards | P1 |
Strategy Migrated | User migrates between vault strategies | P2 |
Next Steps
A tracking plan is the foundation every DeFi app needs before any analytics work makes sense. Without it, event names drift, properties go missing, and the data your team relies on for product and growth decisions becomes unreliable.
This guide gives you production-ready plans for the 3 most common DeFi app types:
App Type | Events | Start Here |
DEX | 26 events | Swap Initiated, Swap Completed, Swap Failed |
Lending | 14 events | Supply Initiated, Borrow Initiated, Liquidation Occurred |
Yield Vaults | 12 events | Deposit Initiated, Deposit Completed, Withdraw Completed |
Start small with only P0 events. Get them firing correctly on staging, verify the property shapes in your Formo dashboard, then deploy to production. P1 and P2 events add signal but do not change the core funnel picture. A lean, analytics implementation is already more valuable than having no analytics at all.
Frequently Asked Questions
What is a DeFi tracking plan?
A DeFi tracking plan is a detailed specification that defines every analytics event your app emits, when it fires, and which properties are in it. It covers both offchain events (page views, form inputs, review modals) and onchain events (transactions, volume, contract events). It is the shared schema that keeps your product, engineering, and analytics teams aligned.
Which events should a DEX instrument first?
Start with the P0 conversion funnel: Swap Review Opened, Swap Initiated, Swap Completed, and Swap Failed. These 4 events give you conversion rate, drop-off point, volume, and error breakdown. Add limit order and cross-chain events next, then liquidity and discovery events once the core swap funnel is stable and verified.
Which events should a lending app instrument first?
Prioritise the deposit and borrow lifecycle: Supply Initiated, Supply Completed, Borrow Initiated, Borrow Completed, and Repay Completed. Add Liquidation Occurred early as well, captured from your indexer. These events cover the full money market funnel and give you TVL inflows, borrowing, and risk exposure in a single event stream.
Which events should a yield app instrument first?
Start with Deposit Initiated, Deposit Completed, Withdraw Initiated, and Withdraw Completed. These 4 events measure your full TVL inflow and outflow cycle. Add yield_earned_usd and holding_duration_seconds on Withdraw Completed to track realised APY per user. Yield Claimed and Strategy Migrated are useful additions once the core deposit funnel is stable.
Do I need to track wallet activity manually?
No. The Formo SDK auto-tracks wallet connects, disconnections, chain switches, and transactions. Calling formo.track() for any of these creates duplicate events. Your formo.track() calls cover only the custom product events specific to your app.
What is the Object Action naming convention?
Object Action is a naming standard where every event name combines a noun (the thing acted on) and a past-tense verb (what happened). Examples: Swap Initiated, Deposit Completed, Liquidation Occurred. It keeps event names consistent across teams, makes funnels readable at a glance, and prevents the naming drift that accumulates when multiple engineers instrument events independently.
How do I track volume with the volume property in Formo?
Volume is a reserved property in the Formo SDK that captures the USD value of a transaction event. It surfaces directly in your dashboard as protocol volume without additional configuration. Pass it on every volume-generating event: swaps, supplies, borrows, repayments, deposits, and withdrawals.

