Two weeks since real launch. Here's what we can see, what we can't, and what we need to set up now.
Rewards is an onboarding program for the Ramp Network mobile app. Users complete missions across four sections, each driving a first-time action. The end goal: complete all missions to unlock a loyalty bonus (0.25 USDC cashback on every future transaction).
| Section | Missions | Reward |
|---|---|---|
| Welcome to Ramp Network | WALLET (connect wallet) | Part of 1.00 USDC |
| Earn with USDC | TOPUP1 (top up $25+), EARN10 (earn 10 days) | 0.75 USDC |
| Discover swaps | SWAP1 (first swap $50+), SWAP3 (3 swaps) | 2.75 USDC |
| Grow your network | INV3 (invite 3), INV7 (invite 7), SEND5 (send to 5) | 1.00 USDC |
| Unlock loyalty bonus | ONBOARD (complete all above) | 1.00 USDC + ongoing cashback |
The reward structure incentivizes the right behavior. Swaps pay the most (2.75 USDC combined) because they're the highest-margin transaction type. The loyalty bonus at the end creates a reason to complete the full journey, not just cherry-pick easy missions.
Mobile app only (by design). Non-EU/EEA (regulatory). Release flag (rewards-feature, ID 10188193) at 100% rollout for eligible users. No holdout group. Created by Jakub Jastrzebski, last modified by Marek Rycerski.
Rewards visitors show dramatically different behavior from the general population:
| Metric | Rewards Visitors | All Users | Multiplier |
|---|---|---|---|
| W1 rolling retention | 59.7% | 8.1% | 7.4x |
| W2 rolling retention | 69.8% | 5.6% | 12.4x |
| Weekly transaction rate | 33.1% | 10.4% | 3.2x |
33.1% of rewards visitors completed a transaction in the week of March 23, compared to 10.4% of the general population.
42% of users who land on the app dashboard navigate to rewards within 30 minutes (Marek's funnel). That's strong discoverability. iOS penetration is 15.7% (1,035 of 6,594 users). Android is 6.3% (424 of 6,710) with identical flag targeting, so the gap is behavioral or UX, not configuration.
The product design supports return visits. The loyalty gauge (3/10) and "Earned" counter create visible progress. "Complete missions in any order" reduces friction. Users see their journey status every time they open rewards.
The causation gap. Whether this is causation or selection bias is unknown. We ran the causality analysis (next section). The answer is clear: the data cannot separate the two, and the structure of the experiment makes it impossible without a holdout.
What to do: Modify the rewards-feature flag (Segment 2) from 100% to 50% to create a randomized holdout. Two weeks of data at current volumes should be enough for the primary retention metric. This is the single most important thing to set up.
We ran four tests. All four point the same way: there is no natural control group, so we cannot attribute any behavioral lift to rewards.
We measured average daily page_views per user in the pre-period (Feb 14 - Mar 15) for two segments: (1) users who later visited the Rewards screen after Mar 16, and (2) all eligible users.
| Segment | Feb Unique Users | Mar Unique Users | Daily Avg Page Views |
|---|---|---|---|
| Future rewards visitors | 8 | 3,038 | Identical to below |
| All eligible users | 8 | 3,038 | Identical to above |
The two segments are literally the same users. Every user who was active in the pre-period later visited rewards. There is no group of "active users who skipped rewards" to compare against.
Pre-period transactions per user: zero for both segments across every day. The experiment was too new for baseline transaction behavior.
Chart: pre-period page_views | Chart: pre-period transactions | Chart: segment sizes
We split users by pre-period activity (1-3 page_views = low, 10+ = high) and compared post-launch transaction rates for rewards visitors vs non-visitors within each group.
| Cohort | Rewards Visitors (Weekly Txns) | Non-Visitors (Weekly Txns) | Non-Visitor Segment Size |
|---|---|---|---|
| Low-activity (1-3 pre-period PVs) | 1, 2, 5 | 0, 0, 0 | 0 users |
| High-activity (10+ pre-period PVs) | 135, 157, 78 | 0, 0, 0 | 0 users |
The "non-visitor" segments are empty. Zero users. Every previously-active user found and visited rewards. There is no control group to compare against.
Chart: low-activity | Chart: high-activity | Chart: cohort sizes
The 7.4x retention multiplier is not measuring the effect of rewards. It's measuring the difference between users who come back to the app (and inevitably find rewards) and users who don't come back at all. Rewards visitors are more retained because they're the users who return, not because rewards made them return.
This is a structural problem, not a data gap. The rewards-feature flag is a release flag at 100% rollout. Every eligible user sees rewards. There is no randomly withheld group. Any comparison between "visitors" and "non-visitors" is comparing returning users to churned users.
The only way to measure causal impact is a randomized holdout. Set the flag to 50% for Segment 2. Two weeks at current volumes gives enough power for the retention metric. Without this, every retention and engagement number in this report (and Marek's dashboard) carries an asterisk.
Users are clearly interested in this section. 57% section-to-action conversion is the highest across the board. They click through, they start. But EARN10 has zero claims across the entire period.
From the app: a user at 1/2 progress on "Earn with USDC" has completed TOPUP1 but not EARN10. This is the exact pattern the aggregate data shows.
EARN10 requires maintaining a $25+ USDC balance for 10 consecutive days. If the balance drops below $25 at any point (withdrawal, price movement), progress is fully deleted. Not decremented. The entire mission event history is wiped via a transactional delete.
Silent destructive resets. From the code (earn-10-days.tracker.ts): the withdrawal handler checks the balance, and if it's below threshold, calls stopTrackingMission. This deletes the MissionEventLogEntity and MissionEntity rows. The backend publishes a user stream event with completedSteps: 0. The frontend receives this and silently refetches. No toast, no message, no explanation. The user's progress bar drops from e.g. 5/10 to 0/10 and they have no idea why.
10 consecutive days is already a high bar for a first-time earn mission. Combined with silent destructive resets, the failure mode is: user deposits, sees progress building over a week, makes a small withdrawal, progress vanishes, user gives up.
Show users why progress reset. "Your balance dropped below $25 USDC. Keep your balance above $25 to maintain your earning streak."
Evaluate whether 10 consecutive days is the right threshold for an onboarding mission. Watch TOPUP1 weekly trend as a leading indicator of deposit behavior.
17 SWAP1 claims in two weeks. Small. The swap section pays the most (2.75 USDC combined) and has 41% section-to-action conversion, so users are engaging with the CTA. But few complete.
The section bundles SWAP1 and SWAP3 under "Discover swaps - 2.75 USDC" so users see the combined reward without necessarily understanding the breakdown.
The question we can't answer: Do the 17 users who completed SWAP1 go on to swap again organically? This is the whole point. If the mission just rewards a swap that would have happened anyway, it's not driving behavior. If it creates a habit (swap 2, 3, 4 after claiming), it's working. There's no post-mission activity tracking to measure this.
Instrument post-claim activity: track transactions after mission completion, excluding the completing transaction itself. Once sample grows, compare swap frequency before and after SWAP1 completion for the same users. Consider merging SWAP3 into SWAP1 or auto-completing it when organic swaps accumulate.
| Mission | Claims | Section Conversion |
|---|---|---|
| INV3 (invite 3) | 0 | "Grow your network": 49% |
| INV7 (invite 7) | 0 | |
| SEND5 (send to 5) | 0 |
49% section-to-action conversion. Second highest. Users click "Add contacts" but nobody completes. Both INV3 and INV7 use the identical CTA text "Add contacts" (code review confirmed in rewards-actions.use-case.ts), which feels redundant.
Social missions are inherently slower than transactional ones. Getting 3 friends to sign up in 2 weeks is ambitious. 7 is unrealistic at this stage. SEND5 (send to 5 different people) is a high bar for someone who just started.
Give it more time. Social missions may convert on a longer timescale. If INV3 starts converting but INV7 doesn't, consider lowering the INV7 threshold or removing it. SEND5 may need a lower target initially.
| What We Can't Measure | Why It Matters | Status |
|---|---|---|
| Causality | The fundamental question: rewards causes activity vs attracts active users | Flag at 100%, no holdout group |
| Program cost | Can't calculate ROI even if we prove impact | Data is in the rewards DB (designed for aggregation). Needs a SQL query, not a new pipeline |
| Unclaimed reward rate | Don't know if users are earning and not claiming | SQL query comparing AWARDED to CLAIMED status in the rewards DB. Nobody has run it yet |
| Post-mission organic activity | Can't answer "does completing X lead to more X?" | Not instrumented |
| Android gap root cause | 15.7% iOS vs 6.3% Android, identical targeting | Needs UX investigation |
| Issue | Impact |
|---|---|
| EARN10 progress resets silently on withdrawal | Progress bar drops to 0 with no explanation. Confirmed: no explicit ideation of broken streak UX |
| MISSION_INCOMPLETE mapped to 'available' | Green "available" badge on missions that aren't complete |
| No post-claim feedback | "Reward claim submitted" toast with no timeline, no receipt |
| Deprecated claim endpoint still in use | Frontend uses POST /rewards/claim with rewardIds[]. New POST /rewards/:id/claim has zero consumers |
Cashback is live. Confirmed on in production config. REWARDS_CASHBACK_AWARDING_ENABLED defaults to false in code (as a feature flag should) but is toggled on in the live environment. "Unlock loyalty bonus" in the product UI is the ONBOARD meta-mission: completing all missions unlocks 0.25 USDC cashback on every future transaction.
amountWei on every reward, designed for aggregation. Run a SQL query to get total payouts, cost per user, and payout volume over time. This doesn't need a new pipeline - the data is there.| Source | What | Scope |
|---|---|---|
| Amplitude (100017324) | page_view, click events, retention, funnels, user properties | New Widget - Production |
| Omni | fct_widget_events, transactions_simplified | March 2026 |
| Marek's dashboard | e-wbjlpksc | Launch markets, properly filtered |
| Flag config | 10188193 | Targeting rules, rollout % |
| Code review | /packages/backend/src/rewards/, /frontend/widget-2/src/features/rewards/ | Two independent reviews |
| Product UI | App screenshots | Current production build |
| Chart | ID | What |
|---|---|---|
| Pre-period page_views | e-25z3qh1e | Avg daily PVs, rewards visitors vs all (identical) |
| Pre-period transactions | e-wftul308 | Avg daily txns, rewards visitors vs all (both zero) |
| Low-activity post-launch | e-1yh0mu83 | Weekly txn uniques, 1-3 pre-PV users |
| High-activity post-launch | e-dk8tx7m9 | Weekly txn uniques, 10+ pre-PV users |
| Pre-period segment sizes | e-688g6ypp | Unique users per segment (identical: 8 Feb, 3038 Mar) |
| Post-period cohort sizes | e-ivwibbql | Non-visitor segments are empty (0 users) |
| Mission | Claims | Status |
|---|---|---|
| WALLET | 115 | Working |
| TOPUP1 | 65 | Working |
| TX1 | 30 | New, needs investigation |
| SWAP1 | 17 | Small but nonzero |
| SWAP3 | 2 | Effectively dead |
| EARN10 | 0 | Blocked (see EARN10 analysis) |
| INV3 | 0 | Too early to call |
| INV7 | 0 | Threshold likely too high |
| SEND5 | 0 | Threshold likely too high |