@ak--47/dungeon-master 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dungeons/technical/array-of-object-lookup.js +0 -2
  3. package/dungeons/technical/hook-helpers-verify.js +89 -0
  4. package/dungeons/technical/identity-model-verify.js +47 -0
  5. package/dungeons/technical/pattern-aggregate-by-bin.js +41 -0
  6. package/dungeons/technical/pattern-attributed-by-source.js +42 -0
  7. package/dungeons/technical/pattern-frequency-by-frequency.js +40 -0
  8. package/dungeons/technical/pattern-funnel-frequency.js +54 -0
  9. package/dungeons/technical/pattern-ttc-by-segment.js +45 -0
  10. package/dungeons/technical/simple.js +3 -4
  11. package/dungeons/technical/simplest-schema.json +5 -0
  12. package/dungeons/technical/text-generation.js +1 -1
  13. package/dungeons/vertical/ai-platform.js +216 -164
  14. package/dungeons/vertical/community.js +74 -41
  15. package/dungeons/vertical/crypto.js +343 -233
  16. package/dungeons/vertical/dating.js +246 -292
  17. package/dungeons/vertical/devtools.js +179 -70
  18. package/dungeons/vertical/ecommerce.js +275 -122
  19. package/dungeons/vertical/education.js +345 -312
  20. package/dungeons/vertical/fintech.js +445 -314
  21. package/dungeons/vertical/fitness.js +199 -165
  22. package/dungeons/vertical/food-delivery.js +309 -330
  23. package/dungeons/vertical/gaming.js +387 -241
  24. package/dungeons/vertical/healthcare.js +149 -66
  25. package/dungeons/vertical/insurance-application.js +173 -75
  26. package/dungeons/vertical/logistics.js +107 -12
  27. package/dungeons/vertical/marketplace.js +171 -76
  28. package/dungeons/vertical/media.js +259 -402
  29. package/dungeons/vertical/real-estate.js +336 -318
  30. package/dungeons/vertical/sass.js +294 -288
  31. package/dungeons/vertical/social.js +289 -234
  32. package/dungeons/vertical/travel.js +66 -20
  33. package/index.js +17 -17
  34. package/lib/core/config-validator.js +226 -175
  35. package/lib/core/context.js +10 -24
  36. package/lib/core/storage.js +6 -1
  37. package/lib/generators/events.js +57 -104
  38. package/lib/generators/funnels.js +212 -93
  39. package/lib/generators/mirror.js +3 -2
  40. package/lib/generators/product-names.js +1 -1
  41. package/lib/generators/scd.js +2 -1
  42. package/lib/generators/text.js +1 -1
  43. package/lib/hook-helpers/_internal.js +23 -0
  44. package/lib/hook-helpers/cohort.js +124 -0
  45. package/lib/hook-helpers/identity.js +56 -0
  46. package/lib/hook-helpers/index.js +44 -0
  47. package/lib/hook-helpers/inject.js +99 -0
  48. package/lib/hook-helpers/mutate.js +151 -0
  49. package/lib/hook-helpers/timing.js +99 -0
  50. package/lib/hook-patterns/aggregate-per-user-by-bin.js +38 -0
  51. package/lib/hook-patterns/attributed-by-source.js +72 -0
  52. package/lib/hook-patterns/frequency-by-frequency.js +46 -0
  53. package/lib/hook-patterns/funnel-frequency-breakdown.js +73 -0
  54. package/lib/hook-patterns/index.js +14 -0
  55. package/lib/hook-patterns/time-to-convert-by-segment.js +41 -0
  56. package/lib/orchestrators/user-loop.js +143 -293
  57. package/lib/utils/utils.js +97 -52
  58. package/lib/verify/emulate-breakdown.js +281 -0
  59. package/lib/verify/index.js +12 -0
  60. package/lib/verify/verify-dungeon.js +61 -0
  61. package/package.json +6 -4
  62. package/scripts/smoke-test-all.mjs +162 -0
  63. package/scripts/verify-runner.mjs +72 -24
  64. package/types.d.ts +434 -225
  65. package/dungeons/technical/ad-spend-schema.json +0 -128
  66. package/dungeons/technical/anonymous-users-schema.json +0 -92
  67. package/dungeons/technical/array-of-object-lookup-schema.json +0 -191
  68. package/dungeons/technical/experiments-schema.json +0 -203
  69. package/dungeons/technical/foobar-schema.json +0 -362
  70. package/dungeons/technical/group-analytics-schema.json +0 -241
  71. package/dungeons/technical/mirror-strategies-schema.json +0 -84
  72. package/dungeons/technical/nested-objects-schema.json +0 -145
  73. package/dungeons/technical/retention-cadence-schema.json +0 -37
  74. package/dungeons/technical/sanity-schema.json +0 -185
  75. package/dungeons/technical/scale-test-schema.json +0 -70
  76. package/dungeons/technical/scd-schema.json +0 -467
  77. package/dungeons/technical/simple-schema.json +0 -362
  78. package/dungeons/technical/text-generation-schema.json +0 -1062
  79. package/dungeons/vertical/ai-platform-schema.json +0 -617
  80. package/dungeons/vertical/community-schema.json +0 -579
  81. package/dungeons/vertical/crypto-schema.json +0 -546
  82. package/dungeons/vertical/dating-schema.json +0 -401
  83. package/dungeons/vertical/devtools-schema.json +0 -601
  84. package/dungeons/vertical/ecommerce-schema.json +0 -604
  85. package/dungeons/vertical/education-schema.json +0 -5686
  86. package/dungeons/vertical/fintech-schema.json +0 -630
  87. package/dungeons/vertical/fitness-schema.json +0 -530
  88. package/dungeons/vertical/food-delivery-schema.json +0 -36728
  89. package/dungeons/vertical/gaming-schema.json +0 -2703
  90. package/dungeons/vertical/healthcare-schema.json +0 -549
  91. package/dungeons/vertical/insurance-application-schema.json +0 -485
  92. package/dungeons/vertical/logistics-schema.json +0 -574
  93. package/dungeons/vertical/marketplace-schema.json +0 -533
  94. package/dungeons/vertical/media-schema.json +0 -4749
  95. package/dungeons/vertical/real-estate-schema.json +0 -527
  96. package/dungeons/vertical/sass-schema.json +0 -3128
  97. package/dungeons/vertical/social-schema.json +0 -620
  98. package/dungeons/vertical/travel-schema.json +0 -580
@@ -1,7 +1,7 @@
1
1
  // ── TWEAK THESE ──
2
2
  const SEED = "coinnest";
3
3
  const num_days = 120;
4
- const num_users = 6_000;
4
+ const num_users = 10_000;
5
5
  const avg_events_per_user_per_day = 0.83;
6
6
  let token = "your-mixpanel-token";
7
7
 
@@ -46,85 +46,224 @@ const chance = u.initChance(SEED);
46
46
 
47
47
  /**
48
48
  * ===================================================================
49
- * ANALYTICS HOOKS (8 architected patterns)
49
+ * ANALYTICS HOOKS (10 hooks)
50
50
  * ===================================================================
51
51
  *
52
- * 1. WHALE WALLETS (everything hookpurchase value)
53
- * 2% of wallets do 60% of trade volume. Deterministic via
54
- * charCodeAt(0) % 50 === 0. Whale swaps are 50x bigger with
55
- * is_whale=true.
56
- * → Insights: "swap" total trade_amount_usd, breakdown is_whale
57
- * → Insights: "swap" count of unique users, filter is_whale = true
58
- * (expect ~2% of users, ~60% of total volume)
59
- *
60
- * 2. GAS PRICE SPIKE (event + everything hook — time-based)
61
- * Days 35-37, network congestion. Gas fees 10x on swap/transfer,
62
- * gas_spike=true. 40% of swaps in this window fail.
63
- * → Insights: "swap" avg gas_fee_usd by day
64
- * (expect massive spike at days 35-37)
65
- * → Insights: "swap" count, breakdown swap_status, filter gas_spike=true
66
- * (expect ~40% failed during spike)
67
- *
68
- * 3. TOKEN LAUNCH SURGE (event + everything hook — timed release)
69
- * Day 50, "MOON" token lists. After day 50, 25% of swaps become
70
- * MOON trades. MOON traders get 5x event volume via cloned swaps.
71
- * → Insights: "swap" total by day, breakdown moon_trade
72
- * (expect large surge after day 50)
73
- * → Insights: "swap" total trade_amount_usd, filter moon_trade=true
74
- *
75
- * 4. AIRDROP HUNTER CHURN (everything hook — churn)
76
- * Users who claim airdrops but never swap are bots. 95% of their
77
- * events after the airdrop claim are removed.
78
- * → Retention: any event, segment by users who did "claim airdrop"
79
- * (expect abysmal retention for airdrop-only users)
80
- * → Insights: "claim airdrop" unique users vs "swap" unique users
81
- *
82
- * 5. KYC FUNNEL COMPLETION (everything hook — conversion)
83
- * KYC-verified users deposit 4x more and swap 8x more (cloned
84
- * events). kyc_verified=true flagged on post-KYC events.
85
- * → Insights: "deposit" avg deposit_amount_usd, breakdown kyc_verified
86
- * (expect ~4x for verified users)
87
- * → Insights: "swap" count per user, breakdown kyc_verified
88
- *
89
- * 6. STAKE-TO-RETAIN (everything hook — retention)
90
- * Users who stake within first 14 days get 70% D60 retention
91
- * (events injected). Non-stakers lose events after day 60 to
92
- * simulate 15% retention.
93
- * → Retention: any event, segment staked_early=true vs false
94
- * (expect dramatic retention gap at D60)
95
- * → Insights: event count by day, breakdown staked_early
96
- *
97
- * 7. PRO TIER MAKER FEES (everything hook — subscription tier)
98
- * Pro users pay 0.05% maker fees (vs 0.30%). Pro users trade 6x
99
- * volume. pro_advantage=true on Pro swap events.
100
- * → Insights: "swap" avg maker_fee_pct, breakdown trading_tier
101
- * (expect Pro ~0.05, Standard ~0.30)
102
- * → Insights: "swap" total trade_amount_usd, breakdown pro_advantage
103
- *
104
- * 8. RUG-PULL AFTERMATH (everything hook — time-based churn)
105
- * Day 70, "SCAM" token rugs. Users who held SCAM (swapped it
106
- * before day 70) lose 80% of events after day 70. Flagged
107
- * rug_pull_victim=true.
108
- * → Retention: any event, segment rug_pull_victim=true vs others
109
- * (expect massive drop-off at day 70 for victims)
110
- * → Insights: any event total by day, filter rug_pull_victim=true
52
+ * NOTE: All cohort effects are HIDDEN no flag stamping. Discoverable
53
+ * via behavioral cohorts (count event per user, observe trade_amount
54
+ * distribution) or raw-prop breakdowns (token_pair, day, trading_tier).
111
55
  *
112
- * ===================================================================
113
- * ADVANCED ANALYSIS IDEAS
114
- * ===================================================================
56
+ * 1. WHALE WALLETS (everything)
57
+ *
58
+ * PATTERN: 2% of wallets (deterministic via id hash) get swap
59
+ * trade_amount_usd boosted 50x. No flag — analyst sees long-tail
60
+ * trade-amount distribution.
61
+ *
62
+ * HOW TO FIND IT IN MIXPANEL:
63
+ *
64
+ * Report 1: Trade Amount Distribution
65
+ * - Report type: Insights
66
+ * - Event: "swap"
67
+ * - Measure: Distribution of "trade_amount_usd"
68
+ * - Expected: heavy long tail; top 2% of users dominate volume
69
+ *
70
+ * Report 2: Volume Share by Top Traders Cohort
71
+ * - Cohort A: top 5% by total swap volume per user
72
+ * - Event: "swap"
73
+ * - Measure: Total of "trade_amount_usd"
74
+ * - Expected: cohort A drives ~ 60%+ of total volume
75
+ *
76
+ * REAL-WORLD ANALOGUE: Whale wallets dominate exchange volume.
77
+ *
78
+ * ---------------------------------------------------------------
79
+ * 2. GAS PRICE SPIKE (event + everything)
80
+ *
81
+ * PATTERN: Days 35-37, gas_fee_usd 10x on swap + transfer events.
82
+ * In window, 40% of swaps get swap_status flipped to "failed". No flag.
83
+ *
84
+ * HOW TO FIND IT IN MIXPANEL:
85
+ *
86
+ * Report 1: Avg Gas Fee Over Time
87
+ * - Report type: Insights
88
+ * - Event: "swap"
89
+ * - Measure: Average of "gas_fee_usd"
90
+ * - Line chart by day
91
+ * - Expected: spike days 35-37 (~10x baseline)
92
+ *
93
+ * Report 2: Swap Failure Rate Over Time
94
+ * - Report type: Insights
95
+ * - Event: "swap"
96
+ * - Measure: Total
97
+ * - Filter: swap_status = "failed"
98
+ * - Line chart by day
99
+ * - Expected: failure rate spikes during gas window
100
+ *
101
+ * REAL-WORLD ANALOGUE: Network congestion breaks transactions.
102
+ *
103
+ * ---------------------------------------------------------------
104
+ * 3. TOKEN LAUNCH SURGE (event + everything)
105
+ *
106
+ * PATTERN: Day 50+, 25% of swaps get token_pair flipped to a MOON
107
+ * pair. Each MOON swap clones 4 extra swap events with unique offset.
108
+ * No flag — discover via token_pair breakdown over time.
109
+ *
110
+ * HOW TO FIND IT IN MIXPANEL:
111
+ *
112
+ * Report 1: Swap Volume by Token Pair Over Time
113
+ * - Report type: Insights
114
+ * - Event: "swap"
115
+ * - Measure: Total
116
+ * - Filter: token_pair contains "MOON"
117
+ * - Line chart by day
118
+ * - Expected: appears at day 50, surges thereafter
119
+ *
120
+ * REAL-WORLD ANALOGUE: Meme/altcoin listings drive frenzied volume.
121
+ *
122
+ * ---------------------------------------------------------------
123
+ * 4. AIRDROP HUNTER CHURN (everything)
124
+ *
125
+ * PATTERN: 4% of users (deterministic via charCodeAt(1) % 25 === 0)
126
+ * are airdrop-farming bots. After their first "claim airdrop" event,
127
+ * 95% of subsequent events are removed.
128
+ *
129
+ * HOW TO FIND IT IN MIXPANEL:
130
+ *
131
+ * Report 1: Retention by Airdrop Claim
132
+ * - Report type: Retention
133
+ * - Event A: "claim airdrop"
134
+ * - Event B: any event
135
+ * - Expected: claimers retain dramatically worse than overall users
136
+ *
137
+ * Report 2: Airdrop Hunters vs Real Traders
138
+ * - Report type: Insights
139
+ * - Events: "claim airdrop" unique users vs "swap" unique users
140
+ * - Expected: large overlap gap — many claimers never swap again
141
+ *
142
+ * REAL-WORLD ANALOGUE: Airdrop farms are a well-known DeFi growth
143
+ * tax — bots claim free tokens and immediately abandon the platform.
144
+ *
145
+ * ---------------------------------------------------------------
146
+ * 5. KYC FUNNEL COMPLETION (everything)
147
+ *
148
+ * PATTERN: Users with kyc-completed get post-KYC deposit_amount_usd
149
+ * boosted 4x and 7 extra cloned swaps per existing post-KYC swap.
150
+ * No flag — discover via cohort builder.
151
+ *
152
+ * HOW TO FIND IT IN MIXPANEL:
153
+ *
154
+ * Report 1: Avg Deposit by KYC Cohort
155
+ * - Report type: Insights (with cohort)
156
+ * - Cohort A: users with >= 1 "kyc completed"
157
+ * - Cohort B: rest
158
+ * - Event: "deposit"
159
+ * - Measure: Average of "deposit_amount_usd"
160
+ * - Expected: A ~ 4x B
161
+ *
162
+ * REAL-WORLD ANALOGUE: KYC unlocks higher limits.
163
+ *
164
+ * ---------------------------------------------------------------
165
+ * 6. STAKE-TO-RETAIN (everything)
166
+ *
167
+ * PATTERN: Users who stake any token within first 14 days get extra
168
+ * cloned swap + portfolio events past day 60. Non-stakers lose 85% of
169
+ * post-day-60 events. No flag — discover via retention cohort.
170
+ *
171
+ * HOW TO FIND IT IN MIXPANEL:
172
+ *
173
+ * Report 1: Retention by Early Stake Cohort
174
+ * - Report type: Retention
175
+ * - Cohort A: users with >= 1 "stake" in first 14 days
176
+ * - Cohort B: rest
177
+ * - Expected: A ~ 70% D60 vs B ~ 15%
178
+ *
179
+ * REAL-WORLD ANALOGUE: Staking creates skin in the game.
180
+ *
181
+ * ---------------------------------------------------------------
182
+ * 7. PRO TIER MAKER FEES (everything)
183
+ *
184
+ * PATTERN: Pro-tier users (profile.trading_tier) get maker_fee_pct
185
+ * pinned to 0.05 (vs Standard 0.30) on swap events plus 5 extra
186
+ * cloned swaps per existing. Mutates raw prop. Discover via
187
+ * trading_tier breakdown on maker_fee_pct.
188
+ *
189
+ * HOW TO FIND IT IN MIXPANEL:
190
+ *
191
+ * Report 1: Avg Maker Fee by Tier
192
+ * - Report type: Insights
193
+ * - Event: "swap"
194
+ * - Measure: Average of "maker_fee_pct"
195
+ * - Breakdown: "trading_tier"
196
+ * - Expected: Pro ~ 0.05, Standard ~ 0.30
197
+ *
198
+ * REAL-WORLD ANALOGUE: Tiered fees retain volume traders.
199
+ *
200
+ * ---------------------------------------------------------------
201
+ * 8. RUG-PULL AFTERMATH (everything)
202
+ *
203
+ * PATTERN: ~2% of pre-day-70 swaps get token_pair flipped to a SCAM
204
+ * pair. Users who held SCAM lose 80% of post-day-70 events. No flag.
205
+ *
206
+ * HOW TO FIND IT IN MIXPANEL:
207
+ *
208
+ * Report 1: Retention by SCAM Holder Cohort
209
+ * - Report type: Retention
210
+ * - Cohort A: users with >= 1 swap where token_pair contains "SCAM"
211
+ * - Cohort B: rest
212
+ * - Expected: A retention drops sharply at day 70
213
+ *
214
+ * REAL-WORLD ANALOGUE: Rug-pulls collapse trust.
215
+ *
216
+ * ---------------------------------------------------------------
217
+ * 9. TRADING FUNNEL TIME-TO-CONVERT (funnel-post)
218
+ *
219
+ * PATTERN: Pro tier users complete deposit→swap→withdrawal funnel
220
+ * 1.4x faster than baseline (factor 0.7); Standard 1.3x slower
221
+ * (factor 1.3). Mutates funnel event timestamps.
222
+ *
223
+ * HOW TO FIND IT IN MIXPANEL:
224
+ *
225
+ * Report 1: Trading Funnel Median Time-to-Convert by Tier
226
+ * - Report type: Funnels
227
+ * - Steps: "deposit" -> "swap" -> "withdrawal"
228
+ * - Measure: Median time to convert
229
+ * - Breakdown: "trading_tier"
230
+ * - Expected: Pro ~ 0.7x; Standard ~ 1.3x
115
231
  *
116
- * Cross-hook patterns:
117
- * - Whale + MOON: Do whales drive the MOON token surge?
118
- * - Gas Spike + Rug Pull: Do SCAM holders panic-sell during gas spikes?
119
- * - KYC + Pro Tier: Do verified users upgrade to Pro faster?
120
- * - Stakers + Whales: Do whales stake more, or just trade?
121
- * - Airdrop Hunters + Rug Pull: Overlap between bots and victims?
232
+ * NOTE (funnel-post measurement): visible only via Mixpanel funnel
233
+ * median TTC. Cross-event MIN→MIN SQL queries on raw events do NOT
234
+ * show this funnel-post adjusts gaps within funnel instances, not
235
+ * across the user's full event history.
122
236
  *
123
- * Cohort analysis:
124
- * - By preferred_chain: Ethereum vs Solana engagement
125
- * - By wallet_type: MetaMask vs Phantom retention
126
- * - By trading_tier: Standard→Pro upgrade path
127
- * - By kyc_status: verified vs pending conversion rates
237
+ * REAL-WORLD ANALOGUE: Pro traders execute faster end-to-end.
238
+ *
239
+ * ---------------------------------------------------------------
240
+ * 10. SWAP-COUNT MAGIC NUMBER (everything)
241
+ *
242
+ * PATTERN: Users with 8-20 swaps in dataset get +30% on stake
243
+ * amount_usd plus 1-2 extra cloned stake events per existing.
244
+ * Users with 21+ swaps drop 40% of portfolio-viewed events
245
+ * (over-active traders ignore portfolio review). No flag.
246
+ *
247
+ * HOW TO FIND IT IN MIXPANEL:
248
+ *
249
+ * Report 1: Avg Stake Amount by Swap-Count Bucket
250
+ * - Report type: Insights (with cohort)
251
+ * - Cohort A: users with 8-20 "swap"
252
+ * - Cohort B: users with 0-7
253
+ * - Event: "stake"
254
+ * - Measure: Average of "amount_usd"
255
+ * - Expected: A ~ 1.3x B
256
+ *
257
+ * Report 2: Portfolio Views per User on Heavy Swappers
258
+ * - Report type: Insights (with cohort)
259
+ * - Cohort C: users with >= 21 "swap"
260
+ * - Cohort A: users with 8-20
261
+ * - Event: "portfolio viewed"
262
+ * - Measure: Total per user
263
+ * - Expected: C ~ 40% fewer portfolio views per user
264
+ *
265
+ * REAL-WORLD ANALOGUE: Engaged swappers grow stake; over-active
266
+ * day-traders ignore long-term portfolio review.
128
267
  *
129
268
  * ===================================================================
130
269
  * EXPECTED METRICS SUMMARY
@@ -132,14 +271,17 @@ const chance = u.initChance(SEED);
132
271
  *
133
272
  * Hook | Metric | Baseline | Effect | Ratio
134
273
  * ----------------------|-----------------------|----------|---------|------
135
- * Whale Wallets | Avg trade_amount_usd | $200 | $10,000 | 50x
136
- * Gas Price Spike | Avg gas_fee_usd | $5 | $50 | 10x
137
- * Token Launch Surge | Swap count post-D50 | 1x | 5x | 5x
138
- * Airdrop Hunter Churn | Post-airdrop events | 100% | 5% | 0.05x
139
- * KYC Completion | Avg deposit_amount_usd| $500 | $2,000 | 4x
140
- * Stake-to-Retain | D60 retention | 15% | 70% | ~5x
141
- * Pro Tier Fees | Avg maker_fee_pct | 0.30 | 0.05 | 6x
142
- * Rug-Pull Aftermath | Post-D70 events | 100% | 20% | 0.2x
274
+ * Whale Wallets | Top 5% trade share | 5% | ~ 60% | n/a
275
+ * Gas Price Spike | gas_fee_usd days 35-7 | 1x | 10x | 10x
276
+ * Token Launch Surge | MOON pair share post-D50 | 0% | ~ 25%+ | new
277
+ * Airdrop Hunter Churn | post-airdrop events | 1x | 0.05x | -95%
278
+ * KYC Completion | deposit_amount_usd | 1x | 4x | 4x
279
+ * Stake-to-Retain | D60 retention | 15% | 70% | ~ 5x
280
+ * Pro Tier Fees | maker_fee_pct | 0.30 | 0.05 | -83%
281
+ * Rug-Pull Aftermath | victim post-D70 | 1x | 0.2x | -80%
282
+ * Trading T2C | median min by tier | 1x | 0.7x/1.3x | 1.86x range
283
+ * Swap Magic Number | sweet stake amount | 1x | 1.3x | 1.3x
284
+ * Swap Magic Number | over portfolio/user | 1x | 0.6x | -40%
143
285
  */
144
286
 
145
287
  // Token pairs used in swap events
@@ -165,12 +307,16 @@ const NFT_COLLECTIONS = [
165
307
 
166
308
  /** @type {Config} */
167
309
  const config = {
310
+ version: 2,
168
311
  token,
169
312
  seed: SEED,
170
- numDays: num_days,
313
+ datasetStart: "2026-01-01T00:00:00Z",
314
+ datasetEnd: "2026-05-01T23:59:59Z",
315
+ // numDays: num_days,
171
316
  avgEventsPerUserPerDay: avg_events_per_user_per_day,
172
317
  numUsers: num_users,
173
- hasAnonIds: false,
318
+ hasAnonIds: true,
319
+ avgDevicePerUser: 2,
174
320
  hasSessionIds: true,
175
321
  format: "json",
176
322
  gzip: true,
@@ -225,6 +371,7 @@ const config = {
225
371
  event: "wallet connected",
226
372
  weight: 1,
227
373
  isFirstEvent: true,
374
+ isAuthEvent: true,
228
375
  properties: {
229
376
  wallet_type: ["MetaMask", "Phantom", "Coinbase Wallet", "Rainbow", "WalletConnect"],
230
377
  chain: ["Ethereum", "Solana", "Base", "Arbitrum", "Polygon"],
@@ -254,7 +401,6 @@ const config = {
254
401
  deposit_amount_usd: u.weighNumRange(10, 5000, 0.3, 500),
255
402
  chain: ["Ethereum", "Solana", "Base", "Arbitrum", "Polygon"],
256
403
  deposit_method: ["wallet_transfer", "bridge", "on_ramp", "exchange_transfer"],
257
- kyc_verified: [false],
258
404
  }
259
405
  },
260
406
  {
@@ -277,13 +423,6 @@ const config = {
277
423
  swap_status: ["completed", "completed", "completed", "completed", "pending", "failed"],
278
424
  slippage_pct: u.weighNumRange(0.01, 5, 0.3, 0.5),
279
425
  maker_fee_pct: [0.30],
280
- is_whale: [false],
281
- kyc_verified: [false],
282
- gas_spike: [false],
283
- pro_advantage: [false],
284
- rug_pull_victim: [false],
285
- moon_trade: [false],
286
- staked_early: [false],
287
426
  }
288
427
  },
289
428
  {
@@ -363,7 +502,6 @@ const config = {
363
502
  amount_usd: u.weighNumRange(10, 5000, 0.3, 300),
364
503
  gas_fee_usd: u.weighNumRange(0.5, 30, 0.3, 5),
365
504
  destination_type: ["wallet", "exchange", "contract", "ens_name"],
366
- gas_spike: [false],
367
505
  }
368
506
  },
369
507
  ],
@@ -372,7 +510,6 @@ const config = {
372
510
  trading_tier: ["Standard", "Standard", "Standard", "Pro"],
373
511
  preferred_chain: ["Ethereum", "Solana", "Base", "Arbitrum", "Polygon"],
374
512
  wallet_type: ["MetaMask", "Phantom", "Coinbase Wallet", "Rainbow", "WalletConnect"],
375
- staked_early: [false],
376
513
  },
377
514
 
378
515
  userProps: {
@@ -382,70 +519,40 @@ const config = {
382
519
  total_trade_volume: u.weighNumRange(0, 500000, 0.3, 5000),
383
520
  portfolio_value: u.weighNumRange(0, 200000, 0.3, 3000),
384
521
  kyc_status: ["pending"],
385
- has_staked: [false],
386
- staked_early: [false],
387
522
  },
388
523
 
389
524
  groupKeys: [],
390
525
  groupProps: {},
391
526
  lookupTables: [],
392
527
 
393
- /**
394
- * ARCHITECTED ANALYTICS HOOKS
395
- *
396
- * This hook function creates 8 deliberate patterns in the data:
397
- *
398
- * 1. WHALE WALLETS: 2% of users (deterministic) trade 50x volume, is_whale=true
399
- * 2. GAS PRICE SPIKE: Days 35-37 gas fees 10x, 40% swaps fail, gas_spike=true
400
- * 3. TOKEN LAUNCH SURGE: Day 50+ MOON token, 25% swaps become MOON, 5x volume for MOON traders
401
- * 4. AIRDROP HUNTER CHURN: Airdrop claimers with no swaps lose 95% of events post-claim
402
- * 5. KYC COMPLETION: Verified users deposit 4x more, swap 8x more, kyc_verified=true
403
- * 6. STAKE-TO-RETAIN: Early stakers (first 14 days) get 70% D60 retention vs 15%
404
- * 7. PRO TIER FEES: Pro users pay 0.05% fees (vs 0.30%), trade 6x volume, pro_advantage=true
405
- * 8. RUG-PULL AFTERMATH: Day 70 SCAM token rugs, holders lose 80% of post-D70 events
406
- */
407
528
  hook: function (record, type, meta) {
408
- const NOW = dayjs();
409
- const DATASET_START = NOW.subtract(num_days, "days");
410
-
411
- // ===============================================================
412
- // Hook #2 (partial): GAS PRICE SPIKE (event)
413
- // Days 35-37, network congestion. Gas fees 10x on swap/transfer
414
- // events and gas_spike=true.
415
- // ===============================================================
416
- if (type === "event") {
417
- const EVENT_TIME = dayjs(record.time);
418
- const dayInDataset = EVENT_TIME.diff(DATASET_START, "day");
419
-
420
- // Gas price spike: days 35-37
421
- if (dayInDataset >= 35 && dayInDataset <= 37) {
422
- if (record.event === "swap") {
423
- record.gas_fee_usd = Math.round((record.gas_fee_usd || 5) * 10);
424
- record.gas_spike = true;
425
- }
426
- if (record.event === "transfer") {
427
- record.gas_fee_usd = Math.round((record.gas_fee_usd || 5) * 10);
428
- record.gas_spike = true;
429
- }
430
- }
431
-
432
- // ===============================================================
433
- // Hook #3 (partial): TOKEN LAUNCH SURGE (event)
434
- // After day 50, 25% of swap events get token_pair changed to
435
- // include "MOON". Tagged moon_trade=true.
436
- // ===============================================================
437
- if (record.event === "swap" && dayInDataset >= 50) {
438
- if (chance.bool({ likelihood: 25 })) {
439
- record.token_pair = chance.pickone(["MOON/USDC", "MOON/ETH", "ETH/MOON"]);
440
- record.moon_trade = true;
529
+ // HOOKS 2 + 3: GAS PRICE SPIKE and TOKEN LAUNCH SURGE moved to everything
530
+ // hook below — event hook fires before bunchIntoSessions reshuffles timestamps,
531
+ // causing day-window tags to leak across the boundary.
532
+
533
+ // HOOK 9 (T2C): TRADING FUNNEL TIME-TO-CONVERT (funnel-post)
534
+ // Pro tier users complete deposit→swap→withdrawal funnel 1.4x faster
535
+ // (factor 0.7 on inter-event gaps); Standard 1.3x slower (factor 1.3).
536
+ if (type === "funnel-post") {
537
+ const segment = meta?.profile?.trading_tier;
538
+ if (Array.isArray(record) && record.length > 1) {
539
+ const factor = (
540
+ segment === "Pro" ? 0.7 :
541
+ segment === "Standard" ? 1.3 :
542
+ 1.0
543
+ );
544
+ if (factor !== 1.0) {
545
+ for (let i = 1; i < record.length; i++) {
546
+ const prev = dayjs(record[i - 1].time);
547
+ const newGap = Math.round(dayjs(record[i].time).diff(prev) * factor);
548
+ record[i].time = prev.add(newGap, "milliseconds").toISOString();
549
+ }
441
550
  }
442
551
  }
443
552
  }
444
553
 
445
- // ===============================================================
446
- // EVERYTHING HOOK — all complex behavioral patterns
447
- // ===============================================================
448
554
  if (type === "everything") {
555
+ const datasetStart = dayjs.unix(meta.datasetStart);
449
556
  const userEvents = record;
450
557
  const profile = meta.profile;
451
558
 
@@ -454,17 +561,12 @@ const config = {
454
561
  e.trading_tier = profile.trading_tier;
455
562
  e.preferred_chain = profile.preferred_chain;
456
563
  e.wallet_type = profile.wallet_type;
457
- e.staked_early = profile.staked_early;
458
564
  });
459
565
 
460
566
  const firstEventTime = userEvents.length > 0 ? dayjs(userEvents[0].time) : null;
461
567
 
462
- // -----------------------------------------------------------
463
- // Hook #1: WHALE WALLETS
464
- // 2% of wallets do massive volume. Deterministic selection via
465
- // charCodeAt(0) % 50 === 0. Whales get 50x trade_amount_usd
466
- // on all swap events with is_whale=true.
467
- // -----------------------------------------------------------
568
+ // HOOK 1: WHALE WALLETS — 2% of users (charCodeAt(0) % 50 === 0)
569
+ // get swap trade_amount_usd boosted 50x. No flag.
468
570
  const userId = userEvents.length > 0 ? (userEvents[0].user_id || userEvents[0].distinct_id || "") : "";
469
571
  const isWhale = userId.length > 0 && userId.charCodeAt(0) % 50 === 0;
470
572
 
@@ -472,30 +574,27 @@ const config = {
472
574
  userEvents.forEach(e => {
473
575
  if (e.event === "swap") {
474
576
  e.trade_amount_usd = Math.round((e.trade_amount_usd || 200) * 50);
475
- e.is_whale = true;
476
577
  }
477
578
  });
478
579
  }
479
580
 
480
- // -----------------------------------------------------------
481
- // Hook #2 (continued): GAS PRICE SPIKE — failed swaps
482
- // During days 35-37, 40% of swaps get swap_status changed to
483
- // "failed".
484
- // -----------------------------------------------------------
581
+ // HOOK 2 moved to end (after all cloning) for full coverage
582
+
583
+ // HOOK 3: TOKEN LAUNCH SURGE — after d50, 25% of swaps flip token_pair to MOON.
584
+ const day50 = datasetStart.add(50, "days");
485
585
  userEvents.forEach(e => {
486
- if (e.event === "swap" && e.gas_spike === true) {
487
- if (chance.bool({ likelihood: 40 })) {
488
- e.swap_status = "failed";
489
- }
586
+ if (e.event === "swap" && dayjs(e.time).isAfter(day50) && chance.bool({ likelihood: 25 })) {
587
+ e.token_pair = chance.pickone(["MOON/USDC", "MOON/ETH", "ETH/MOON"]);
490
588
  }
491
589
  });
492
590
 
493
- // -----------------------------------------------------------
494
- // Hook #3 (continued): TOKEN LAUNCH SURGE volume boost
495
- // Users who traded MOON get 5x swap event volume via cloned
496
- // events spread over days after day 50.
497
- // -----------------------------------------------------------
498
- const moonSwaps = userEvents.filter(e => e.event === "swap" && e.moon_trade === true);
591
+ // HOOK 3 (cont): TOKEN LAUNCH SURGE — clone 4 extra MOON-pair
592
+ // swap events per existing MOON swap. Cloned with unique offset.
593
+ const moonSwaps = userEvents.filter(e =>
594
+ e.event === "swap" &&
595
+ typeof e.token_pair === "string" &&
596
+ e.token_pair.includes("MOON")
597
+ );
499
598
  if (moonSwaps.length > 0) {
500
599
  const clonedMoonEvents = [];
501
600
  moonSwaps.forEach(moonEvt => {
@@ -514,14 +613,16 @@ const config = {
514
613
 
515
614
  // -----------------------------------------------------------
516
615
  // Hook #4: AIRDROP HUNTER CHURN
517
- // Users who have "claim airdrop" events but zero "swap"
518
- // events are bots/hunters. Remove 95% of events after the
519
- // first airdrop claim.
616
+ // 4% of users are deterministically tagged as airdrop-farming bots
617
+ // via charCodeAt(1) % 25 === 0. Bots that have ever claimed an airdrop
618
+ // lose 95% of events after their first claim. (Original cohort
619
+ // "claimed-but-never-swapped" was empty because every claimer also
620
+ // has organic swap events at this scale.)
520
621
  // -----------------------------------------------------------
622
+ const isAirdropBot = userId.length > 1 && userId.charCodeAt(1) % 25 === 0;
521
623
  const hasAirdropClaim = userEvents.some(e => e.event === "claim airdrop");
522
- const hasSwap = userEvents.some(e => e.event === "swap");
523
624
 
524
- if (hasAirdropClaim && !hasSwap) {
625
+ if (isAirdropBot && hasAirdropClaim) {
525
626
  const firstClaim = userEvents.find(e => e.event === "claim airdrop");
526
627
  if (firstClaim) {
527
628
  const claimTime = dayjs(firstClaim.time);
@@ -536,31 +637,22 @@ const config = {
536
637
  }
537
638
  }
538
639
 
539
- // -----------------------------------------------------------
540
- // Hook #5: KYC FUNNEL COMPLETION
541
- // Users with "kyc completed" events get 4x deposit_amount_usd
542
- // and 8x more swap events (cloned). Flag kyc_verified=true on
543
- // post-KYC events.
544
- // -----------------------------------------------------------
640
+ // HOOK 5: KYC FUNNEL COMPLETION — users with kyc-completed
641
+ // get post-KYC deposit_amount_usd boosted 4x and 7 extra cloned
642
+ // swaps per existing post-KYC swap. No flag.
545
643
  const kycCompleted = userEvents.find(e => e.event === "kyc completed");
546
644
  if (kycCompleted) {
547
645
  const kycTime = dayjs(kycCompleted.time);
548
646
 
549
- // Boost deposits and flag post-KYC events
550
647
  userEvents.forEach(e => {
551
- if (dayjs(e.time).isAfter(kycTime)) {
552
- if (e.event === "deposit") {
553
- e.deposit_amount_usd = Math.round((e.deposit_amount_usd || 500) * 4);
554
- e.kyc_verified = true;
555
- }
556
- if (e.event === "swap") {
557
- e.kyc_verified = true;
558
- }
648
+ if (dayjs(e.time).isAfter(kycTime) && e.event === "deposit") {
649
+ e.deposit_amount_usd = Math.round((e.deposit_amount_usd || 500) * 4);
559
650
  }
560
651
  });
561
652
 
562
- // Clone swap events for 8x volume
563
- const postKycSwaps = userEvents.filter(e => e.event === "swap" && e.kyc_verified === true);
653
+ const postKycSwaps = userEvents.filter(e =>
654
+ e.event === "swap" && dayjs(e.time).isAfter(kycTime)
655
+ );
564
656
  const clonedKycSwaps = [];
565
657
  postKycSwaps.forEach(swapEvt => {
566
658
  const swapTime = dayjs(swapEvt.time);
@@ -570,7 +662,6 @@ const config = {
570
662
  time: swapTime.add(chance.integer({ min: 1, max: 48 }), "hours").toISOString(),
571
663
  user_id: swapEvt.user_id,
572
664
  trade_amount_usd: Math.round((swapEvt.trade_amount_usd || 200) * chance.floating({ min: 0.8, max: 1.5 })),
573
- kyc_verified: true,
574
665
  });
575
666
  }
576
667
  });
@@ -583,19 +674,15 @@ const config = {
583
674
  // dataset have 70% D60 retention (events injected beyond D60).
584
675
  // Non-stakers lose events after day 60 to simulate 15% retention.
585
676
  // -----------------------------------------------------------
586
- const day14 = DATASET_START.add(14, "days");
587
- const day60 = DATASET_START.add(60, "days");
677
+ const day14 = datasetStart.add(14, "days");
678
+ const day60 = datasetStart.add(60, "days");
588
679
 
589
680
  const stakedEarly = userEvents.some(e =>
590
681
  e.event === "stake" && dayjs(e.time).isBefore(day14)
591
682
  );
592
683
 
593
684
  if (stakedEarly) {
594
- // Mark all events for this user
595
- userEvents.forEach(e => { e.staked_early = true; });
596
-
597
- // 70% retention: inject late-stage events for stakers
598
- // (only inject if they don't already have many post-D60 events)
685
+ // 70% retention: inject late-stage events for stakers (no flag)
599
686
  const postD60Events = userEvents.filter(e => dayjs(e.time).isAfter(day60));
600
687
  if (postD60Events.length < 5) {
601
688
  const swapTemplate = userEvents.find(e => e.event === "swap");
@@ -607,7 +694,6 @@ const config = {
607
694
  time: day60.add(chance.integer({ min: 1, max: 55 }), "days").toISOString(),
608
695
  user_id: swapTemplate.user_id,
609
696
  trade_amount_usd: Math.round((swapTemplate.trade_amount_usd || 200) * chance.floating({ min: 0.5, max: 2.0 })),
610
- staked_early: true,
611
697
  });
612
698
  }
613
699
  }
@@ -617,7 +703,6 @@ const config = {
617
703
  ...portfolioTemplate,
618
704
  time: day60.add(chance.integer({ min: 1, max: 55 }), "days").toISOString(),
619
705
  user_id: portfolioTemplate.user_id,
620
- staked_early: true,
621
706
  });
622
707
  }
623
708
  }
@@ -634,21 +719,14 @@ const config = {
634
719
  }
635
720
  }
636
721
 
637
- // -----------------------------------------------------------
638
- // Hook #7: PRO TIER MAKER FEES
639
- // Pro users (from profile.trading_tier) pay maker_fee_pct=0.05
640
- // vs standard 0.30. Pro users also trade 6x volume.
641
- // pro_advantage=true on Pro swap events.
642
- // -----------------------------------------------------------
722
+ // HOOK 7: PRO TIER MAKER FEES — Pro users pay maker_fee_pct=0.05
723
+ // (vs Standard 0.30) and get 5 extra cloned swaps. Mutates raw
724
+ // maker_fee_pct prop. Reads trading_tier from profile.
643
725
  if (profile.trading_tier === "Pro") {
644
726
  userEvents.forEach(e => {
645
- if (e.event === "swap") {
646
- e.maker_fee_pct = 0.05;
647
- e.pro_advantage = true;
648
- }
727
+ if (e.event === "swap") e.maker_fee_pct = 0.05;
649
728
  });
650
729
 
651
- // Clone swap events for 6x volume
652
730
  const proSwaps = userEvents.filter(e => e.event === "swap");
653
731
  const clonedProSwaps = [];
654
732
  proSwaps.forEach(swapEvt => {
@@ -660,37 +738,29 @@ const config = {
660
738
  user_id: swapEvt.user_id,
661
739
  trade_amount_usd: Math.round((swapEvt.trade_amount_usd || 200) * chance.floating({ min: 0.5, max: 3.0 })),
662
740
  maker_fee_pct: 0.05,
663
- pro_advantage: true,
664
741
  });
665
742
  }
666
743
  });
667
744
  userEvents.push(...clonedProSwaps);
668
745
  } else {
669
- // Ensure standard fee on non-Pro swap events
670
746
  userEvents.forEach(e => {
671
- if (e.event === "swap") {
672
- e.maker_fee_pct = 0.30;
673
- }
747
+ if (e.event === "swap") e.maker_fee_pct = 0.30;
674
748
  });
675
749
  }
676
750
 
677
751
  // -----------------------------------------------------------
678
752
  // Hook #8: RUG-PULL AFTERMATH
679
753
  // Day 70, "SCAM" token rugs. Users who swapped SCAM token
680
- // before day 70 lose 80% of events after day 70.
681
- // rug_pull_victim=true on remaining events.
754
+ // before day 70 lose 80% of events after day 70. No flag —
755
+ // derive cohort by detecting users with token_pair containing "SCAM".
682
756
  // -----------------------------------------------------------
683
- const day70 = DATASET_START.add(70, "days");
757
+ const day70 = datasetStart.add(70, "days");
684
758
 
685
- // Check if user traded SCAM before day 70
686
- // ~10% of pre-day-70 swaps naturally get SCAM token pair
687
- // (injected here since SCAM isn't in the default TOKEN_PAIRS list)
688
759
  let hadScam = false;
689
760
  userEvents.forEach(e => {
690
761
  if (e.event === "swap") {
691
- const swapDay = dayjs(e.time).diff(DATASET_START, "day");
692
- // Before day 70, ~8% of swaps randomly become SCAM trades
693
- if (swapDay >= 10 && swapDay < 70 && chance.bool({ likelihood: 8 })) {
762
+ const swapDay = dayjs(e.time).diff(datasetStart, "day");
763
+ if (swapDay >= 10 && swapDay < 70 && chance.bool({ likelihood: 2 })) {
694
764
  e.token_pair = chance.pickone(["SCAM/USDC", "SCAM/ETH", "ETH/SCAM"]);
695
765
  hadScam = true;
696
766
  }
@@ -700,17 +770,57 @@ const config = {
700
770
  if (hadScam) {
701
771
  for (let i = userEvents.length - 1; i >= 0; i--) {
702
772
  const evt = userEvents[i];
703
- if (dayjs(evt.time).isAfter(day70)) {
704
- if (chance.bool({ likelihood: 80 })) {
705
- userEvents.splice(i, 1);
706
- } else {
707
- evt.rug_pull_victim = true;
773
+ if (dayjs(evt.time).isAfter(day70) && chance.bool({ likelihood: 80 })) {
774
+ userEvents.splice(i, 1);
775
+ }
776
+ }
777
+ }
778
+
779
+ // HOOK 10: SWAP-COUNT MAGIC NUMBER (no flags)
780
+ // Sweet 8-20 swaps → +30% on stake amount_usd; clone 1-2 extra
781
+ // stake events per existing. Over 21+ → drop 40% of portfolio
782
+ // viewed events (over-active traders ignore their portfolio).
783
+ const swapCount = userEvents.filter(e => e.event === "swap").length;
784
+ if (swapCount >= 8 && swapCount <= 20) {
785
+ const stakeTemplate = userEvents.find(e => e.event === "stake");
786
+ if (stakeTemplate) {
787
+ const stakes = userEvents.filter(e => e.event === "stake");
788
+ stakes.forEach(s => {
789
+ if (typeof s.amount_usd === "number") s.amount_usd = Math.round(s.amount_usd * 1.3);
790
+ const extras = chance.integer({ min: 1, max: 2 });
791
+ for (let k = 0; k < extras; k++) {
792
+ userEvents.push({
793
+ ...s,
794
+ time: dayjs(s.time).add(chance.integer({ min: 5, max: 360 }), "minutes").toISOString(),
795
+ user_id: s.user_id,
796
+ });
708
797
  }
798
+ });
799
+ }
800
+ } else if (swapCount >= 21) {
801
+ for (let i = userEvents.length - 1; i >= 0; i--) {
802
+ if (userEvents[i].event === "portfolio viewed" && chance.bool({ likelihood: 40 })) {
803
+ userEvents.splice(i, 1);
709
804
  }
710
805
  }
711
806
  }
712
807
 
713
- // Sort by time after all mutations
808
+ // HOOK 2: GAS PRICE SPIKE — days 35-37 swap+transfer gas_fee_usd 10x,
809
+ // 40% of swaps get swap_status=failed. Runs AFTER all cloning to
810
+ // catch events that land in the spike window from clone offsets.
811
+ const day35 = datasetStart.add(35, "days");
812
+ const day38 = datasetStart.add(38, "days");
813
+ userEvents.forEach(e => {
814
+ if (e.event !== "swap" && e.event !== "transfer") return;
815
+ const t = dayjs(e.time);
816
+ if (t.isAfter(day35) && t.isBefore(day38)) {
817
+ e.gas_fee_usd = Math.round((e.gas_fee_usd || 5) * 10);
818
+ if (e.event === "swap" && chance.bool({ likelihood: 40 })) {
819
+ e.swap_status = "failed";
820
+ }
821
+ }
822
+ });
823
+
714
824
  userEvents.sort((a, b) => new Date(a.time) - new Date(b.time));
715
825
  }
716
826