MetaMetrics uses 9 consolidated events with discriminating properties (vs 38+ Sentry traces). Optimizes Segment costs by grouping similar actions into generic events with type properties.
Example: PERPS_SCREEN_VIEWED with screen_type: 'trading' | 'withdrawal' | ... instead of 9 separate screen events.
Location: app/components/UI/Perps/hooks/usePerpsEventTracking.ts
import { usePerpsEventTracking } from '../../hooks/usePerpsEventTracking';
import { MetaMetricsEvents } from '../../../../core/Analytics';
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
// Declarative: Track on mount
usePerpsEventTracking({
eventName: MetaMetricsEvents.PERPS_SCREEN_VIEWED,
properties: {
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]: PERPS_EVENT_VALUE.SCREEN_TYPE.TRADING,
[PERPS_EVENT_PROPERTY.ASSET]: 'BTC',
},
conditions: [!!asset], // Optional: wait for data
});
// Imperative: Track on action
const { track } = usePerpsEventTracking();
track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
PERPS_EVENT_VALUE.INTERACTION_TYPE.TAP,
[PERPS_EVENT_PROPERTY.ACTION_TYPE]:
PERPS_EVENT_VALUE.ACTION_TYPE.START_TRADING,
});Location: app/controllers/perps/PerpsController.ts, app/controllers/perps/services/TradingService.ts, etc.
The controller and services use trackPerpsEvent from the metrics adapter (see app/components/UI/Perps/adapters/mobileInfrastructure.ts), which maps to MetaMetrics events via AnalyticsEventBuilder.
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
// Inside controller/service (metrics injected via dependencies)
this.#getMetrics().trackPerpsEvent(PerpsAnalyticsEvent.TradeTransaction, {
[PERPS_EVENT_PROPERTY.STATUS]: PERPS_EVENT_VALUE.STATUS.EXECUTED,
[PERPS_EVENT_PROPERTY.ASSET]: params.coin,
[PERPS_EVENT_PROPERTY.ORDER_TYPE]: params.orderType,
[PERPS_EVENT_PROPERTY.COMPLETION_DURATION]: performance.now() - startTime,
});Properties:
screen_type(required): Type of screen being viewed- Core screens:
'markets'|'trading'|'position_close'|'leverage'|'tutorial'|'withdrawal'|'asset_details'|'error' - Home screens:
'perps_home'|'wallet_home_perps_tab'|'homescreen'(deprecated, use perps_home) - TP/SL screens:
'tp_sl'|'create_tpsl'|'edit_tpsl' - Deposit screens:
'deposit_input'|'deposit_review' - Market list screens:
'market_list'|'market_list_all'|'market_list_crypto'|'market_list_stocks' - Position management:
'close_all_positions'|'cancel_all_orders'|'increase_exposure'|'add_margin'|'remove_margin' - Other screens:
'pnl_hero_card'|'order_book'|'full_screen_chart'|'activity'|'geo_block_notif'|'compliance_block_notif'|'cancel_trade_with_token_toast'
- Core screens:
asset(optional): Asset symbol (e.g.,'BTC','ETH')direction(optional):'long' | 'short'source(optional): Where user came from- Entry points:
'banner'|'notification'|'main_action_button'|'deeplink'|'push_notification' - Navigation sources:
'perp_markets'|'perp_market'|'perp_market_search'|'perp_asset_screen'|'position_tab'|'perps_tab' - Home source:
'perps_home'— unified origin for all market taps fromPerpsHomeView(paired withsource_section, see below) - Other home-related sources (legacy/other):
'perps_home_position'|'perps_home_orders'|'perps_home_watchlist'|'perps_home_explore_crypto'|'perps_home_explore_stocks'|'perps_home_activity'|'perps_home_empty_state'— deprecated in favour ofsource = perps_home+source_section - Market list sources:
'perps_market_list_all'|'perps_market_list_crypto'|'perps_market_list_stocks' - Trade/Position sources:
'trade_screen'|'position_screen'|'tp_sl_view'|'trade_menu_action'|'open_position'|'trade_details' - Explore source:
'explore'(from Explore/Trending page) - Discovery sources:
'related_markets'(from Related markets rail on asset details) - Other sources:
'tutorial'|'perps_tutorial'|'close_toast'|'position_close_toast'|'tooltip'|'magnifying_glass'|'crypto_button'|'stocks_button'|'order_book'|'full_screen_chart'|'stop_loss_prompt_banner'|'wallet_home'|'wallet_main_action_menu'|'homescreen_tab'|'perps_asset_screen_no_funds'|'home_section'|'market_insights' - Geo-block sources:
'deposit_button'|'withdraw_button'|'trade_action'|'add_funds_action'|'cancel_order'|'asset_detail_screen'|'close_position_action'|'modify_position_action'|'order_book_long_button'|'order_book_short_button'|'order_book_close_button'|'order_book_modify_button'|'auto_close_action'|'adjust_margin_action'|'stop_loss_prompt_add_margin'|'stop_loss_prompt_set_sl'|'close_all_positions_button'|'cancel_all_orders_button'
- Entry points:
open_position(optional): Number of open positions (used for homepage_perps_tab, perps_home, asset_details, order_book, trading, close_all_positions screens; number)open_order(optional): Number of open orders (used for wallet_home_perps_tab, perps_home, asset_details screens; number)market_category(optional): Currently active market filter tab or watchlist state (used formarket_listscreen)- Filter values:
'all'|'crypto'|'stock'|'commodity'|'forex'|'new' - Watchlist:
'watchlist'— emitted instead of the filter when the watchlist toggle is active
- Filter values:
source_section(optional): Specific sub-section within the origin screen. Present onasset_detailsevents and on per-sectionperps_homeimpression events. Values vary bysource:source = perps_home:'positions'|'orders'|'watchlist'|'whats_happening'|'products'|'top_gainers'|'top_losers'|'crypto'|'commodity'|'stock'|'forex'source = explore:'perps_movers'|'perps_crypto'|'perps_stocks_commodities'|'perps_markets'source = perp_markets:'all_markets'|'crypto'|'stock'|'commodity'|'forex'|'new'|'watchlist'|'active_search'- All values come from
PERPS_EVENT_VALUE.SOURCE_SECTIONin@metamask/perps-controller.
section_name(optional): Stable identifier for a home screen section viewed via scroll impression. Present only onperps_homeimpression events emitted byusePerpsHomeSectionTracking. Values:'balance'|'positions'|'orders'|'watchlist'|'whats_happening'|'products'|'top_movers'|'explore_crypto'|'explore_commodities'|'explore_stocks'|'explore_forex'|'recent_activity'. (PERPS_EVENT_PROPERTY.SECTION_NAME; values fromPERPS_EVENT_VALUE.SECTION_NAME.)section_index(optional): 1-based position of the section among visible (rendered) sections, ranked by y-position. Reflects the real on-screen order and adjusts automatically for A/B reordering or hidden sections. Present alongsidesection_nameon impression events. (PERPS_EVENT_PROPERTY.SECTION_INDEX.)sections_displayed(optional): Ordered array ofsection_namevalues visible on the home screen at the timescreen_type = perps_homefires. Captures the layout composition in one field, enabling analysis of section co-occurrence. (PERPS_EVENT_PROPERTY.SECTIONS_DISPLAYED.)watchlist_count(optional): Number of markets in the user's watchlist at event time. Included in the baseperps_homescreen view. (PERPS_EVENT_PROPERTY.WATCHLIST_COUNT.)watchlist_markets(optional): Ordered array of market symbols in the user's watchlist at event time. Included in the baseperps_homescreen view alongsidewatchlist_count. (PERPS_EVENT_PROPERTY.WATCHLIST_MARKETS.)error_type(optional): Type of error for error screen views (e.g.,'network','backend'; used when screen_type is'error')has_perp_balance(optional): Whether user has a perps balance or positions (boolean)has_take_profit(optional): Whether take profit is set (boolean, used for TP/SL screens)has_stop_loss(optional): Whether stop loss is set (boolean, used for TP/SL screens)dollar_pnl(optional): P&L in dollars (number, used for pnl_hero_card screen)percent_pnl(optional): P&L as percentage (number, used for pnl_hero_card screen)button_clicked(optional): Button that led to this screen (entry point tracking, see Entry Point Tracking)button_location(optional): Location of the button clicked (entry point tracking, see Entry Point Tracking)outage_banner_shown(optional): Whether the service interruption banner is displayed (boolean, used for perps_home, asset_details, trading screens)market_insights_displayed(optional): Whether market insights content is displayed on the screen (boolean, used for asset_details screen)ab_test_button_color(optional): Button color test variant ('control' | 'monochrome'), only included when test is enabled (for baseline exposure tracking)- Future AB tests:
ab_test_{test_name}(see Multiple Concurrent Tests)
Properties:
interaction_type(required): Type of user interaction- Basic interactions:
'tap'|'zoom'|'slide'|'search_clicked'|'button_clicked' - Order interactions:
'order_type_viewed'|'order_type_selected' - Setting interactions:
'setting_changed'(deprecated, use leverage_changed) |'leverage_changed' - Tutorial interactions:
'tutorial_started'|'tutorial_completed'|'tutorial_navigation' - Chart interactions:
'candle_period_viewed'|'candle_period_changed'|'full_screen_chart' - Watchlist interactions:
'favorite_toggled'(add/remove from watchlist) - Position management:
'add_margin'|'remove_margin'|'increase_exposure'|'reduce_exposure'|'flip_position'|'contact_support'|'stop_loss_one_click_prompt' - Hero card interactions:
'display_hero_card'|'share_pnl_hero_card' - Pay-with interactions:
'payment_token_selector'|'payment_method_changed'|'cancel_trade_with_token' - Slippage interactions:
'slippage_config_opened'|'slippage_config_changed'|'slippage_limit_blocked_order' - Discovery interactions:
'related_market_clicked' - Market list filter:
'market_list_filter'— category badge or watchlist toggle tap in PerpsMarketListView (PERPS_EVENT_VALUE.INTERACTION_TYPE.MARKET_LIST_FILTER)
- Basic interactions:
action(optional): Specific action performed:'connection_retry'|'connection_go_back'|'share'|'add_margin'|'remove_margin'|'edit_tp_sl'|'create_tp_sl'|'create_position'|'increase_exposure'|'flip_long_to_short'|'flip_short_to_long'attempt_number(optional): Retry attempt number when action is 'connection_retry' (number)action_type(optional):'start_trading'|'skip'|'stop_loss_set'|'take_profit_set'|'adl_learn_more'|'learn_more'|'favorite_market'|'unfavorite_market'(Note:favorite_market= add to watchlist,unfavorite_market= remove from watchlist)asset(optional): Asset symbol (e.g.,'BTC','ETH')direction(optional):'long' | 'short'order_size(optional): Size of the order in tokens (number)leverage_used(optional): Leverage value being used (number)order_type(optional):'market' | 'limit'setting_type(optional): Type of setting changed:'leverage'|'slippage'input_method(optional): How value was entered:'slider' | 'keyboard' | 'preset' | 'manual' | 'percentage_button'candle_period(optional): Selected candle periodfavorites_count(optional): Total number of markets in watchlist after toggle (number, used withfavorite_toggled)section_viewed(optional): Section viewed through a slide interaction; includes'related_markets'for the asset details Related markets rail.source_market(optional): Source asset symbol for Related markets tile taps.market(optional): Destination asset symbol for Related markets tile taps.category(optional): Collection/List identifier for Related markets tile taps.position(optional): 1-based tile position in the Related markets rail.button_clicked(optional): Button identifier for entry point tracking (see Entry Point Tracking):'deposit'|'withdraw'|'perps_home'|'tutorial'|'tooltip'|'market_list'|'open_position'|'magnifying_glass'|'crypto'|'stocks'|'give_feedback'|'competition_banner_engage'|'competition_banner_close'- Discovery section header values (
PERPS_EVENT_VALUE.BUTTON_CLICKED):'watchlist'|'top_movers'|'whats_happening' - Market list filter values:
'all'|'crypto'|'stock'|'commodity'|'forex'|'watchlist'(used withinteraction_type = market_list_filter)
- Discovery section header values (
button_location(optional): Location of the button for entry point tracking (see Entry Point Tracking):'perps_home'|'perps_tutorial'|'perps_home_empty_state'|'perps_asset_screen'|'perps_tab'|'trade_menu_action'|'wallet_home'|'market_list'|'screen'|'tooltip'|'perp_market_details'|'order_book'|'full_screen_chart'- Discovery value (
PERPS_EVENT_VALUE.BUTTON_LOCATION.ASSET_DETAILS):'asset_details'— magnifying glass button on the asset details screen
- Discovery value (
result_count(optional): Number of search results after a market search query stabilises; included withinteraction_type = search_clicked. Reported for both non-zero and zero-result searches. (PERPS_EVENT_PROPERTY.RESULT_COUNT.)initial_payment_method(optional): Payment method before change (e.g.'perps_balance'or token symbol; used withpayment_method_changed)new_payment_method(optional): Payment method after change (e.g.'perps_balance'or token symbol; used withpayment_method_changed)max_slippage_pct(optional): Current max slippage percentage (number, used with slippage interactions)max_slippage_source(optional): How the slippage value was set:'default' | 'user_configured'(used with slippage interactions)estimated_slippage_pct(optional): Estimated slippage percentage (number, used withslippage_limit_blocked_order)section_viewed(optional): Stable section name scrolled into view (e.g.,'explore_crypto','explore_stocks','recent_activity'; used withslideinteraction). Values matchPERPS_EVENT_VALUE.SECTION_NAMEconstants.location(optional): Location context for scroll tracking (e.g.,'perps_home')source(optional): Source context for favorites (e.g.,'perp_asset_screen')tab_name(optional): Tab being viewed (e.g.,'trades'|'orders'|'funding'|'deposits')screen_name(optional): Screen name context (e.g.,'connection_error'|'perps_hero_card'|'perps_activity_history')navigation_method(optional): How user navigated in tutorial:'swipe'|'continue_button'|'progress_dot'previous_screen(optional): Previous screen position in tutorial navigation (number)current_screen(optional): Current screen position in tutorial navigation (number)screen_position(optional): Screen position in tutorial (number)total_screens(optional): Total screens in tutorial (number)completion_duration_tutorial(optional): Time spent in tutorial (number)steps_viewed(optional): Number of tutorial steps viewed (number)view_occurrences(optional): Number of times tutorial was viewed (number)ab_test_button_color(optional): Button color test variant ('control' | 'monochrome'), only included when test is enabled and user taps Long/Short or Place Order button (for engagement tracking)- Future AB tests:
ab_test_{test_name}(see Multiple Concurrent Tests)
Properties:
status(required):'submitted' | 'executed' | 'partially_filled' | 'failed'asset(required): Asset symbol (e.g.,'BTC','ETH')direction(required):'long' | 'short'order_type(required):'market' | 'limit'leverage(required): Leverage multiplier (number)order_size(required for executed): Size of the order in tokens (number)asset_price(required for executed): Price of the asset (number)completion_duration(required): Duration in milliseconds (number)source(optional): Screen where trade was initiated (e.g.,'perp_asset_screen','order_book_long_button','position_screen','explore')action(optional): Specific trade action:'create_position'|'increase_exposure'|'flip_long_to_short'|'flip_short_to_long'order_value(optional): USD value of the order (order_size × asset_price; number)margin_used(optional): Margin required/used in USDC (number)metamask_fee(optional): MetaMask fee amount in USDC (number)metamask_fee_rate(optional): MetaMask fee rate as decimal (number)discount_percentage(optional): Fee discount percentage (number)estimated_rewards(optional): Estimated reward points (number)take_profit_price(optional): Take profit trigger price (number)stop_loss_price(optional): Stop loss trigger price (number)input_method(optional): How value was entered:'slider' | 'keyboard' | 'preset' | 'manual' | 'percentage_button'limit_price(optional): Limit order price (for limit orders) (number)trade_with_token(optional): Whether the user paid with a token other than Perps balance (boolean)mm_pay_token_selected(optional): Token symbol selected for pay-with (e.g.'USDC'); whentrade_with_tokenis true, the selected token symbol; when user uses Perps balance,'Perps Balance'mm_pay_network_selected(optional): Network/chain for pay-with (e.g.'ethereum'), included whentrade_with_tokenis trueerror_message(optional): Error description when status is 'failed'
Properties:
status(required):'submitted' | 'executed' | 'partially_filled' | 'failed'asset(required): Asset symbol (e.g.,'BTC','ETH')direction(required):'long' | 'short'order_type(required):'market' | 'limit'open_position_size(required): Size of the open position (number)order_size(required): Size being closed (number)completion_duration(required): Duration in milliseconds (number)source(optional): Screen where close was initiated (e.g.,'perp_asset_screen','order_book','position_screen')order_value(optional): USD value of the close order (requested_size × asset_price; number)close_type(optional):'full' | 'partial'percentage_closed(optional): Percentage of position closed (number)dollar_pnl(optional): Profit/loss in dollars (number)percent_pnl(optional): Profit/loss as percentage (number)fee(optional): Fee paid in USDC (number)received_amount(optional): Amount received after close (number)input_method(optional): How value was entered:'slider' | 'keyboard' | 'preset' | 'manual' | 'percentage_button'amount_filled(optional): Amount filled in partially filled orders (number)remaining_amount(optional): Amount remaining in partially filled orders (number)error_message(optional): Error description when status is 'failed'
Properties:
status(required):'submitted' | 'executed' | 'failed'asset(required): Asset symbol (e.g.,'BTC','ETH')completion_duration(required): Duration in milliseconds (number)order_type(optional):'market' | 'limit'error_message(optional): Error description when status is 'failed'
Properties:
status(required):'completed' | 'failed'(withdrawal usescompletedrather thanexecuted)withdrawal_amount(required): Amount being withdrawn in USDC (number)completion_duration(optional): Duration in milliseconds (number)error_message(optional): Error description when status is 'failed'
Properties:
status(required):'submitted' | 'executed' | 'failed'asset(required): Asset symbol (e.g.,'BTC','ETH')completion_duration(required): Duration in milliseconds (number)take_profit_price(at least one required): Take profit trigger price (number)stop_loss_price(at least one required): Stop loss trigger price (number)direction(optional):'long' | 'short'source(optional): Where TP/SL update originated (e.g.,'tp_sl_view','position_screen')action(optional): Which TP/SL fields were set:'tp'(take profit only) |'sl'(stop loss only) |'tpsl'(both)position_size(optional): Size of the position (number)screen_type(optional):'create_tpsl' | 'edit_tpsl'- Whether creating TP/SL for new order or editing existing positionhas_take_profit(optional): Whether take profit is set (boolean)has_stop_loss(optional): Whether stop loss is set (boolean)take_profit_percentage(optional): Take profit percentage from entry price (number)stop_loss_percentage(optional): Stop loss percentage from entry price (number)error_message(optional): Error description when status is 'failed'
Properties:
error_type(required for errors):'network' | 'app_crash' | 'backend' | 'validation' | 'warning'error_message(required for errors): Error description string (preferPERPS_EVENT_VALUE.ERROR_MESSAGE_KEYfor consistent analytics)warning_message(required for warnings): Warning description stringscreen_type(optional): Screen where error/warning occurred (e.g.,'trading','withdrawal','market_list','position_close')screen_name(optional): Specific screen name (e.g.,'connection_error','perps_market_details','perps_order','perps_hero_card','perps_activity_history','perps_home')retry_attempts(optional): Number of retry attempts (number)asset(optional): Asset symbol if error is asset-specific (e.g.,'BTC','ETH')action(optional): Action being attempted when error occurred
Note: This event is used for both errors (with error_type + error_message) and warnings (with warning_message). Use PERPS_EVENT_VALUE.ERROR_MESSAGE_KEY for standardized error message keys (e.g., insufficient_balance, order_failed, geo_restriction).
Tracks unified account (HIP-3) migration lifecycle on HyperLiquid. Fired from the controller's HyperLiquidProvider during account abstraction mode transitions. Not used directly from UI components.
Properties:
status(required): Migration outcome'not_applicable'- Wallet has no HyperLiquid account yet (nothing to migrate)'already_enabled'- Account is already in a compatible mode (unifiedAccountorportfolioMargin)'migration_required'- Account needs migration from a legacy mode'success'- Migration completed successfully'failed'- Migration failed (user rejected signature or network error)
abstraction_mode(required): Current or target account abstraction mode (string, e.g.,'unifiedAccount','dexAbstraction','default','disabled')previous_abstraction_mode(optional): Account mode before migration attempt (string, included on success/failure)error_message(optional): Error description when status is'failed'or'not_applicable'(e.g.,'no_hl_account')
Usage (controller-side only):
// Inside HyperLiquidProvider (via trackPerpsEvent)
this.#deps.metrics.trackPerpsEvent(PerpsAnalyticsEvent.AccountSetup, {
[PERPS_EVENT_PROPERTY.STATUS]: PERPS_EVENT_VALUE.STATUS.SUCCESS,
[PERPS_EVENT_PROPERTY.PREVIOUS_ABSTRACTION_MODE]: currentMode,
[PERPS_EVENT_PROPERTY.ABSTRACTION_MODE]: 'unifiedAccount',
});Note: In code, property names and values are accessed via
PERPS_EVENT_PROPERTYandPERPS_EVENT_VALUEfrom@metamask/perps-controller(source:packages/perps-controller/src/constants/eventNames.tsin the MetaMask/core monorepo). The string values shown in the event sections above are what actually gets sent to Segment.
import { MetaMetricsEvents } from '../../../../core/Analytics';
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
usePerpsEventTracking({
eventName: MetaMetricsEvents.PERPS_SCREEN_VIEWED,
properties: {
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]:
PERPS_EVENT_VALUE.SCREEN_TYPE.YOUR_SCREEN,
},
});const { track } = usePerpsEventTracking();
track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
PERPS_EVENT_VALUE.INTERACTION_TYPE.TAP,
});// Via metrics adapter (trackPerpsEvent)
this.#getMetrics().trackPerpsEvent(PerpsAnalyticsEvent.TradeTransaction, {
[PERPS_EVENT_PROPERTY.STATUS]: PERPS_EVENT_VALUE.STATUS.EXECUTED,
[PERPS_EVENT_PROPERTY.COMPLETION_DURATION]: duration,
});To support multiple AB tests running concurrently (e.g., TAT-1937 button colors, TAT-1940 asset CTA, TAT-1827 homepage CTA), we use flat properties instead of generic properties.
Property Naming: ab_test_{test_name} (no _enabled suffix needed)
Why no _enabled property?
- Events are only sent when test is enabled (
isEnabled === true) - Including the property means the test is active
- No need for redundant
_enabledflag
Example with 3 concurrent tests:
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
usePerpsEventTracking({
eventName: MetaMetricsEvents.PERPS_SCREEN_VIEWED,
properties: {
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]:
PERPS_EVENT_VALUE.SCREEN_TYPE.ASSET_DETAILS,
[PERPS_EVENT_PROPERTY.ASSET]: 'BTC',
// Test 1: Button color test (TAT-1937) - only included when enabled
...(isButtonColorTestEnabled && {
[PERPS_EVENT_PROPERTY.AB_TEST_BUTTON_COLOR]: buttonColorVariant,
}),
// Test 2: Asset CTA test (TAT-1940) - future
...(isAssetCTATestEnabled && {
[PERPS_EVENT_PROPERTY.AB_TEST_ASSET_CTA]: assetCTAVariant,
}),
// Test 3: Homepage CTA test (TAT-1827) - future
...(isHomepageCTATestEnabled && {
[PERPS_EVENT_PROPERTY.AB_TEST_HOMEPAGE_CTA]: homepageCTAVariant,
}),
},
});✅ Track in both events: Use dual tracking to enable engagement rate calculation.
Dual Tracking Approach:
-
PERPS_SCREEN_VIEWED (baseline exposure):
- Include
ab_test_button_colorwhen test is enabled - Establishes how many users were exposed to each variant
- Required to calculate engagement rate
- Include
-
PERPS_UI_INTERACTION (engagement):
- Include
ab_test_button_colorwhen user taps Long/Short or Place Order button - Only sent when test is enabled
- Measures which variant drives more button presses
- Include
Why Both Events?
- Engagement Rate = Button presses / Screen views per variant
- Answers: "Which button color makes users more likely to press the button?"
Example: For TAT-1937 (button color test):
- Screen views establish baseline (how many saw control vs monochrome)
- Button presses measure engagement
- Compare button presses to screen views for each variant
For details, see perps-ab-testing.md.
The PnL Hero Card screen is tracked with additional P&L context and source information.
screen_type:'pnl_hero_card'source:'close_toast' | 'perp_asset_screen'- How user arrived at the screendollar_pnl: P&L in dollars (number)percent_pnl: P&L as percentage (ROE)asset: Asset symbol (e.g.,'BTC')direction:'long' | 'short'
| Value | Description |
|---|---|
'close_toast' |
User tapped on the close position success toast |
'perp_asset_screen' |
User navigated from the asset/position screen |
The TP/SL (Take Profit / Stop Loss) tracking differentiates between creating TP/SL for a new order vs editing TP/SL for an existing position.
| Value | Description |
|---|---|
'create_tpsl' |
Creating TP/SL for a new order (before order placement) |
'edit_tpsl' |
Editing TP/SL for an existing position |
has_take_profit: Whether take profit is currently set (boolean)has_stop_loss: Whether stop loss is currently set (boolean)take_profit_percentage: Take profit percentage from entry pricestop_loss_percentage: Stop loss percentage from entry price
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
// Screen view tracking
usePerpsEventTracking({
eventName: MetaMetricsEvents.PERPS_SCREEN_VIEWED,
properties: {
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]: isEditingExistingPosition
? PERPS_EVENT_VALUE.SCREEN_TYPE.EDIT_TPSL
: PERPS_EVENT_VALUE.SCREEN_TYPE.CREATE_TPSL,
[PERPS_EVENT_PROPERTY.ASSET]: asset,
[PERPS_EVENT_PROPERTY.HAS_TAKE_PROFIT]: !!initialTakeProfitPrice,
[PERPS_EVENT_PROPERTY.HAS_STOP_LOSS]: !!initialStopLossPrice,
},
});Entry point tracking captures how users navigate to screens, enabling analysis of user flows and button effectiveness.
button_clicked: Identifies which button was clickedbutton_location: Identifies where the button was located
| Value | Description |
|---|---|
'deposit' |
Add funds / deposit button |
'withdraw' |
Withdraw funds button |
'perps_home' |
Navigate to perps home button |
'tutorial' |
Learn more / tutorial button |
'tooltip' |
Got it button in tooltip bottom sheets |
'market_list' |
Market list navigation button |
'open_position' |
Tap on a position card |
'magnifying_glass' |
Search icon button |
'crypto' |
Crypto tab in market list |
'stocks' |
Stocks & Commodities tab in market list |
'commodities' |
Commodities tab |
'forex' |
Forex tab |
'new' |
New markets |
'give_feedback' |
Give feedback button |
'competition_banner_engage' |
User tapped the competition banner to navigate to rewards (local constant, pending addition to PERPS_EVENT_VALUE) |
'competition_banner_close' |
User dismissed the competition banner (local constant, pending addition to PERPS_EVENT_VALUE) |
'watchlist' |
Watchlist section "See all" header tap (home) or watchlist toggle in market list (local constant) |
'top_movers' |
Top Movers section "View All" header tap (local constant) |
'whats_happening' |
What's Happening section header tap (local constant) |
| Value | Description |
|---|---|
'perps_home' |
Perps home screen |
'perps_tutorial' |
Tutorial screen |
'perps_home_empty_state' |
Perps home empty state (no balance) |
'perps_asset_screen' |
Asset details screen |
'perps_tab' |
Positions tab |
'trade_menu_action' |
Trade menu action button |
'wallet_home' |
Wallet home screen |
'market_list' |
Market list screen |
'screen' |
Generic screen location |
'tooltip' |
Tooltip bottom sheet |
'perp_market_details' |
Market details screen |
'order_book' |
Order book screen |
'full_screen_chart' |
Full screen chart view |
'asset_details' |
Asset details screen — magnifying glass button |
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
// Track button click
track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
PERPS_EVENT_VALUE.INTERACTION_TYPE.BUTTON_CLICKED,
[PERPS_EVENT_PROPERTY.BUTTON_CLICKED]:
PERPS_EVENT_VALUE.BUTTON_CLICKED.DEPOSIT,
[PERPS_EVENT_PROPERTY.BUTTON_LOCATION]:
PERPS_EVENT_VALUE.BUTTON_LOCATION.PERPS_HOME,
});
// Pass to navigation for screen view tracking
navigation.navigate(Routes.PERPS.MARKET_LIST, {
button_clicked: PERPS_EVENT_VALUE.BUTTON_CLICKED.MAGNIFYING_GLASS,
button_location: PERPS_EVENT_VALUE.BUTTON_LOCATION.PERPS_HOME,
});
// Include in screen view event
usePerpsEventTracking({
eventName: MetaMetricsEvents.PERPS_SCREEN_VIEWED,
properties: {
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]:
PERPS_EVENT_VALUE.SCREEN_TYPE.MARKET_LIST,
...(buttonClicked && {
[PERPS_EVENT_PROPERTY.BUTTON_CLICKED]: buttonClicked,
}),
...(buttonLocation && {
[PERPS_EVENT_PROPERTY.BUTTON_LOCATION]: buttonLocation,
}),
},
});When users are geo-blocked, the geo_block_notif screen type is used to track which action triggered the block.
screen_type:'geo_block_notif'source: What action was blocked'deposit_button'- User tried to deposit'withdraw_button'- User tried to withdraw'trade_action'- User tried to place a trade'add_funds_action'- User tried to add funds'cancel_order'- User tried to cancel an order'asset_detail_screen'- User tried to access asset details
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
usePerpsEventTracking({
eventName: MetaMetricsEvents.PERPS_SCREEN_VIEWED,
properties: {
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]:
PERPS_EVENT_VALUE.SCREEN_TYPE.GEO_BLOCK_NOTIF,
[PERPS_EVENT_PROPERTY.SOURCE]: PERPS_EVENT_VALUE.SOURCE.DEPOSIT_BUTTON, // or other blocked action
},
});When a wallet is blocked by an OFAC compliance check, the compliance_block_notif screen type is used. The event fires automatically via AccessRestrictedContext whenever showAccessRestrictedModal() is called — no per-action tracking is needed.
screen_type:'compliance_block_notif'
import {
PERPS_EVENT_PROPERTY,
PERPS_EVENT_VALUE,
} from '@metamask/perps-controller';
// PERPS_EVENT_VALUE.SCREEN_TYPE.COMPLIANCE_BLOCK_NOTIF === 'compliance_block_notif'The event is tracked in showAccessRestrictedModal inside AccessRestrictedContext.tsx. All call sites — useComplianceGate and every gated action handler — call this method, so coverage is automatic.
// app/components/UI/Compliance/contexts/AccessRestrictedContext.tsx
const showAccessRestrictedModal = useCallback(() => {
notificationAsync(NotificationFeedbackType.Warning);
setIsVisible(true);
track(MetaMetricsEvents.PERPS_SCREEN_VIEWED, {
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]:
PERPS_EVENT_VALUE.SCREEN_TYPE.COMPLIANCE_BLOCK_NOTIF,
});
}, [track]);| Geo-blocking | Compliance blocking | |
|---|---|---|
screen_type |
geo_block_notif |
compliance_block_notif |
| Tracked per action | Yes — each handler tracks source |
No — tracked once in context |
| Source property | Identifies the blocked action | Not included (all routes share the same modal) |
| Feature flag | Geo eligibility | complianceEnabled |
The Perps Discovery System uses a unified source + source_section model across all entry points. source identifies the high-level origin screen; source_section identifies the specific sub-section within it.
Deprecation note: Encoded
sourcevalues likeperps_home_watchlist,perps_home_explore_crypto,perps_home_top_gainersare replaced bysource = perps_home+source_section. The old encoded values still exist in@metamask/perps-controllerbut are no longer emitted from the discovery system.
When a user taps a market in PerpsHomeView, source = perps_home and source_section identifies the section:
| Home section | source_section value |
Constant |
|---|---|---|
| Position card | 'positions' |
PERPS_EVENT_VALUE.SOURCE_SECTION.POSITIONS |
| Order card | 'orders' |
PERPS_EVENT_VALUE.SOURCE_SECTION.ORDERS |
| Watchlist | 'watchlist' |
PERPS_EVENT_VALUE.SOURCE_SECTION.WATCHLIST |
| What's Happening | 'whats_happening' |
PERPS_EVENT_VALUE.SOURCE_SECTION.WHATS_HAPPENING |
| Products | 'products' |
PERPS_EVENT_VALUE.SOURCE_SECTION.PRODUCTS |
| Top Movers (gainers) | 'top_gainers' |
PERPS_EVENT_VALUE.SOURCE_SECTION.TOP_GAINERS |
| Top Movers (losers) | 'top_losers' |
PERPS_EVENT_VALUE.SOURCE_SECTION.TOP_LOSERS |
| Explore › Crypto | 'crypto' |
PERPS_EVENT_VALUE.SOURCE_SECTION.CRYPTO |
| Explore › Commodities | 'commodity' |
PERPS_EVENT_VALUE.SOURCE_SECTION.COMMODITY |
| Explore › Stocks | 'stock' |
PERPS_EVENT_VALUE.SOURCE_SECTION.STOCK |
| Explore › Forex | 'forex' |
PERPS_EVENT_VALUE.SOURCE_SECTION.FOREX |
When a user taps a market in the Explore/Trending view, source = explore and source_section identifies the tab's Perps section:
| Explore tab | Perps section | source_section value |
Constant |
|---|---|---|---|
| Now | Perps Movers (pill list) | 'perps_movers' |
PERPS_EVENT_VALUE.SOURCE_SECTION.PERPS_MOVERS |
| Crypto | Crypto Perps (tiles) | 'perps_crypto' |
PERPS_EVENT_VALUE.SOURCE_SECTION.PERPS_CRYPTO |
| Macro | Stocks & Commodities | 'perps_stocks_commodities' |
PERPS_EVENT_VALUE.SOURCE_SECTION.PERPS_STOCKS_COMMODITIES |
| RWAs | RWA markets | 'perps_markets' |
PERPS_EVENT_VALUE.SOURCE_SECTION.PERPS_MARKETS |
When navigating from PerpsMarketListView → PerpsMarketDetailsView, source = perp_markets (always) and source_section carries the active subsection at the time of tap:
| Active state in market list | source_section value |
Constant |
|---|---|---|
| No filter, no search | 'all_markets' |
PERPS_EVENT_VALUE.SOURCE_SECTION.ALL_MARKETS |
| Crypto filter active | 'crypto' |
PERPS_EVENT_VALUE.SOURCE_SECTION.CRYPTO |
| Stock filter active | 'stock' |
PERPS_EVENT_VALUE.SOURCE_SECTION.STOCK |
| Commodity filter active | 'commodity' |
PERPS_EVENT_VALUE.SOURCE_SECTION.COMMODITY |
| Forex filter active | 'forex' |
PERPS_EVENT_VALUE.SOURCE_SECTION.FOREX |
| New markets filter active | 'new' |
PERPS_EVENT_VALUE.SOURCE_SECTION.NEW |
| Watchlist toggle active | 'watchlist' |
PERPS_EVENT_VALUE.SOURCE_SECTION.WATCHLIST |
| Search query present | 'active_search' |
PERPS_EVENT_VALUE.SOURCE_SECTION.ACTIVE_SEARCH |
usePerpsHomeSectionTracking emits two events per section as the user scrolls it into view:
PERPS_UI_INTERACTION { interaction_type: slide, section_viewed: <section_name>, location: perps_home }— existing slide event (kept)PERPS_SCREEN_VIEWED { screen_type: perps_home, section_name, section_index }— per-section impression
Section names used in impression events:
| Home section | section_name value |
PERPS_EVENT_VALUE.SECTION_NAME constant |
|---|---|---|
| Balance | 'balance' |
BALANCE |
| Positions | 'positions' |
POSITIONS |
| Orders | 'orders' |
ORDERS |
| Watchlist | 'watchlist' |
WATCHLIST |
| What's Happening | 'whats_happening' |
WHATS_HAPPENING |
| Products | 'products' |
PRODUCTS |
| Top Movers | 'top_movers' |
TOP_MOVERS |
| Explore Crypto | 'explore_crypto' |
EXPLORE_CRYPTO |
| Explore Commodities | 'explore_commodities' |
EXPLORE_COMMODITIES |
| Explore Stocks | 'explore_stocks' |
EXPLORE_STOCKS |
| Explore Forex | 'explore_forex' |
EXPLORE_FOREX |
| Recent Activity | 'recent_activity' |
RECENT_ACTIVITY |
section_index = 1-based rank by y-position among rendered sections. Adjusts automatically for hidden or A/B-reordered sections.
All
source_sectionandsection_namevalues are sourced from@metamask/perps-controller(PERPS_EVENT_VALUE.SOURCE_SECTIONandPERPS_EVENT_VALUE.SECTION_NAME).
- Use constants - Never hardcode strings
- Track status - Always include success/failure
- Track duration - Include
completion_durationfor transactions - Use properties - Don't create new events for minor variations
- Auto timestamp -
usePerpsEventTrackingadds it automatically - AB test tracking - Only in screen view events, not every interaction
- Entry point tracking - Include
button_clickedandbutton_locationto track user navigation flows - Source = current screen - The
sourceproperty must always identify the screen the user is currently on, never a screen from earlier in the navigation chain. If the user navigates A → B → action C, the source for C must be B, not A. - Explicit source passing - Reusable components (e.g.,
PerpsMarketTypeSection,PerpsWatchlistMarkets,PerpsCard) must receivesourceas a prop from the parent screen rather than setting it implicitly. The screen component is the single owner of thesourcevalue.
| Sentry | MetaMetrics |
|---|---|
| 38+ traces | 9 events |
| Performance | Behavior |
| Technical metrics | Business metrics |
usePerpsMeasurement |
usePerpsEventTracking |
- Event Tracking Hook:
app/components/UI/Perps/hooks/usePerpsEventTracking.ts - Events:
app/core/Analytics/MetaMetrics.events.ts - Properties & Values: Exported from
@metamask/perps-controllerasPERPS_EVENT_PROPERTY,PERPS_EVENT_VALUE(source:packages/perps-controller/src/constants/eventNames.tsin the MetaMask/core monorepo) - Metrics Adapter:
app/components/UI/Perps/adapters/mobileInfrastructure.ts(mapstrackPerpsEventto MetaMetrics) - Controller:
@metamask/perps-controller(PerpsController,HyperLiquidProvider,TradingService,AccountService) - Asset Viewed Funnel:
app/core/Analytics/trade-transaction-funnel/assetViewedAnalytics.ts(parallelASSET_VIEWEDemission for Perps)