@ak--47/dungeon-master 1.3.0 → 1.3.1
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.
- package/dungeons/technical/array-of-object-lookup.js +0 -2
- package/dungeons/technical/simple.js +3 -4
- package/dungeons/technical/simplest-schema.json +5 -0
- package/dungeons/technical/text-generation.js +1 -1
- package/dungeons/vertical/ai-platform.js +192 -133
- package/dungeons/vertical/community.js +64 -34
- package/dungeons/vertical/crypto.js +333 -224
- package/dungeons/vertical/dating.js +228 -282
- package/dungeons/vertical/devtools.js +164 -62
- package/dungeons/vertical/ecommerce.js +240 -91
- package/dungeons/vertical/education.js +328 -310
- package/dungeons/vertical/fintech.js +437 -311
- package/dungeons/vertical/fitness.js +141 -58
- package/dungeons/vertical/food-delivery.js +298 -318
- package/dungeons/vertical/gaming.js +378 -231
- package/dungeons/vertical/healthcare.js +140 -60
- package/dungeons/vertical/insurance-application.js +168 -73
- package/dungeons/vertical/logistics.js +95 -6
- package/dungeons/vertical/marketplace.js +137 -51
- package/dungeons/vertical/media.js +241 -386
- package/dungeons/vertical/real-estate.js +322 -315
- package/dungeons/vertical/sass.js +253 -263
- package/dungeons/vertical/social.js +262 -206
- package/dungeons/vertical/travel.js +59 -16
- package/index.js +17 -17
- package/lib/core/config-validator.js +93 -21
- package/lib/core/context.js +10 -24
- package/lib/core/storage.js +6 -1
- package/lib/generators/events.js +11 -14
- package/lib/generators/funnels.js +12 -4
- package/lib/generators/mirror.js +3 -2
- package/lib/generators/product-names.js +1 -1
- package/lib/generators/scd.js +2 -1
- package/lib/generators/text.js +1 -1
- package/lib/orchestrators/user-loop.js +47 -47
- package/lib/utils/utils.js +71 -39
- package/package.json +1 -1
- package/scripts/smoke-test-all.mjs +162 -0
- package/scripts/verify-runner.mjs +72 -24
- package/types.d.ts +38 -15
- package/dungeons/technical/ad-spend-schema.json +0 -128
- package/dungeons/technical/anonymous-users-schema.json +0 -92
- package/dungeons/technical/array-of-object-lookup-schema.json +0 -191
- package/dungeons/technical/experiments-schema.json +0 -203
- package/dungeons/technical/foobar-schema.json +0 -362
- package/dungeons/technical/group-analytics-schema.json +0 -241
- package/dungeons/technical/mirror-strategies-schema.json +0 -84
- package/dungeons/technical/nested-objects-schema.json +0 -145
- package/dungeons/technical/retention-cadence-schema.json +0 -37
- package/dungeons/technical/sanity-schema.json +0 -185
- package/dungeons/technical/scale-test-schema.json +0 -70
- package/dungeons/technical/scd-schema.json +0 -467
- package/dungeons/technical/simple-schema.json +0 -362
- package/dungeons/technical/text-generation-schema.json +0 -1062
- package/dungeons/vertical/ai-platform-schema.json +0 -617
- package/dungeons/vertical/community-schema.json +0 -579
- package/dungeons/vertical/crypto-schema.json +0 -546
- package/dungeons/vertical/dating-schema.json +0 -401
- package/dungeons/vertical/devtools-schema.json +0 -601
- package/dungeons/vertical/ecommerce-schema.json +0 -604
- package/dungeons/vertical/education-schema.json +0 -5686
- package/dungeons/vertical/fintech-schema.json +0 -630
- package/dungeons/vertical/fitness-schema.json +0 -530
- package/dungeons/vertical/food-delivery-schema.json +0 -36728
- package/dungeons/vertical/gaming-schema.json +0 -2703
- package/dungeons/vertical/healthcare-schema.json +0 -549
- package/dungeons/vertical/insurance-application-schema.json +0 -485
- package/dungeons/vertical/logistics-schema.json +0 -574
- package/dungeons/vertical/marketplace-schema.json +0 -533
- package/dungeons/vertical/media-schema.json +0 -4749
- package/dungeons/vertical/real-estate-schema.json +0 -527
- package/dungeons/vertical/sass-schema.json +0 -3128
- package/dungeons/vertical/social-schema.json +0 -620
- package/dungeons/vertical/travel-schema.json +0 -580
|
@@ -46,85 +46,224 @@ const chance = u.initChance(SEED);
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* ===================================================================
|
|
49
|
-
* ANALYTICS HOOKS (
|
|
49
|
+
* ANALYTICS HOOKS (10 hooks)
|
|
50
50
|
* ===================================================================
|
|
51
51
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
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
|
-
*
|
|
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: ~8% 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)
|
|
115
218
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
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?
|
|
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.
|
|
122
222
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* -
|
|
127
|
-
* -
|
|
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
|
|
231
|
+
*
|
|
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.
|
|
236
|
+
*
|
|
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 |
|
|
136
|
-
* Gas Price Spike |
|
|
137
|
-
* Token Launch Surge |
|
|
138
|
-
* Airdrop Hunter Churn |
|
|
139
|
-
* KYC Completion |
|
|
140
|
-
* Stake-to-Retain | D60 retention | 15% | 70% | ~5x
|
|
141
|
-
* Pro Tier Fees |
|
|
142
|
-
* Rug-Pull Aftermath |
|
|
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
|
|
@@ -167,7 +309,9 @@ const NFT_COLLECTIONS = [
|
|
|
167
309
|
const config = {
|
|
168
310
|
token,
|
|
169
311
|
seed: SEED,
|
|
170
|
-
|
|
312
|
+
datasetStart: "2026-01-01T00:00:00Z",
|
|
313
|
+
datasetEnd: "2026-04-28T23:59:59Z",
|
|
314
|
+
// numDays: num_days,
|
|
171
315
|
avgEventsPerUserPerDay: avg_events_per_user_per_day,
|
|
172
316
|
numUsers: num_users,
|
|
173
317
|
hasAnonIds: false,
|
|
@@ -254,7 +398,6 @@ const config = {
|
|
|
254
398
|
deposit_amount_usd: u.weighNumRange(10, 5000, 0.3, 500),
|
|
255
399
|
chain: ["Ethereum", "Solana", "Base", "Arbitrum", "Polygon"],
|
|
256
400
|
deposit_method: ["wallet_transfer", "bridge", "on_ramp", "exchange_transfer"],
|
|
257
|
-
kyc_verified: [false],
|
|
258
401
|
}
|
|
259
402
|
},
|
|
260
403
|
{
|
|
@@ -277,13 +420,6 @@ const config = {
|
|
|
277
420
|
swap_status: ["completed", "completed", "completed", "completed", "pending", "failed"],
|
|
278
421
|
slippage_pct: u.weighNumRange(0.01, 5, 0.3, 0.5),
|
|
279
422
|
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
423
|
}
|
|
288
424
|
},
|
|
289
425
|
{
|
|
@@ -363,7 +499,6 @@ const config = {
|
|
|
363
499
|
amount_usd: u.weighNumRange(10, 5000, 0.3, 300),
|
|
364
500
|
gas_fee_usd: u.weighNumRange(0.5, 30, 0.3, 5),
|
|
365
501
|
destination_type: ["wallet", "exchange", "contract", "ens_name"],
|
|
366
|
-
gas_spike: [false],
|
|
367
502
|
}
|
|
368
503
|
},
|
|
369
504
|
],
|
|
@@ -372,7 +507,6 @@ const config = {
|
|
|
372
507
|
trading_tier: ["Standard", "Standard", "Standard", "Pro"],
|
|
373
508
|
preferred_chain: ["Ethereum", "Solana", "Base", "Arbitrum", "Polygon"],
|
|
374
509
|
wallet_type: ["MetaMask", "Phantom", "Coinbase Wallet", "Rainbow", "WalletConnect"],
|
|
375
|
-
staked_early: [false],
|
|
376
510
|
},
|
|
377
511
|
|
|
378
512
|
userProps: {
|
|
@@ -382,70 +516,40 @@ const config = {
|
|
|
382
516
|
total_trade_volume: u.weighNumRange(0, 500000, 0.3, 5000),
|
|
383
517
|
portfolio_value: u.weighNumRange(0, 200000, 0.3, 3000),
|
|
384
518
|
kyc_status: ["pending"],
|
|
385
|
-
has_staked: [false],
|
|
386
|
-
staked_early: [false],
|
|
387
519
|
},
|
|
388
520
|
|
|
389
521
|
groupKeys: [],
|
|
390
522
|
groupProps: {},
|
|
391
523
|
lookupTables: [],
|
|
392
524
|
|
|
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
525
|
hook: function (record, type, meta) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
//
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
record.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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;
|
|
526
|
+
// HOOKS 2 + 3: GAS PRICE SPIKE and TOKEN LAUNCH SURGE moved to everything
|
|
527
|
+
// hook below — event hook fires before bunchIntoSessions reshuffles timestamps,
|
|
528
|
+
// causing day-window tags to leak across the boundary.
|
|
529
|
+
|
|
530
|
+
// HOOK 9 (T2C): TRADING FUNNEL TIME-TO-CONVERT (funnel-post)
|
|
531
|
+
// Pro tier users complete deposit→swap→withdrawal funnel 1.4x faster
|
|
532
|
+
// (factor 0.7 on inter-event gaps); Standard 1.3x slower (factor 1.3).
|
|
533
|
+
if (type === "funnel-post") {
|
|
534
|
+
const segment = meta?.profile?.trading_tier;
|
|
535
|
+
if (Array.isArray(record) && record.length > 1) {
|
|
536
|
+
const factor = (
|
|
537
|
+
segment === "Pro" ? 0.7 :
|
|
538
|
+
segment === "Standard" ? 1.3 :
|
|
539
|
+
1.0
|
|
540
|
+
);
|
|
541
|
+
if (factor !== 1.0) {
|
|
542
|
+
for (let i = 1; i < record.length; i++) {
|
|
543
|
+
const prev = dayjs(record[i - 1].time);
|
|
544
|
+
const newGap = Math.round(dayjs(record[i].time).diff(prev) * factor);
|
|
545
|
+
record[i].time = prev.add(newGap, "milliseconds").toISOString();
|
|
546
|
+
}
|
|
441
547
|
}
|
|
442
548
|
}
|
|
443
549
|
}
|
|
444
550
|
|
|
445
|
-
// ===============================================================
|
|
446
|
-
// EVERYTHING HOOK — all complex behavioral patterns
|
|
447
|
-
// ===============================================================
|
|
448
551
|
if (type === "everything") {
|
|
552
|
+
const datasetStart = dayjs.unix(meta.datasetStart);
|
|
449
553
|
const userEvents = record;
|
|
450
554
|
const profile = meta.profile;
|
|
451
555
|
|
|
@@ -454,17 +558,12 @@ const config = {
|
|
|
454
558
|
e.trading_tier = profile.trading_tier;
|
|
455
559
|
e.preferred_chain = profile.preferred_chain;
|
|
456
560
|
e.wallet_type = profile.wallet_type;
|
|
457
|
-
e.staked_early = profile.staked_early;
|
|
458
561
|
});
|
|
459
562
|
|
|
460
563
|
const firstEventTime = userEvents.length > 0 ? dayjs(userEvents[0].time) : null;
|
|
461
564
|
|
|
462
|
-
//
|
|
463
|
-
//
|
|
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
|
-
// -----------------------------------------------------------
|
|
565
|
+
// HOOK 1: WHALE WALLETS — 2% of users (charCodeAt(0) % 50 === 0)
|
|
566
|
+
// get swap trade_amount_usd boosted 50x. No flag.
|
|
468
567
|
const userId = userEvents.length > 0 ? (userEvents[0].user_id || userEvents[0].distinct_id || "") : "";
|
|
469
568
|
const isWhale = userId.length > 0 && userId.charCodeAt(0) % 50 === 0;
|
|
470
569
|
|
|
@@ -472,30 +571,41 @@ const config = {
|
|
|
472
571
|
userEvents.forEach(e => {
|
|
473
572
|
if (e.event === "swap") {
|
|
474
573
|
e.trade_amount_usd = Math.round((e.trade_amount_usd || 200) * 50);
|
|
475
|
-
e.is_whale = true;
|
|
476
574
|
}
|
|
477
575
|
});
|
|
478
576
|
}
|
|
479
577
|
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
|
|
484
|
-
|
|
578
|
+
// HOOK 2: GAS PRICE SPIKE — days 35-37 swap+transfer gas_fee_usd 10x,
|
|
579
|
+
// 40% of swaps get swap_status=failed. Runs in everything hook so
|
|
580
|
+
// timestamp checks see post-bunchIntoSessions times.
|
|
581
|
+
const day35 = datasetStart.add(35, "days");
|
|
582
|
+
const day38 = datasetStart.add(38, "days");
|
|
485
583
|
userEvents.forEach(e => {
|
|
486
|
-
if (e.event
|
|
487
|
-
|
|
584
|
+
if (e.event !== "swap" && e.event !== "transfer") return;
|
|
585
|
+
const t = dayjs(e.time);
|
|
586
|
+
if (t.isAfter(day35) && t.isBefore(day38)) {
|
|
587
|
+
e.gas_fee_usd = Math.round((e.gas_fee_usd || 5) * 10);
|
|
588
|
+
if (e.event === "swap" && chance.bool({ likelihood: 40 })) {
|
|
488
589
|
e.swap_status = "failed";
|
|
489
590
|
}
|
|
490
591
|
}
|
|
491
592
|
});
|
|
492
593
|
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
594
|
+
// HOOK 3: TOKEN LAUNCH SURGE — after d50, 25% of swaps flip token_pair to MOON.
|
|
595
|
+
const day50 = datasetStart.add(50, "days");
|
|
596
|
+
userEvents.forEach(e => {
|
|
597
|
+
if (e.event === "swap" && dayjs(e.time).isAfter(day50) && chance.bool({ likelihood: 25 })) {
|
|
598
|
+
e.token_pair = chance.pickone(["MOON/USDC", "MOON/ETH", "ETH/MOON"]);
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// HOOK 3 (cont): TOKEN LAUNCH SURGE — clone 4 extra MOON-pair
|
|
603
|
+
// swap events per existing MOON swap. Cloned with unique offset.
|
|
604
|
+
const moonSwaps = userEvents.filter(e =>
|
|
605
|
+
e.event === "swap" &&
|
|
606
|
+
typeof e.token_pair === "string" &&
|
|
607
|
+
e.token_pair.includes("MOON")
|
|
608
|
+
);
|
|
499
609
|
if (moonSwaps.length > 0) {
|
|
500
610
|
const clonedMoonEvents = [];
|
|
501
611
|
moonSwaps.forEach(moonEvt => {
|
|
@@ -514,14 +624,16 @@ const config = {
|
|
|
514
624
|
|
|
515
625
|
// -----------------------------------------------------------
|
|
516
626
|
// Hook #4: AIRDROP HUNTER CHURN
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
// first
|
|
627
|
+
// 4% of users are deterministically tagged as airdrop-farming bots
|
|
628
|
+
// via charCodeAt(1) % 25 === 0. Bots that have ever claimed an airdrop
|
|
629
|
+
// lose 95% of events after their first claim. (Original cohort
|
|
630
|
+
// "claimed-but-never-swapped" was empty because every claimer also
|
|
631
|
+
// has organic swap events at this scale.)
|
|
520
632
|
// -----------------------------------------------------------
|
|
633
|
+
const isAirdropBot = userId.length > 1 && userId.charCodeAt(1) % 25 === 0;
|
|
521
634
|
const hasAirdropClaim = userEvents.some(e => e.event === "claim airdrop");
|
|
522
|
-
const hasSwap = userEvents.some(e => e.event === "swap");
|
|
523
635
|
|
|
524
|
-
if (
|
|
636
|
+
if (isAirdropBot && hasAirdropClaim) {
|
|
525
637
|
const firstClaim = userEvents.find(e => e.event === "claim airdrop");
|
|
526
638
|
if (firstClaim) {
|
|
527
639
|
const claimTime = dayjs(firstClaim.time);
|
|
@@ -536,31 +648,22 @@ const config = {
|
|
|
536
648
|
}
|
|
537
649
|
}
|
|
538
650
|
|
|
539
|
-
//
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
// and 8x more swap events (cloned). Flag kyc_verified=true on
|
|
543
|
-
// post-KYC events.
|
|
544
|
-
// -----------------------------------------------------------
|
|
651
|
+
// HOOK 5: KYC FUNNEL COMPLETION — users with kyc-completed
|
|
652
|
+
// get post-KYC deposit_amount_usd boosted 4x and 7 extra cloned
|
|
653
|
+
// swaps per existing post-KYC swap. No flag.
|
|
545
654
|
const kycCompleted = userEvents.find(e => e.event === "kyc completed");
|
|
546
655
|
if (kycCompleted) {
|
|
547
656
|
const kycTime = dayjs(kycCompleted.time);
|
|
548
657
|
|
|
549
|
-
// Boost deposits and flag post-KYC events
|
|
550
658
|
userEvents.forEach(e => {
|
|
551
|
-
if (dayjs(e.time).isAfter(kycTime)) {
|
|
552
|
-
|
|
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
|
-
}
|
|
659
|
+
if (dayjs(e.time).isAfter(kycTime) && e.event === "deposit") {
|
|
660
|
+
e.deposit_amount_usd = Math.round((e.deposit_amount_usd || 500) * 4);
|
|
559
661
|
}
|
|
560
662
|
});
|
|
561
663
|
|
|
562
|
-
|
|
563
|
-
|
|
664
|
+
const postKycSwaps = userEvents.filter(e =>
|
|
665
|
+
e.event === "swap" && dayjs(e.time).isAfter(kycTime)
|
|
666
|
+
);
|
|
564
667
|
const clonedKycSwaps = [];
|
|
565
668
|
postKycSwaps.forEach(swapEvt => {
|
|
566
669
|
const swapTime = dayjs(swapEvt.time);
|
|
@@ -570,7 +673,6 @@ const config = {
|
|
|
570
673
|
time: swapTime.add(chance.integer({ min: 1, max: 48 }), "hours").toISOString(),
|
|
571
674
|
user_id: swapEvt.user_id,
|
|
572
675
|
trade_amount_usd: Math.round((swapEvt.trade_amount_usd || 200) * chance.floating({ min: 0.8, max: 1.5 })),
|
|
573
|
-
kyc_verified: true,
|
|
574
676
|
});
|
|
575
677
|
}
|
|
576
678
|
});
|
|
@@ -583,19 +685,15 @@ const config = {
|
|
|
583
685
|
// dataset have 70% D60 retention (events injected beyond D60).
|
|
584
686
|
// Non-stakers lose events after day 60 to simulate 15% retention.
|
|
585
687
|
// -----------------------------------------------------------
|
|
586
|
-
const day14 =
|
|
587
|
-
const day60 =
|
|
688
|
+
const day14 = datasetStart.add(14, "days");
|
|
689
|
+
const day60 = datasetStart.add(60, "days");
|
|
588
690
|
|
|
589
691
|
const stakedEarly = userEvents.some(e =>
|
|
590
692
|
e.event === "stake" && dayjs(e.time).isBefore(day14)
|
|
591
693
|
);
|
|
592
694
|
|
|
593
695
|
if (stakedEarly) {
|
|
594
|
-
//
|
|
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)
|
|
696
|
+
// 70% retention: inject late-stage events for stakers (no flag)
|
|
599
697
|
const postD60Events = userEvents.filter(e => dayjs(e.time).isAfter(day60));
|
|
600
698
|
if (postD60Events.length < 5) {
|
|
601
699
|
const swapTemplate = userEvents.find(e => e.event === "swap");
|
|
@@ -607,7 +705,6 @@ const config = {
|
|
|
607
705
|
time: day60.add(chance.integer({ min: 1, max: 55 }), "days").toISOString(),
|
|
608
706
|
user_id: swapTemplate.user_id,
|
|
609
707
|
trade_amount_usd: Math.round((swapTemplate.trade_amount_usd || 200) * chance.floating({ min: 0.5, max: 2.0 })),
|
|
610
|
-
staked_early: true,
|
|
611
708
|
});
|
|
612
709
|
}
|
|
613
710
|
}
|
|
@@ -617,7 +714,6 @@ const config = {
|
|
|
617
714
|
...portfolioTemplate,
|
|
618
715
|
time: day60.add(chance.integer({ min: 1, max: 55 }), "days").toISOString(),
|
|
619
716
|
user_id: portfolioTemplate.user_id,
|
|
620
|
-
staked_early: true,
|
|
621
717
|
});
|
|
622
718
|
}
|
|
623
719
|
}
|
|
@@ -634,21 +730,14 @@ const config = {
|
|
|
634
730
|
}
|
|
635
731
|
}
|
|
636
732
|
|
|
637
|
-
//
|
|
638
|
-
//
|
|
639
|
-
//
|
|
640
|
-
// vs standard 0.30. Pro users also trade 6x volume.
|
|
641
|
-
// pro_advantage=true on Pro swap events.
|
|
642
|
-
// -----------------------------------------------------------
|
|
733
|
+
// HOOK 7: PRO TIER MAKER FEES — Pro users pay maker_fee_pct=0.05
|
|
734
|
+
// (vs Standard 0.30) and get 5 extra cloned swaps. Mutates raw
|
|
735
|
+
// maker_fee_pct prop. Reads trading_tier from profile.
|
|
643
736
|
if (profile.trading_tier === "Pro") {
|
|
644
737
|
userEvents.forEach(e => {
|
|
645
|
-
if (e.event === "swap")
|
|
646
|
-
e.maker_fee_pct = 0.05;
|
|
647
|
-
e.pro_advantage = true;
|
|
648
|
-
}
|
|
738
|
+
if (e.event === "swap") e.maker_fee_pct = 0.05;
|
|
649
739
|
});
|
|
650
740
|
|
|
651
|
-
// Clone swap events for 6x volume
|
|
652
741
|
const proSwaps = userEvents.filter(e => e.event === "swap");
|
|
653
742
|
const clonedProSwaps = [];
|
|
654
743
|
proSwaps.forEach(swapEvt => {
|
|
@@ -660,27 +749,23 @@ const config = {
|
|
|
660
749
|
user_id: swapEvt.user_id,
|
|
661
750
|
trade_amount_usd: Math.round((swapEvt.trade_amount_usd || 200) * chance.floating({ min: 0.5, max: 3.0 })),
|
|
662
751
|
maker_fee_pct: 0.05,
|
|
663
|
-
pro_advantage: true,
|
|
664
752
|
});
|
|
665
753
|
}
|
|
666
754
|
});
|
|
667
755
|
userEvents.push(...clonedProSwaps);
|
|
668
756
|
} else {
|
|
669
|
-
// Ensure standard fee on non-Pro swap events
|
|
670
757
|
userEvents.forEach(e => {
|
|
671
|
-
if (e.event === "swap")
|
|
672
|
-
e.maker_fee_pct = 0.30;
|
|
673
|
-
}
|
|
758
|
+
if (e.event === "swap") e.maker_fee_pct = 0.30;
|
|
674
759
|
});
|
|
675
760
|
}
|
|
676
761
|
|
|
677
762
|
// -----------------------------------------------------------
|
|
678
763
|
// Hook #8: RUG-PULL AFTERMATH
|
|
679
764
|
// Day 70, "SCAM" token rugs. Users who swapped SCAM token
|
|
680
|
-
// before day 70 lose 80% of events after day 70.
|
|
681
|
-
//
|
|
765
|
+
// before day 70 lose 80% of events after day 70. No flag —
|
|
766
|
+
// derive cohort by detecting users with token_pair containing "SCAM".
|
|
682
767
|
// -----------------------------------------------------------
|
|
683
|
-
const day70 =
|
|
768
|
+
const day70 = datasetStart.add(70, "days");
|
|
684
769
|
|
|
685
770
|
// Check if user traded SCAM before day 70
|
|
686
771
|
// ~10% of pre-day-70 swaps naturally get SCAM token pair
|
|
@@ -688,7 +773,7 @@ const config = {
|
|
|
688
773
|
let hadScam = false;
|
|
689
774
|
userEvents.forEach(e => {
|
|
690
775
|
if (e.event === "swap") {
|
|
691
|
-
const swapDay = dayjs(e.time).diff(
|
|
776
|
+
const swapDay = dayjs(e.time).diff(datasetStart, "day");
|
|
692
777
|
// Before day 70, ~8% of swaps randomly become SCAM trades
|
|
693
778
|
if (swapDay >= 10 && swapDay < 70 && chance.bool({ likelihood: 8 })) {
|
|
694
779
|
e.token_pair = chance.pickone(["SCAM/USDC", "SCAM/ETH", "ETH/SCAM"]);
|
|
@@ -700,17 +785,41 @@ const config = {
|
|
|
700
785
|
if (hadScam) {
|
|
701
786
|
for (let i = userEvents.length - 1; i >= 0; i--) {
|
|
702
787
|
const evt = userEvents[i];
|
|
703
|
-
if (dayjs(evt.time).isAfter(day70)) {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
788
|
+
if (dayjs(evt.time).isAfter(day70) && chance.bool({ likelihood: 80 })) {
|
|
789
|
+
userEvents.splice(i, 1);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// HOOK 10: SWAP-COUNT MAGIC NUMBER (no flags)
|
|
795
|
+
// Sweet 8-20 swaps → +30% on stake amount_usd; clone 1-2 extra
|
|
796
|
+
// stake events per existing. Over 21+ → drop 40% of portfolio
|
|
797
|
+
// viewed events (over-active traders ignore their portfolio).
|
|
798
|
+
const swapCount = userEvents.filter(e => e.event === "swap").length;
|
|
799
|
+
if (swapCount >= 8 && swapCount <= 20) {
|
|
800
|
+
const stakeTemplate = userEvents.find(e => e.event === "stake");
|
|
801
|
+
if (stakeTemplate) {
|
|
802
|
+
const stakes = userEvents.filter(e => e.event === "stake");
|
|
803
|
+
stakes.forEach(s => {
|
|
804
|
+
if (typeof s.amount_usd === "number") s.amount_usd = Math.round(s.amount_usd * 1.3);
|
|
805
|
+
const extras = chance.integer({ min: 1, max: 2 });
|
|
806
|
+
for (let k = 0; k < extras; k++) {
|
|
807
|
+
userEvents.push({
|
|
808
|
+
...s,
|
|
809
|
+
time: dayjs(s.time).add(chance.integer({ min: 5, max: 360 }), "minutes").toISOString(),
|
|
810
|
+
user_id: s.user_id,
|
|
811
|
+
});
|
|
708
812
|
}
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
} else if (swapCount >= 21) {
|
|
816
|
+
for (let i = userEvents.length - 1; i >= 0; i--) {
|
|
817
|
+
if (userEvents[i].event === "portfolio viewed" && chance.bool({ likelihood: 40 })) {
|
|
818
|
+
userEvents.splice(i, 1);
|
|
709
819
|
}
|
|
710
820
|
}
|
|
711
821
|
}
|
|
712
822
|
|
|
713
|
-
// Sort by time after all mutations
|
|
714
823
|
userEvents.sort((a, b) => new Date(a.time) - new Date(b.time));
|
|
715
824
|
}
|
|
716
825
|
|