content

DeFi Tracking Plan: How to Set Up Analytics For Your Crypto App (DEX, Lending, and Yield)

DeFi Tracking Plan: How to Set Up Analytics For Your Crypto App (DEX, Lending, and Yield)

DeFi Tracking Plan: How to Set Up Analytics For Your Crypto App (DEX, Lending, and Yield)

Yos Riady

Yos Riady

Published

· Updated on

Updated

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

Swap Initiated

Limit Order

Placed

Limit Order Placed

Supply

Completed

Supply Completed

Deposit

Initiated

Deposit Initiated

Vault

Viewed

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

volume

number

USD value of the swap, deposit, supply, or borrow. Use on every transaction event.

revenue

number

Protocol fee earned from the transaction. Must be non-negative.

points

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

Swap Initiated

Limit Order

Placed

Limit Order Placed

Supply

Completed

Supply Completed

Deposit

Initiated

Deposit Initiated

Vault

Viewed

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

volume

number

USD value of the swap, deposit, supply, or borrow. Use on every transaction event.

revenue

number

Protocol fee earned from the transaction. Must be non-negative.

points

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 volume property 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_type enum 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

from_token

string

Input token symbol

ETH

from_token_address

address

Input token contract address

0x...

to_token

string

Output token symbol

USDT

to_token_address

address

Output token contract address

0x...

amount_in

string

Input amount

1

amount_in_usd

number

Input value in USD

2013.00

amount_out

string

Expected output amount

2013.285177

amount_out_usd

number

Expected output value in USD

2013.00

rate

string

Display rate string

1 ETH = 2013.2852 USDT

minimum_received

string

Minimum output after slippage

2003.22

price_impact

string

Price impact percentage

0.01%

max_slippage

number

Slippage tolerance (%)

0.5

trade_route_dexes

string[]

DEXes in the route

["Uniswap V3"]

chain

chain

Active chain

ethereum

Token Approval Initiated

Trigger: Approval transaction submitted to the wallet. Priority: P1

Property

Type

Description

Example

token_symbol

string

Token being approved

USDT

token_address

address

Token contract address

0x...

spender_address

address

The contract is being approved to spend

0x...

approval_type

string

"permit" or "approve"

permit

approval_amount

string

Amount approved or "unlimited"

unlimited

chain

chain

Active chain

ethereum

Token Approval Completed

Trigger: Approval transaction confirmed onchain Priority: P1

Property

Type

Description

Example

token_symbol

string

The token that was approved

USDT

token_address

address

Token contract address

0x...

spender_address

address

Approved spender

0x...

approval_type

string

"permit" or "approve"

permit

tx_hash

string

Confirmation transaction hash

0x...

chain

chain

Active chain

ethereum

Token Approval Failed

Trigger: Approval was rejected or reverted. Priority: P1

Property

Type

Description

Example

token_symbol

string

Token that failed approval

USDT

token_address

address

Token contract address

0x...

approval_type

string

"permit" or "approve"

permit

error_type

string

Normalised enum: user_rejected, reverted, timeout

user_rejected

error_message

string

Raw error message for debugging

User rejected the request

chain

chain

Active chain

ethereum

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

from_token

string

Input token symbol

ETH

from_token_address

address

Input token contract address

0x...

to_token

string

Output token symbol

USDT

to_token_address

address

Output token contract address

0x...

pair

string

Token pair string

ETH/USDT

amount_in

string

Input amount

1

amount_in_usd

number

Input value in USD

2013.00

amount_out_estimated

string

Estimated output amount

2013.285177

amount_out_usd

number

Estimated output value in USD

2013.00

minimum_received

string

Minimum output after slippage

2003.22

rate

string

Rate at submission

2013.2852

price_impact

string

Price impact percentage

0.01%

max_slippage

number

Slippage tolerance (%)

0.5

transaction_time_limit

number

Deadline in minutes

20

gas_token

string

Token used for gas

ETH

trade_route_dexes

string[]

DEXes in the route

["Uniswap V3"]

trade_route_steps

number

Number of hops in the route

1

route_split

boolean

Whether the route is split across pools

false

chain

chain

Active chain

ethereum

volume

number

USD trade size (Formo reserved)

2013.00

Swap Completed

Trigger: Swap transaction confirmed onchain Priority: P0

Property

Type

Description

Example

from_token

string

Input token symbol

ETH

to_token

string

Output token symbol

USDT

pair

string

Token pair string

ETH/USDT

amount_in

string

Input amount

1

amount_in_usd

number

Input value in USD

2013.00

amount_out_actual

string

Actual output amount received

2013.10

amount_out_usd

number

Actual output value in USD

2013.10

slippage_actual

number

Realised slippage (%)

0.009

max_slippage

number

Slippage tolerance set by user (%)

0.5

tx_hash

string

Confirmation transaction hash

0x...

gas_used

string

Gas units consumed

150000

gas_price_gwei

string

Gas price at confirmation

12.5

chain

chain

Active chain

ethereum

volume

number

USD trade size (Formo reserved)

2013.00

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

from_token

string

Input token symbol

ETH

to_token

string

Output token symbol

USDT

pair

string

Token pair string

ETH/USDT

amount_in

string

Attempted input amount

1

amount_in_usd

number

Attempted input value in USD

2013.00

error_type

string

Normalised enum: user_rejected, reverted, slippage_exceeded, timeout

user_rejected

error_message

string

Raw error message for debugging

Transaction reverted

max_slippage

number

Slippage tolerance set by user (%)

0.5

chain

chain

Active chain

ethereum

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

token_symbol

string

Selected token symbol

ETH

token_address

address

Token contract address

0x...

token_position

string

"input" or "output"

input

paired_token

string

The other token in the pair

USDT

chain

chain

Active chain

ethereum

Amount Entered

Trigger: User enters or changes the swap amount (debounce 500ms). Priority: P1

Property

Type

Description

Example

token_symbol

string

Token symbol for the entered amount

ETH

amount

string

Amount entered

1.5

amount_usd

number

USD value at time of entry

3020.00

token_position

string

"input" or "output"

input

chain

chain

Active chain

ethereum

Token Pair Reversed

Trigger: User clicks the reverse arrow between input and output. Priority: P2

Property

Type

Description

Example

from_token

string

The token that was the input before reversal

ETH

to_token

string

The token that was the output before reversal

USDT

chain

chain

Active chain

ethereum

Max Balance Clicked

Trigger: User clicks the Max button on the input token. Priority: P2

Property

Type

Description

Example

token_symbol

string

Token whose max balance was used

ETH

max_balance

string

Full balance amount

2.45

max_balance_usd

number

USD value of the full balance

4930.00

chain

chain

Active chain

ethereum

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

current_max_slippage

number

Slippage tolerance at time of opening (%)

current_transaction_time_limit

number

Current deadline setting in minutes

current_gas_token

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

previous_value

number

Slippage before the change (%)

0.5

new_value

number

Slippage after the change (%)

1.0

input_method

string

"preset" or "custom"

preset

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

side

string

"buy" or "sell"

buy

from_token

string

Input token symbol

USDC

to_token

string

Output token symbol

ETH

amount_in

string

Input amount

1000

amount_in_usd

number

Input value in USD

1000.00

limit_price

string

Target price for execution

1950

expiry_seconds

number

Order expiry duration in seconds

604800

chain

chain

Active chain

ethereum

volume

number

USD order size (Formo reserved)

1000.00

Limit Order Filled

Trigger: Limit order filled onchain. Priority: P0

Property

Type

Description

Example

order_id

string

Onchain order identifier

0x...

from_token

string

Input token symbol

USDC

to_token

string

Output token symbol

ETH

amount_in

string

Input amount filled

1000

amount_out

string

Output amount received

0.5128

fill_price

string

Actual execution price

1950.20

tx_hash

string

Fill transaction hash

0x...

chain

chain

Active chain

ethereum

volume

number

USD fill size (Formo reserved)

1000.00

Limit Order Cancelled

Trigger: User cancels a limit order. Priority: P1

Property

Type

Description

Example

order_id

string

Onchain order identifier

0x...

from_token

string

Input token symbol

USDC

to_token

string

Output token symbol

ETH

limit_price

string

Target price at cancellation

1950

age_seconds

number

Time the order was open before cancellation

3600

chain

chain

Active chain

ethereum

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

from_token

string

Input token symbol

USDC

to_token

string

Output token symbol

USDC

source_chain

chain

Origin chain

ethereum

destination_chain

chain

Destination chain

base

amount_in

string

Input amount

500

amount_in_usd

number

Input value in USD

500.00

amount_out_estimated

string

Estimated output on the destination chain

499.50

bridge_provider

string

Bridge or messaging protocol used

Across

estimated_duration_seconds

number

Estimated settlement time

180

volume

number

USD swap size (Formo reserved)

500.00

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

source_chain

chain

Origin chain

ethereum

destination_chain

chain

Destination chain

base

from_token

string

Input token symbol

USDC

to_token

string

Output token symbol

USDC

amount_out_actual

string

Actual output received on the destination chain

499.45

actual_duration_seconds

number

Actual settlement time

215

bridge_provider

string

Bridge or messaging protocol used

Across

tx_hash_source

string

Source chain transaction hash

0x...

tx_hash_destination

string

Destination chain transaction hash

0x...

volume

number

USD swap size (Formo reserved)

500.00

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

pool_id

string

Pool identifier

ETH-USDC-0.05

token_a

string

First token symbol

ETH

token_b

string

Second token symbol

USDC

amount_a

string

Amount of token A deposited

1

amount_b

string

Amount of token B deposited

2013

total_usd

number

Total deposit value in USD

4026.00

price_range_min

string

Lower bound of the LP price range

1800

price_range_max

string

Upper bound of the LP price range

2200

fee_tier

string

Pool fee tier

0.05%

chain

chain

Active chain

ethereum

volume

number

USD deposit size (Formo reserved)

4026.00

Liquidity Add Completed

Trigger: Add-liquidity transaction confirmed onchain. Priority: P0

Property

Type

Description

Example

pool_id

string

Pool identifier

ETH-USDC-0.05

position_id

string

Onchain position ID

12345

tx_hash

string

Confirmation transaction hash

0x...

total_usd

number

Total deposit value confirmed

4026.00

chain

chain

Active chain

ethereum

volume

number

USD deposit size (Formo reserved)

4026.00

Pool Searched

Trigger: User types in the pool search box (debounce 500ms) Priority: P2

Property

Type

Description

Example

query

string

Search term entered

ETH

results_count

number

Number of results returned

12

Pool Filter Applied

Trigger: User applies a filter on the pools list. Priority: P2

Property

Type

Description

Example

filter_type

string

Filter category: "chain", "fee_tier", "tvl_range"

chain

filter_value

string

Value selected for the filter

base

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 volume property for USD value on supply, borrow, repay, withdraw, and liquidation events.

  • Fire Health Factor Warning is shown whenever the UI surfaces a liquidation risk, so you can correlate warnings with actual liquidations.

  • Capture Liquidation Occurred from your indexer or contract event listener, not from the frontend.

  • Include health_factor_after on Borrow Initiated so 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

market_id

string

Market identifier

lending-ethereum-ETH

asset

string

Asset symbol

ETH

supply_apy

number

Current supply APY (%)

2.4

borrow_apy

number

Current borrow APY (%)

3.1

chain

chain

Active chain

ethereum

Lending: Supply and Withdraw

Deposit-side lifecycle events.

Supply Initiated

Trigger: Supply transaction submitted. Priority: P0

Property

Type

Description

Example

asset

string

Asset symbol

USDC

asset_address

address

Asset contract address

0x...

amount

string

Amount supplied

1000

amount_usd

number

USD value at time of supply

1000.00

supply_apy

number

APY at time of supply (%)

2.4

market_id

string

Market identifier

lending-ethereum-USDC

chain

chain

Active chain

ethereum

volume

number

USD supply size (Formo reserved)

1000.00

Supply Completed

Trigger: Supply transaction confirmed. Priority: P0

Property

Type

Description

Example

asset

string

Asset symbol

USDC

amount

string

Amount confirmed

1000

amount_usd

number

USD value confirmed

1000.00

tx_hash

string

Confirmation transaction hash

0x...

chain

chain

Active chain

ethereum

volume

number

USD supply size (Formo reserved)

1000.00

Withdraw Completed

Trigger: Withdraw transaction confirmed. Priority: P0

Property

Type

Description

Example

asset

string

Asset symbol

USDC

amount

string

Amount withdrawn

300

amount_usd

number

USD value withdrawn

300.00

withdraw_type

string

"partial" or "full"

partial

tx_hash

string

Confirmation transaction hash

0x...

chain

chain

Active chain

ethereum

volume

number

USD withdrawal size (Formo reserved)

300.00

Collateral Enabled

Trigger: User enables an asset as collateral. Priority: P1

Fired when a supplied asset is marked as collateral.

Property

Type

Description

Example

asset

string

Asset enabled as collateral

ETH

ltv

number

Loan-to-value ratio for this asset

0.8

chain

chain

Active chain

ethereum

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

asset

string

Asset being borrowed

USDC

amount

string

Amount borrowed

500

amount_usd

number

USD value at time of borrow

500.00

borrow_apy

number

APY at time of borrow (%)

3.1

interest_rate_mode

string

"variable" or "stable"

variable

health_factor_after

number

Health factor after the borrow

2.1

chain

chain

Active chain

ethereum

volume

number

USD borrow size (Formo reserved)

500.00

Borrow Completed

Trigger: Borrow transaction confirmed. Priority: P0

Property

Type

Description

Example

asset

string

Asset borrowed

USDC

amount

string

Amount confirmed

500

amount_usd

number

USD value confirmed

500.00

tx_hash

string

Confirmation transaction hash

0x...

chain

chain

Active chain

ethereum

volume

number

USD borrow size (Formo reserved)

500.00

Repay Completed

Trigger: Repay transaction confirmed. Priority: P0

Property

Type

Description

Example

asset

string

Asset repaid

USDC

amount

string

Amount repaid

200

amount_usd

number

USD value repaid

200.00

repay_type

string

"partial" or "full"

partial

tx_hash

string

Confirmation transaction hash

0x...

chain

chain

Active chain

ethereum

volume

number

USD repayment size (Formo reserved)

200.00

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

health_factor

number

Current health factor at time of warning

1.05

warning_level

string

"medium" or "high"

high

chain

chain

Active chain

ethereum

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

collateral_asset

string

Asset seized as collateral

ETH

debt_asset

string

Asset repaid by the liquidator

USDC

collateral_seized_usd

number

USD value of collateral seized

1500.00

debt_repaid_usd

number

USD value of debt repaid

1450.00

liquidation_bonus_usd

number

USD value of the liquidation bonus

50.00

tx_hash

string

Liquidation transaction hash

0x...

chain

chain

Active chain

ethereum

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 volume property for USD value on deposit and withdraw events.

  • On Withdraw Completed, compute yield_earned_usd and holding_duration_seconds server-side from the original deposit, so you can analyse realised APY and retention.

  • If your vault auto-compounds, you can skip Yield Claimed and rely on Withdraw 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

vault_id

string

Vault identifier

usdc-yield-v3

vault_name

string

Display name of the vault

USDC Yield Vault

asset

string

Underlying asset symbol

USDC

apy

number

Current APY (%)

8.4

tvl_usd

number

Current TVL in USD

12500000.00

chain

chain

Active chain

ethereum

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

filter_type

string

Filter category: "apy", "chain", "asset"

apy

filter_value

string

Value selected for the filter

>5%

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

vault_id

string

Vault identifier

usdc-yield-v3

asset

string

Asset being deposited

USDC

amount

string

Amount deposited

1000

amount_usd

number

USD value at time of deposit

1000.00

expected_apy

number

APY displayed to the user at deposit time (%)

8.4

chain

chain

Active chain

ethereum

volume

number

USD deposit size (Formo reserved)

1000.00

Deposit Completed

Trigger: Deposit transaction confirmed Priority: P0

Property

Type

Description

Example

vault_id

string

Vault identifier

usdc-yield-v3

asset

string

Asset deposited

USDC

amount

string

Amount confirmed

1000

amount_usd

number

USD value confirmed

1000.00

shares_minted

string

Vault shares are minted to the user

987.5

tx_hash

string

Confirmation transaction hash

0x...

chain

chain

Active chain

ethereum

volume

number

USD deposit size (Formo reserved)

1000.00

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

vault_id

string

Vault identifier

usdc-yield-v3

asset

string

Asset being withdrawn

USDC

amount

string

Amount requested

500

amount_usd

number

USD value at time of request

500.00

shares_burned

string

Vault shares redeemed

494.8

chain

chain

Active chain

ethereum

volume

number

USD withdrawal size (Formo reserved)

500.00

Withdraw Completed

Trigger: Withdraw transaction confirmed.d Priority: P0

Property

Type

Description

Example

vault_id

string

Vault identifier

usdc-yield-v3

asset

string

Asset withdrawn

USDC

amount

string

Amount confirmed

500

amount_usd

number

USD value confirmed

500.00

tx_hash

string

Confirmation transaction hash

0x...

yield_earned_usd

number

Yield earned over the holding period in USD

23.45

holding_duration_seconds

number

Time between deposit and withdrawal in seconds

2592000

chain

chain

Active chain

ethereum

volume

number

USD withdrawal size (Formo reserved)

500.00

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

vault_id

string

Vault identifier

usdc-yield-v3

reward_token

string

Reward token symbol

USDC

amount

string

Amount claimed

23.45

amount_usd

number

USD value of the claim

23.45

tx_hash

string

Confirmation transaction hash

0x...

chain

chain

Active chain

ethereum

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

from_vault_id

string

Source vault identifier

usdc-yield-v2

to_vault_id

string

Destination vault identifier

usdc-yield-v3

amount_usd

number

USD value migrated

1000.00

chain

chain

Active chain

ethereum

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.

About the Author

About the Author
About the Author
Yos Riady

Founder

Founder

Yos Riady is the founder and CTO of Formo, helping DeFi teams make analytics and attribution simple. Prior to Formo, he was a staff software engineer and tech lead at Chainlink Labs. He helped scale Chainlink into the industry-standard oracle for leading DeFi protocols such as Aave, Morpho, and Spark. He has been in crypto since 2018, working on protocol design, smart contract development, data engineering, and security.

Yos Riady is the founder and CTO of Formo, helping DeFi teams make analytics and attribution simple. Prior to Formo, he was a staff software engineer and tech lead at Chainlink Labs. He helped scale Chainlink into the industry-standard oracle for leading DeFi protocols such as Aave, Morpho, and Spark. He has been in crypto since 2018, working on protocol design, smart contract development, data engineering, and security.

Table of contents

Share this post

Measure what matters onchain

Formo makes analytics and attribution simple for DeFi apps.

Measure what matters onchain

Formo makes analytics and attribution simple for DeFi apps.

Measure what matters onchain

Formo makes analytics and attribution simple for DeFi apps.